From 08df2312036a65c91279392f35ca3c1797d068d6 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 26 Jan 2024 23:00:51 -0300 Subject: [PATCH 001/207] chore: move ui-kit prepare to build script (#31557) --- packages/ui-kit/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ui-kit/package.json b/packages/ui-kit/package.json index 93764f1c2376..acc69d195a78 100644 --- a/packages/ui-kit/package.json +++ b/packages/ui-kit/package.json @@ -25,13 +25,13 @@ "access": "public" }, "scripts": { - "build": "run-s .:build:clean .:build:esm .:build:cjs", + "build": "run-s .:build:prepare .:build:clean .:build:esm .:build:cjs", + ".:build:prepare": "ts-patch install && typia patch", ".:build:clean": "rimraf dist", ".:build:esm": "tsc -p tsconfig.json", ".:build:cjs": "tsc -p tsconfig-cjs.json", "lint": "eslint . --ext .ts,.tsx", - "test": "jest", - "prepare-bkp": "ts-patch install && typia patch" + "test": "jest" }, "devDependencies": { "@babel/core": "~7.21.4", From 2c0260d1069fc525635f69b1020043a09d5228e4 Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Mon, 29 Jan 2024 16:01:10 -0300 Subject: [PATCH 002/207] fix: dryrun option sending email to all queried users (#31571) Co-authored-by: Hugo Costa <20212776+hugocostadev@users.noreply.github.com> --- .changeset/grumpy-eagles-roll.md | 5 ++ .../server/functions/sendMail.ts | 47 ++++++++++--------- .../end-to-end/api/livechat/12-mailer.ts | 2 +- 3 files changed, 32 insertions(+), 22 deletions(-) create mode 100644 .changeset/grumpy-eagles-roll.md diff --git a/.changeset/grumpy-eagles-roll.md b/.changeset/grumpy-eagles-roll.md new file mode 100644 index 000000000000..37e0c7af1688 --- /dev/null +++ b/.changeset/grumpy-eagles-roll.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed Mail dryrun sending email to all users diff --git a/apps/meteor/app/mail-messages/server/functions/sendMail.ts b/apps/meteor/app/mail-messages/server/functions/sendMail.ts index ce57fc06b1c8..50435bc24812 100644 --- a/apps/meteor/app/mail-messages/server/functions/sendMail.ts +++ b/apps/meteor/app/mail-messages/server/functions/sendMail.ts @@ -36,33 +36,38 @@ export const sendMail = async function ({ userQuery = { $and: [userQuery, EJSON.parse(query)] }; } - const users = await Users.find(userQuery).toArray(); - if (dryrun) { - for await (const u of users) { - const user: Partial & Pick = u; - const email = `${user.name} <${user.emails?.[0].address}>`; - const html = placeholders.replace(body, { - unsubscribe: Meteor.absoluteUrl( - generatePath('mailer/unsubscribe/:_id/:createdAt', { - _id: user._id, - createdAt: user.createdAt?.getTime().toString() || '', - }), - ), - name: user.name, - email, - }); + const user = await Users.findOneByEmailAddress(from); - SystemLogger.debug(`Sending email to ${email}`); - await Mailer.send({ - to: email, - from, - subject, - html, + if (!user) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { + function: 'Mailer.sendMail', }); } + + const email = `${user.name} <${user.emails?.[0].address}>`; + const html = placeholders.replace(body, { + unsubscribe: Meteor.absoluteUrl( + generatePath('mailer/unsubscribe/:_id/:createdAt', { + _id: user._id, + createdAt: user.createdAt?.getTime().toString() || '', + }), + ), + name: user.name, + email, + }); + + SystemLogger.debug(`Sending email to ${email}`); + return Mailer.send({ + to: email, + from, + subject, + html, + }); } + const users = await Users.find(userQuery).toArray(); + for await (const u of users) { const user: Partial & Pick = u; if (user?.emails && Array.isArray(user.emails) && user.emails.length) { diff --git a/apps/meteor/tests/end-to-end/api/livechat/12-mailer.ts b/apps/meteor/tests/end-to-end/api/livechat/12-mailer.ts index e49628ce718e..01a47594620d 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/12-mailer.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/12-mailer.ts @@ -13,7 +13,7 @@ describe('Mailer', () => { .post(api('mailer')) .set(credentials) .send({ - from: 'test-email@example.com', + from: 'rocketchat.internal.admin.test@rocket.chat', subject: 'Test email subject', body: 'Test email body [unsubscribe]', dryrun: true, From 0ff92dc3ef31e89b0052f1f541d071fee4f568e2 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Wed, 31 Jan 2024 13:22:14 -0300 Subject: [PATCH 003/207] refactor(client): Decouple WebDAV accounts integration from client initialization (#31553) --- apps/meteor/app/models/client/index.ts | 2 - .../models/client/models/WebdavAccounts.ts | 9 --- apps/meteor/app/webdav/client/actionButton.ts | 47 ---------------- apps/meteor/app/webdav/client/index.js | 15 ----- apps/meteor/app/webdav/client/startup/sync.ts | 20 ------- .../message/toolbar/MessageToolbar.tsx | 4 ++ .../toolbar/useWebDAVMessageAction.tsx | 44 +++++++++++++++ .../useWebDAVAccountIntegrationsQuery.ts | 55 +++++++++++++++++++ apps/meteor/client/importPackages.ts | 1 - .../integrations/AccountIntegrationsPage.tsx | 51 +++++++++-------- .../integrations/AccountIntegrationsRoute.tsx | 2 +- ...RemoveWebDAVAccountIntegrationMutation.tsx | 16 ++++++ .../hooks/useWebdavActions.tsx | 24 ++++---- .../views/room/webdav/SaveToWebdavModal.tsx | 16 +++--- ee/packages/ddp-client/src/types/streams.ts | 2 +- packages/core-services/src/events/Events.ts | 2 +- 16 files changed, 169 insertions(+), 141 deletions(-) delete mode 100644 apps/meteor/app/models/client/models/WebdavAccounts.ts delete mode 100644 apps/meteor/app/webdav/client/actionButton.ts delete mode 100644 apps/meteor/app/webdav/client/index.js delete mode 100644 apps/meteor/app/webdav/client/startup/sync.ts create mode 100644 apps/meteor/client/components/message/toolbar/useWebDAVMessageAction.tsx create mode 100644 apps/meteor/client/hooks/webdav/useWebDAVAccountIntegrationsQuery.ts create mode 100644 apps/meteor/client/views/account/integrations/hooks/useRemoveWebDAVAccountIntegrationMutation.tsx diff --git a/apps/meteor/app/models/client/index.ts b/apps/meteor/app/models/client/index.ts index 6b023c3cc7c7..354baa2b71fd 100644 --- a/apps/meteor/app/models/client/index.ts +++ b/apps/meteor/app/models/client/index.ts @@ -14,7 +14,6 @@ import { RoomRoles } from './models/RoomRoles'; import { UserAndRoom } from './models/UserAndRoom'; import { UserRoles } from './models/UserRoles'; import { Users } from './models/Users'; -import { WebdavAccounts } from './models/WebdavAccounts'; export { Base, @@ -30,7 +29,6 @@ export { ChatPermissions, CustomSounds, EmojiCustom, - WebdavAccounts, /** @deprecated */ Users, /** @deprecated */ diff --git a/apps/meteor/app/models/client/models/WebdavAccounts.ts b/apps/meteor/app/models/client/models/WebdavAccounts.ts deleted file mode 100644 index fbc1092cd86e..000000000000 --- a/apps/meteor/app/models/client/models/WebdavAccounts.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Mongo } from 'meteor/mongo'; - -/** @deprecated */ -export const WebdavAccounts = new Mongo.Collection<{ - _id: string; - name: string; - username: string; - serverURL: string; -}>(null); diff --git a/apps/meteor/app/webdav/client/actionButton.ts b/apps/meteor/app/webdav/client/actionButton.ts deleted file mode 100644 index 9a2f8152e5c0..000000000000 --- a/apps/meteor/app/webdav/client/actionButton.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { imperativeModal } from '../../../client/lib/imperativeModal'; -import { messageArgs } from '../../../client/lib/utils/messageArgs'; -import SaveToWebdav from '../../../client/views/room/webdav/SaveToWebdavModal'; -import { WebdavAccounts } from '../../models/client'; -import { settings } from '../../settings/client'; -import { MessageAction } from '../../ui-utils/client'; -import { getURL } from '../../utils/client'; - -Meteor.startup(() => { - MessageAction.addButton({ - id: 'webdav-upload', - icon: 'upload', - label: 'Save_To_Webdav', - condition: ({ message, subscription }) => { - if (subscription == null) { - return false; - } - if (WebdavAccounts.findOne() == null) { - return false; - } - if (!message.file) { - return false; - } - - return settings.get('Webdav_Integration_Enabled'); - }, - action(_, props) { - const { message = messageArgs(this).msg } = props; - const [attachment] = message.attachments || []; - const url = getURL(attachment.title_link as string, { full: true }); - imperativeModal.open({ - component: SaveToWebdav, - props: { - data: { - attachment, - url, - }, - onClose: imperativeModal.close, - }, - }); - }, - order: 100, - group: 'menu', - }); -}); diff --git a/apps/meteor/app/webdav/client/index.js b/apps/meteor/app/webdav/client/index.js deleted file mode 100644 index b4f7aa4ef133..000000000000 --- a/apps/meteor/app/webdav/client/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; - -import { settings } from '../../settings/client'; - -Meteor.startup(() => { - Tracker.autorun((c) => { - if (!settings.get('Webdav_Integration_Enabled')) { - return; - } - c.stop(); - import('./startup/sync'); - import('./actionButton'); - }); -}); diff --git a/apps/meteor/app/webdav/client/startup/sync.ts b/apps/meteor/app/webdav/client/startup/sync.ts deleted file mode 100644 index 16e82e719e63..000000000000 --- a/apps/meteor/app/webdav/client/startup/sync.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { IWebdavAccount } from '@rocket.chat/core-typings'; -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; - -import { WebdavAccounts } from '../../../models/client'; -import { sdk } from '../../../utils/client/lib/SDKClient'; - -const events = { - changed: (account: IWebdavAccount) => WebdavAccounts.upsert({ _id: account._id }, account), - removed: ({ _id }: { _id: IWebdavAccount['_id'] }) => WebdavAccounts.remove({ _id }), -}; - -Tracker.autorun(async () => { - if (!Meteor.userId()) { - return; - } - const { accounts } = await sdk.rest.get('/v1/webdav.getMyAccounts'); - accounts.forEach((account) => WebdavAccounts.insert(account)); - sdk.stream('notify-user', [`${Meteor.userId()}/webdav`], ({ type, account }) => events[type](account as IWebdavAccount)); -}); diff --git a/apps/meteor/client/components/message/toolbar/MessageToolbar.tsx b/apps/meteor/client/components/message/toolbar/MessageToolbar.tsx index 81385d673749..335fdf1ea158 100644 --- a/apps/meteor/client/components/message/toolbar/MessageToolbar.tsx +++ b/apps/meteor/client/components/message/toolbar/MessageToolbar.tsx @@ -17,6 +17,7 @@ import { useAutoTranslate } from '../../../views/room/MessageList/hooks/useAutoT import { useChat } from '../../../views/room/contexts/ChatContext'; import { useRoomToolbox } from '../../../views/room/contexts/RoomToolboxContext'; import MessageActionMenu from './MessageActionMenu'; +import { useWebDAVMessageAction } from './useWebDAVMessageAction'; const getMessageContext = (message: IMessage, room: IRoom, context?: MessageActionContext): MessageActionContext => { if (context) { @@ -72,6 +73,9 @@ const MessageToolbar = ({ const { messageToolbox: hiddenActions } = useLayoutHiddenActions(); + // TODO: move this to another place + useWebDAVMessageAction(); + const actionsQueryResult = useQuery(['rooms', room._id, 'messages', message._id, 'actions'] as const, async () => { const props = { message, room, user, subscription, settings: mapSettings, chat }; diff --git a/apps/meteor/client/components/message/toolbar/useWebDAVMessageAction.tsx b/apps/meteor/client/components/message/toolbar/useWebDAVMessageAction.tsx new file mode 100644 index 000000000000..a2be70077054 --- /dev/null +++ b/apps/meteor/client/components/message/toolbar/useWebDAVMessageAction.tsx @@ -0,0 +1,44 @@ +import { useSetModal, useSetting } from '@rocket.chat/ui-contexts'; +import React, { useEffect } from 'react'; + +import { MessageAction } from '../../../../app/ui-utils/client/lib/MessageAction'; +import { getURL } from '../../../../app/utils/client'; +import { useWebDAVAccountIntegrationsQuery } from '../../../hooks/webdav/useWebDAVAccountIntegrationsQuery'; +import { messageArgs } from '../../../lib/utils/messageArgs'; +import SaveToWebdavModal from '../../../views/room/webdav/SaveToWebdavModal'; + +export const useWebDAVMessageAction = () => { + const enabled = useSetting('Webdav_Integration_Enabled', false); + + const { data } = useWebDAVAccountIntegrationsQuery({ enabled }); + + const setModal = useSetModal(); + + useEffect(() => { + if (!enabled) { + return; + } + + MessageAction.addButton({ + id: 'webdav-upload', + icon: 'upload', + label: 'Save_To_Webdav', + condition: ({ message, subscription }) => { + return !!subscription && !!data?.length && !!message.file; + }, + action(_, props) { + const { message = messageArgs(this).msg } = props; + const [attachment] = message.attachments || []; + const url = getURL(attachment.title_link as string, { full: true }); + + setModal( setModal(undefined)} />); + }, + order: 100, + group: 'menu', + }); + + return () => { + MessageAction.removeButton('webdav-upload'); + }; + }, [data?.length, enabled, setModal]); +}; diff --git a/apps/meteor/client/hooks/webdav/useWebDAVAccountIntegrationsQuery.ts b/apps/meteor/client/hooks/webdav/useWebDAVAccountIntegrationsQuery.ts new file mode 100644 index 000000000000..171490b33d18 --- /dev/null +++ b/apps/meteor/client/hooks/webdav/useWebDAVAccountIntegrationsQuery.ts @@ -0,0 +1,55 @@ +import type { IWebdavAccountIntegration } from '@rocket.chat/core-typings'; +import { useUserId, useEndpoint, useStream } from '@rocket.chat/ui-contexts'; +import type { UseQueryOptions } from '@tanstack/react-query'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { useEffect, useMemo } from 'react'; + +type UseWebDAVAccountIntegrationsQueryOptions = Omit< + UseQueryOptions, + 'queryKey' | 'queryFn' +>; + +export const useWebDAVAccountIntegrationsQuery = ({ enabled = true, ...options }: UseWebDAVAccountIntegrationsQueryOptions = {}) => { + const uid = useUserId(); + + const queryKey = useMemo(() => ['webdav', 'account-integrations'] as const, []); + + const getMyAccounts = useEndpoint('GET', '/v1/webdav.getMyAccounts'); + + const integrationsQuery = useQuery({ + queryKey, + queryFn: async (): Promise => { + const { accounts } = await getMyAccounts(); + return accounts; + }, + enabled: !!uid && enabled, + staleTime: Infinity, + ...options, + }); + + const queryClient = useQueryClient(); + + const subscribeToNotifyUser = useStream('notify-user'); + + useEffect(() => { + if (!uid || !enabled) { + return; + } + + return subscribeToNotifyUser(`${uid}/webdav`, ({ type, account }) => { + switch (type) { + case 'changed': + queryClient.invalidateQueries(queryKey); + break; + + case 'removed': + queryClient.setQueryData(queryKey, (old = []) => { + return old.filter((oldAccount) => oldAccount._id !== account._id); + }); + break; + } + }); + }, [enabled, queryClient, queryKey, uid, subscribeToNotifyUser]); + + return integrationsQuery; +}; diff --git a/apps/meteor/client/importPackages.ts b/apps/meteor/client/importPackages.ts index ba1bf0186235..5119f0bba191 100644 --- a/apps/meteor/client/importPackages.ts +++ b/apps/meteor/client/importPackages.ts @@ -29,7 +29,6 @@ import '../app/slashcommands-open/client'; import '../app/slashcommands-topic/client'; import '../app/slashcommands-unarchiveroom/client'; import '../app/tokenpass/client'; -import '../app/webdav/client'; import '../app/webrtc/client'; import '../app/wordpress/client'; import '../app/e2e/client'; diff --git a/apps/meteor/client/views/account/integrations/AccountIntegrationsPage.tsx b/apps/meteor/client/views/account/integrations/AccountIntegrationsPage.tsx index 312cc89b65a0..2c11b7a384cd 100644 --- a/apps/meteor/client/views/account/integrations/AccountIntegrationsPage.tsx +++ b/apps/meteor/client/views/account/integrations/AccountIntegrationsPage.tsx @@ -1,49 +1,54 @@ -import type { IWebdavAccountIntegration } from '@rocket.chat/core-typings'; import type { SelectOption } from '@rocket.chat/fuselage'; import { SelectLegacy, Box, Button, Field, FieldLabel, FieldRow } from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useEndpoint, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactElement } from 'react'; +import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useMemo } from 'react'; import { useForm, Controller } from 'react-hook-form'; -import { WebdavAccounts } from '../../../../app/models/client'; import { Page, PageHeader, PageScrollableContentWithShadow } from '../../../components/Page'; -import { useReactiveValue } from '../../../hooks/useReactiveValue'; +import { useWebDAVAccountIntegrationsQuery } from '../../../hooks/webdav/useWebDAVAccountIntegrationsQuery'; import { getWebdavServerName } from '../../../lib/getWebdavServerName'; +import { useRemoveWebDAVAccountIntegrationMutation } from './hooks/useRemoveWebDAVAccountIntegrationMutation'; -const getWebdavAccounts = (): IWebdavAccountIntegration[] => WebdavAccounts.find().fetch(); +const AccountIntegrationsPage = () => { + const { data: webdavAccountIntegrations } = useWebDAVAccountIntegrationsQuery(); -const AccountIntegrationsPage = (): ReactElement => { - const t = useTranslation(); - const { handleSubmit, control } = useForm(); - const dispatchToastMessage = useToastMessageDispatch(); - const accounts = useReactiveValue(getWebdavAccounts); - const removeWebdavAccount = useEndpoint('POST', '/v1/webdav.removeWebdavAccount'); + const { handleSubmit, control } = useForm<{ accountSelected: string }>(); + + const options: SelectOption[] = useMemo( + () => webdavAccountIntegrations?.map(({ _id, ...current }) => [_id, getWebdavServerName(current)]) ?? [], + [webdavAccountIntegrations], + ); - const options: SelectOption[] = useMemo(() => accounts?.map(({ _id, ...current }) => [_id, getWebdavServerName(current)]), [accounts]); + const dispatchToastMessage = useToastMessageDispatch(); + const t = useTranslation(); - const handleClickRemove = useMutableCallback(({ accountSelected }) => { - try { - removeWebdavAccount({ accountId: accountSelected }); + const removeMutation = useRemoveWebDAVAccountIntegrationMutation({ + onSuccess: () => { dispatchToastMessage({ type: 'success', message: t('Webdav_account_removed') }); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error as Error }); - } + }, + onError: (error) => { + dispatchToastMessage({ type: 'error', message: error }); + }, + }); + + const handleSubmitForm = useEffectEvent(({ accountSelected }) => { + removeMutation.mutate({ accountSelected }); }); return ( - + {t('WebDAV_Accounts')} ( + rules={{ required: true }} + render={({ field: { onChange, value, name, ref } }) => ( { /> )} /> - diff --git a/apps/meteor/client/views/account/integrations/AccountIntegrationsRoute.tsx b/apps/meteor/client/views/account/integrations/AccountIntegrationsRoute.tsx index ba16360b3d36..00121c688345 100644 --- a/apps/meteor/client/views/account/integrations/AccountIntegrationsRoute.tsx +++ b/apps/meteor/client/views/account/integrations/AccountIntegrationsRoute.tsx @@ -6,7 +6,7 @@ import NotAuthorizedPage from '../../notAuthorized/NotAuthorizedPage'; import AccountIntegrationsPage from './AccountIntegrationsPage'; const AccountIntegrationsRoute = (): ReactElement => { - const webdavEnabled = useSetting('Webdav_Integration_Enabled'); + const webdavEnabled = useSetting('Webdav_Integration_Enabled', false); if (!webdavEnabled) { return ; diff --git a/apps/meteor/client/views/account/integrations/hooks/useRemoveWebDAVAccountIntegrationMutation.tsx b/apps/meteor/client/views/account/integrations/hooks/useRemoveWebDAVAccountIntegrationMutation.tsx new file mode 100644 index 000000000000..d0eb6b6f2a12 --- /dev/null +++ b/apps/meteor/client/views/account/integrations/hooks/useRemoveWebDAVAccountIntegrationMutation.tsx @@ -0,0 +1,16 @@ +import { useEndpoint } from '@rocket.chat/ui-contexts'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; + +type UseRemoveWebDAVAccountIntegrationMutationOptions = Omit, 'mutationFn'>; + +export const useRemoveWebDAVAccountIntegrationMutation = (options?: UseRemoveWebDAVAccountIntegrationMutationOptions) => { + const removeWebdavAccount = useEndpoint('POST', '/v1/webdav.removeWebdavAccount'); + + return useMutation({ + mutationFn: async ({ accountSelected }: { accountSelected: string }) => { + await removeWebdavAccount({ accountId: accountSelected }); + }, + ...options, + }); +}; diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/hooks/useWebdavActions.tsx b/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/hooks/useWebdavActions.tsx index 5605f410fa5f..ae80a7407851 100644 --- a/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/hooks/useWebdavActions.tsx +++ b/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/hooks/useWebdavActions.tsx @@ -1,23 +1,23 @@ import type { IWebdavAccountIntegration } from '@rocket.chat/core-typings'; -import { useTranslation, useSetting, useSetModal } from '@rocket.chat/ui-contexts'; +import { useSetModal, useSetting } from '@rocket.chat/ui-contexts'; import React from 'react'; +import { useTranslation } from 'react-i18next'; -import { WebdavAccounts } from '../../../../../../../app/models/client'; import type { GenericMenuItemProps } from '../../../../../../components/GenericMenu/GenericMenuItem'; -import { useReactiveValue } from '../../../../../../hooks/useReactiveValue'; +import { useWebDAVAccountIntegrationsQuery } from '../../../../../../hooks/webdav/useWebDAVAccountIntegrationsQuery'; import { useChat } from '../../../../contexts/ChatContext'; import AddWebdavAccountModal from '../../../../webdav/AddWebdavAccountModal'; import WebdavFilePickerModal from '../../../../webdav/WebdavFilePickerModal'; -const getWebdavAccounts = (): IWebdavAccountIntegration[] => WebdavAccounts.find().fetch(); - export const useWebdavActions = (): GenericMenuItemProps[] => { - const t = useTranslation(); - const setModal = useSetModal(); - const webDavEnabled = useSetting('Webdav_Integration_Enabled'); - const webDavAccounts = useReactiveValue(getWebdavAccounts); + const enabled = useSetting('Webdav_Integration_Enabled', false); + + const { isSuccess, data } = useWebDAVAccountIntegrationsQuery({ enabled }); + const chat = useChat(); + const { t } = useTranslation(); + const setModal = useSetModal(); const handleAddWebDav = () => setModal( setModal(null)} onConfirm={() => setModal(null)} />); const handleUpload = async (file: File, description?: string) => @@ -33,11 +33,11 @@ export const useWebdavActions = (): GenericMenuItemProps[] => { id: 'webdav-add', content: t('Add_Server'), icon: 'cloud-plus', - disabled: !webDavEnabled, + disabled: !isSuccess, onClick: handleAddWebDav, }, - ...(webDavEnabled && webDavAccounts.length > 0 - ? webDavAccounts.map((account) => ({ + ...(isSuccess + ? data.map((account) => ({ id: account._id, content: account.name, icon: 'cloud-plus' as const, diff --git a/apps/meteor/client/views/room/webdav/SaveToWebdavModal.tsx b/apps/meteor/client/views/room/webdav/SaveToWebdavModal.tsx index 24f7cc27cc59..c51763bee733 100644 --- a/apps/meteor/client/views/room/webdav/SaveToWebdavModal.tsx +++ b/apps/meteor/client/views/room/webdav/SaveToWebdavModal.tsx @@ -2,12 +2,12 @@ import type { MessageAttachment, IWebdavAccount } from '@rocket.chat/core-typing import type { SelectOption } from '@rocket.chat/fuselage'; import { Modal, Box, Button, FieldGroup, Field, FieldLabel, FieldRow, FieldError, Select, Throbber } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; -import { useMethod, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useMethod, useSetting, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useState, useMemo, useEffect, useRef } from 'react'; import { useForm, Controller } from 'react-hook-form'; -import { useEndpointData } from '../../../hooks/useEndpointData'; +import { useWebDAVAccountIntegrationsQuery } from '../../../hooks/webdav/useWebDAVAccountIntegrationsQuery'; import { getWebdavServerName } from '../../../lib/getWebdavServerName'; type SaveToWebdavModalProps = { @@ -32,15 +32,13 @@ const SaveToWebdavModal = ({ onClose, data }: SaveToWebdavModalProps): ReactElem formState: { errors }, } = useForm<{ accountId: string }>(); - const { value } = useEndpointData('/v1/webdav.getMyAccounts'); + const enabled = useSetting('Webdav_Integration_Enabled', false); - const accountsOptions: SelectOption[] = useMemo(() => { - if (value?.accounts) { - return value.accounts.map(({ _id, ...current }) => [_id, getWebdavServerName(current)]); - } + const { data: value } = useWebDAVAccountIntegrationsQuery({ enabled }); - return []; - }, [value?.accounts]); + const accountsOptions: SelectOption[] = useMemo(() => { + return value?.map(({ _id, ...current }) => [_id, getWebdavServerName(current)]) ?? []; + }, [value]); useEffect(() => fileRequest.current?.abort, []); diff --git a/ee/packages/ddp-client/src/types/streams.ts b/ee/packages/ddp-client/src/types/streams.ts index d1202a5b9725..17638c283512 100644 --- a/ee/packages/ddp-client/src/types/streams.ts +++ b/ee/packages/ddp-client/src/types/streams.ts @@ -9,7 +9,6 @@ import type { IEmoji, ICustomSound, INotificationDesktop, - IWebdavAccount, VoipEventDataSignature, IUser, IOmnichannelRoom, @@ -25,6 +24,7 @@ import type { LicenseLimitKind, ICustomUserStatus, UserStatus, + IWebdavAccount, } from '@rocket.chat/core-typings'; import type * as UiKit from '@rocket.chat/ui-kit'; diff --git a/packages/core-services/src/events/Events.ts b/packages/core-services/src/events/Events.ts index 5b14d78128bf..a592a777829c 100644 --- a/packages/core-services/src/events/Events.ts +++ b/packages/core-services/src/events/Events.ts @@ -19,7 +19,6 @@ import type { ISubscription, IUser, IInvite, - IWebdavAccount, ICustomSound, VoipEventDataSignature, UserStatus, @@ -33,6 +32,7 @@ import type { ILivechatVisitor, LicenseLimitKind, ICustomUserStatus, + IWebdavAccount, } from '@rocket.chat/core-typings'; import type * as UiKit from '@rocket.chat/ui-kit'; From 9c6052ee4d59ed32e8ba02795099d5a7eaedd437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:09:14 -0300 Subject: [PATCH 004/207] feat: a11y doc links (#31473) --- .changeset/kind-dragons-flash.md | 5 +++++ .../accessibility/AccessibilityPage.tsx | 20 ++++++++++++++++++- .../agents/AgentsTable/AgentsTable.tsx | 2 +- .../BusinessHoursDisabledPage.tsx | 2 +- .../currentChats/CurrentChatsPage.tsx | 2 +- .../customFields/CustomFieldsTable.tsx | 2 +- .../DepartmentsTable/DepartmentsTable.tsx | 2 +- .../omnichannel/directory/calls/CallTable.tsx | 2 +- .../omnichannel/directory/chats/ChatTable.tsx | 2 +- .../directory/contacts/ContactTable.tsx | 2 +- .../omnichannel/managers/ManagersTable.tsx | 2 +- .../omnichannel/triggers/TriggersTable.tsx | 2 +- .../cannedResponses/CannedResponsesTable.tsx | 2 +- .../omnichannel/monitors/MonitorsTable.tsx | 2 +- .../omnichannel/slaPolicies/SlaTable.tsx | 2 +- .../ee/client/omnichannel/tags/TagsTable.tsx | 2 +- .../client/omnichannel/units/UnitsTable.tsx | 2 +- .../rocketchat-i18n/i18n/en.i18n.json | 4 ++++ 18 files changed, 43 insertions(+), 16 deletions(-) create mode 100644 .changeset/kind-dragons-flash.md diff --git a/.changeset/kind-dragons-flash.md b/.changeset/kind-dragons-flash.md new file mode 100644 index 000000000000..a7619ccf5d95 --- /dev/null +++ b/.changeset/kind-dragons-flash.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +feat: add a11y doc links diff --git a/apps/meteor/client/views/account/accessibility/AccessibilityPage.tsx b/apps/meteor/client/views/account/accessibility/AccessibilityPage.tsx index 62bf2df74ecf..9bb4e57317cf 100644 --- a/apps/meteor/client/views/account/accessibility/AccessibilityPage.tsx +++ b/apps/meteor/client/views/account/accessibility/AccessibilityPage.tsx @@ -16,6 +16,7 @@ import { ToggleSwitch, } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; +import { ExternalLink } from '@rocket.chat/ui-client'; import { useTranslation, useToastMessageDispatch, useEndpoint, useSetting } from '@rocket.chat/ui-contexts'; import { useMutation } from '@tanstack/react-query'; import React, { useMemo } from 'react'; @@ -52,6 +53,7 @@ const AccessibilityPage = () => { const clockModeId = useUniqueId(); const hideUsernamesId = useUniqueId(); const hideRolesId = useUniqueId(); + const linkListId = useUniqueId(); const { formState: { isDirty, dirtyFields, isSubmitting }, @@ -88,7 +90,23 @@ const AccessibilityPage = () => { - {t('Accessibility_activation')} + + {t('Accessibility_activation')} + +

{t('Learn_more_about_accessibility')}

+
    +
  • + {t('Accessibility_statement')} +
  • +
  • + {t('Glossary_of_simplified_terms')} +
  • +
  • + + {t('Accessibility_feature_documentation')} + +
  • +
diff --git a/apps/meteor/client/views/omnichannel/agents/AgentsTable/AgentsTable.tsx b/apps/meteor/client/views/omnichannel/agents/AgentsTable/AgentsTable.tsx index 2488f4f6c36e..5d0e89e1008a 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentsTable/AgentsTable.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentsTable/AgentsTable.tsx @@ -88,7 +88,7 @@ const AgentsTable = () => { icon='headset' title={t('No_agents_yet')} description={t('No_agents_yet_description')} - linkHref='https://go.rocket.chat/omnichannel-docs' + linkHref='https://go.rocket.chat/i/omnichannel-docs' linkText={t('Learn_more_about_agents')} /> )} diff --git a/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursDisabledPage.tsx b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursDisabledPage.tsx index fcee31bbc3bb..8aba7862d956 100644 --- a/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursDisabledPage.tsx +++ b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursDisabledPage.tsx @@ -23,7 +23,7 @@ const BusinessHoursDisabledPage = () => { router.navigate('/admin/settings/Omnichannel')}>{t('Enable_business_hours')} )} - + {t('Learn_more_about_business_hours')} diff --git a/apps/meteor/client/views/omnichannel/currentChats/CurrentChatsPage.tsx b/apps/meteor/client/views/omnichannel/currentChats/CurrentChatsPage.tsx index e48908d4df9d..f209fa638d86 100644 --- a/apps/meteor/client/views/omnichannel/currentChats/CurrentChatsPage.tsx +++ b/apps/meteor/client/views/omnichannel/currentChats/CurrentChatsPage.tsx @@ -321,7 +321,7 @@ const CurrentChatsPage = ({ id, onRowClick }: { id?: string; onRowClick: (_id: s icon='discussion' title={t('No_chats_yet')} description={t('No_chats_yet_description')} - linkHref='https://go.rocket.chat/omnichannel-docs' + linkHref='https://go.rocket.chat/i/omnichannel-docs' linkText={t('Learn_more_about_current_chats')} /> )} diff --git a/apps/meteor/client/views/omnichannel/customFields/CustomFieldsTable.tsx b/apps/meteor/client/views/omnichannel/customFields/CustomFieldsTable.tsx index 3298139319b2..d8609293c2c5 100644 --- a/apps/meteor/client/views/omnichannel/customFields/CustomFieldsTable.tsx +++ b/apps/meteor/client/views/omnichannel/customFields/CustomFieldsTable.tsx @@ -92,7 +92,7 @@ const CustomFieldsTable = () => { description={t('No_custom_fields_yet_description')} buttonAction={handleAddNew} buttonTitle={t('Create_custom_field')} - linkHref='https://go.rocket.chat/omnichannel-docs' + linkHref='https://go.rocket.chat/i/omnichannel-docs' linkText={t('Learn_more_about_custom_fields')} /> )} diff --git a/apps/meteor/client/views/omnichannel/departments/DepartmentsTable/DepartmentsTable.tsx b/apps/meteor/client/views/omnichannel/departments/DepartmentsTable/DepartmentsTable.tsx index 467be681b32d..7aee3f534d42 100644 --- a/apps/meteor/client/views/omnichannel/departments/DepartmentsTable/DepartmentsTable.tsx +++ b/apps/meteor/client/views/omnichannel/departments/DepartmentsTable/DepartmentsTable.tsx @@ -108,7 +108,7 @@ const DepartmentsTable = ({ archived }: { archived: boolean }) => { description={t('No_departments_yet_description')} buttonAction={handleAddNew} buttonTitle={t('Create_department')} - linkHref='https://go.rocket.chat/omnichannel-docs' + linkHref='https://go.rocket.chat/i/omnichannel-docs' linkText={t('Learn_more_about_departments')} /> )} diff --git a/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx b/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx index 131510df625b..916a866b0431 100644 --- a/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx +++ b/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx @@ -110,7 +110,7 @@ const CallTable = () => { icon='phone' title={t('No_calls_yet')} description={t('No_calls_yet_description')} - linkHref='https://go.rocket.chat/omnichannel-docs' + linkHref='https://go.rocket.chat/i/omnichannel-docs' linkText={t('Learn_more_about_voice_channel')} /> )} diff --git a/apps/meteor/client/views/omnichannel/directory/chats/ChatTable.tsx b/apps/meteor/client/views/omnichannel/directory/chats/ChatTable.tsx index 68d8e8a00a50..e11a1c332066 100644 --- a/apps/meteor/client/views/omnichannel/directory/chats/ChatTable.tsx +++ b/apps/meteor/client/views/omnichannel/directory/chats/ChatTable.tsx @@ -149,7 +149,7 @@ const ChatTable = () => { icon='message' title={t('No_chats_yet')} description={t('No_chats_yet_description')} - linkHref='https://go.rocket.chat/omnichannel-docs' + linkHref='https://go.rocket.chat/i/omnichannel-docs' linkText={t('Learn_more_about_conversations')} /> )} diff --git a/apps/meteor/client/views/omnichannel/directory/contacts/ContactTable.tsx b/apps/meteor/client/views/omnichannel/directory/contacts/ContactTable.tsx index 8caeecb342cd..a6516e082e88 100644 --- a/apps/meteor/client/views/omnichannel/directory/contacts/ContactTable.tsx +++ b/apps/meteor/client/views/omnichannel/directory/contacts/ContactTable.tsx @@ -123,7 +123,7 @@ function ContactTable(): ReactElement { description={t('No_contacts_yet_description')} buttonTitle={t('New_contact')} buttonAction={onButtonNewClick} - linkHref='https://go.rocket.chat/omnichannel-docs' + linkHref='https://go.rocket.chat/i/omnichannel-docs' linkText={t('Learn_more_about_contacts')} /> )} diff --git a/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx b/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx index 633f1f6c0975..49ecf27d14d3 100644 --- a/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx +++ b/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx @@ -94,7 +94,7 @@ const ManagersTable = () => { icon='shield' title={t('No_managers_yet')} description={t('No_managers_yet_description')} - linkHref='https://go.rocket.chat/omnichannel-docs' + linkHref='https://go.rocket.chat/i/omnichannel-docs' linkText={t('Learn_more_about_managers')} /> )} diff --git a/apps/meteor/client/views/omnichannel/triggers/TriggersTable.tsx b/apps/meteor/client/views/omnichannel/triggers/TriggersTable.tsx index 8c0ea0213da9..b4e30085429b 100644 --- a/apps/meteor/client/views/omnichannel/triggers/TriggersTable.tsx +++ b/apps/meteor/client/views/omnichannel/triggers/TriggersTable.tsx @@ -61,7 +61,7 @@ const TriggersTable = () => { description={t('No_triggers_yet_description')} buttonAction={handleAddNew} buttonTitle={t('Create_trigger')} - linkHref='https://go.rocket.chat/omnichannel-docs' + linkHref='https://go.rocket.chat/i/omnichannel-docs' linkText={t('Learn_more_about_triggers')} /> )} diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx index 165d7d27863c..013f501f4ffd 100644 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx @@ -141,7 +141,7 @@ const CannedResponsesTable = () => { description={t('No_Canned_Responses_Yet-description')} buttonTitle={t('Create_canned_response')} buttonAction={handleAddNew} - linkHref='https://go.rocket.chat/omnichannel-docs' + linkHref='https://go.rocket.chat/i/omnichannel-docs' linkText={t('Learn_more_about_canned_responses')} /> )} diff --git a/apps/meteor/ee/client/omnichannel/monitors/MonitorsTable.tsx b/apps/meteor/ee/client/omnichannel/monitors/MonitorsTable.tsx index d403cbda98f2..6a99ae93340d 100644 --- a/apps/meteor/ee/client/omnichannel/monitors/MonitorsTable.tsx +++ b/apps/meteor/ee/client/omnichannel/monitors/MonitorsTable.tsx @@ -159,7 +159,7 @@ const MonitorsTable = () => { icon='shield-blank' title={t('No_monitors_yet')} description={t('No_monitors_yet_description')} - linkHref='https://go.rocket.chat/omnichannel-docs' + linkHref='https://go.rocket.chat/i/omnichannel-docs' linkText={t('Learn_more_about_monitors')} /> )} diff --git a/apps/meteor/ee/client/omnichannel/slaPolicies/SlaTable.tsx b/apps/meteor/ee/client/omnichannel/slaPolicies/SlaTable.tsx index ec40f7a1255d..0a07f7468f01 100644 --- a/apps/meteor/ee/client/omnichannel/slaPolicies/SlaTable.tsx +++ b/apps/meteor/ee/client/omnichannel/slaPolicies/SlaTable.tsx @@ -101,7 +101,7 @@ const SlaTable = ({ reload }: { reload: MutableRefObject<() => void> }) => { description={t('No_SLA_policies_yet_description')} buttonTitle={t('Create_SLA_policy')} buttonAction={handleAddNew} - linkHref='https://go.rocket.chat/omnichannel-docs' + linkHref='https://go.rocket.chat/i/omnichannel-docs' linkText={t('Learn_more_about_SLA_policies')} /> )} diff --git a/apps/meteor/ee/client/omnichannel/tags/TagsTable.tsx b/apps/meteor/ee/client/omnichannel/tags/TagsTable.tsx index 3bb44996d22c..ce89fe2d764b 100644 --- a/apps/meteor/ee/client/omnichannel/tags/TagsTable.tsx +++ b/apps/meteor/ee/client/omnichannel/tags/TagsTable.tsx @@ -87,7 +87,7 @@ const TagsTable = () => { description={t('No_tags_yet_description')} buttonTitle={t('Create_tag')} buttonAction={handleAddNew} - linkHref='https://go.rocket.chat/omnichannel-docs' + linkHref='https://go.rocket.chat/i/omnichannel-docs' linkText={t('Learn_more_about_tags')} /> )} diff --git a/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx b/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx index 17c8abcbec91..c88fb4ce4c67 100644 --- a/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx +++ b/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx @@ -84,7 +84,7 @@ const UnitsTable = () => { icon='business' title={t('No_units_yet')} description={t('No_units_yet_description')} - linkHref='https://go.rocket.chat/omnichannel-docs' + linkHref='https://go.rocket.chat/i/omnichannel-docs' buttonAction={handleAddNew} buttonTitle={t('Create_unit')} linkText={t('Learn_more_about_units')} diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 0a54136333ee..dc893c25c85b 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -44,6 +44,8 @@ "Accessibility": "Accessibility", "Accessibility_and_Appearance": "Accessibility & appearance", "Accessibility_activation": "Here you can activate a range of features to enhance your browsing experience.", + "Accessibility_statement": "Accessibility statement", + "Accessibility_feature_documentation": "Accessibility feature documentation", "Accept_Call": "Accept Call", "Accept": "Accept", "Accept_incoming_livechat_requests_even_if_there_are_no_online_agents": "Accept incoming omnichannel requests even if there are no online agents", @@ -2453,6 +2455,7 @@ "Global Policy": "Global Policy", "Global_purge_override_warning": "A global retention policy is in place. If you leave \"Override global retention policy\" off, you can only apply a policy that is stricter than the global policy.", "Global_Search": "Global search", + "Glossary_of_simplified_terms": "Glossary of simplified terms", "Go_to_your_workspace": "Go to your workspace", "Go_to_accessibility_and_appearance": "Go to accessibility and appearance", "Google_Meet_Premium_only": "Google Meet (Premium only)", @@ -3061,6 +3064,7 @@ "Lead_capture_phone_regex": "Lead capture phone regex", "Learn_more": "Learn more", "Learn_more_about_agents": "Learn more about agents", + "Learn_more_about_accessibility": "Learn more about our commitment with accessibility here:", "Learn_more_about_business_hours": "Learn more about business hours", "Learn_more_about_canned_responses": "Learn more about canned responses", "Learn_more_about_contacts": "Learn more about contacts", From f0475cc4cf8c4054923897397ec534aa69935408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Guimar=C3=A3es=20Ribeiro?= Date: Wed, 31 Jan 2024 16:40:02 -0300 Subject: [PATCH 005/207] fix: Login email verification flow (#31583) Co-authored-by: Hugo Costa <20212776+hugocostadev@users.noreply.github.com> --- .changeset/flat-windows-juggle.md | 6 +++++ apps/meteor/tests/e2e/page-objects/auth.ts | 1 - .../src/EmailConfirmationForm.tsx | 6 +++-- .../web-ui-registration/src/LoginForm.tsx | 22 +++++++++---------- 4 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 .changeset/flat-windows-juggle.md diff --git a/.changeset/flat-windows-juggle.md b/.changeset/flat-windows-juggle.md new file mode 100644 index 000000000000..4a0410b7fab1 --- /dev/null +++ b/.changeset/flat-windows-juggle.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/web-ui-registration": patch +--- + +Fixed login email verification flow when a user tries to join with username diff --git a/apps/meteor/tests/e2e/page-objects/auth.ts b/apps/meteor/tests/e2e/page-objects/auth.ts index 98421f6461ab..51290d46f9cc 100644 --- a/apps/meteor/tests/e2e/page-objects/auth.ts +++ b/apps/meteor/tests/e2e/page-objects/auth.ts @@ -15,7 +15,6 @@ export class Registration { return this.page.locator('role=button[name="Reset"]'); } - get btnLogin(): Locator { return this.page.locator('role=button[name="Login"]'); } diff --git a/packages/web-ui-registration/src/EmailConfirmationForm.tsx b/packages/web-ui-registration/src/EmailConfirmationForm.tsx index 680d90897632..3355081caa31 100644 --- a/packages/web-ui-registration/src/EmailConfirmationForm.tsx +++ b/packages/web-ui-registration/src/EmailConfirmationForm.tsx @@ -9,6 +9,9 @@ import { useLoginSendEmailConfirmation } from './hooks/useLoginSendEmailConfirma export const EmailConfirmationForm = ({ email, onBackToLogin }: { email?: string; onBackToLogin: () => void }): ReactElement => { const { t } = useTranslation(); + const basicEmailRegex = /^[^@]+@[^@]+$/; + const isEmail = basicEmailRegex.test(email || ''); + const { register, handleSubmit, @@ -17,7 +20,7 @@ export const EmailConfirmationForm = ({ email, onBackToLogin }: { email?: string email: string; }>({ defaultValues: { - email, + email: isEmail ? email : '', }, }); @@ -45,7 +48,6 @@ export const EmailConfirmationForm = ({ email, onBackToLogin }: { email?: string {...register('email', { required: true, })} - disabled={Boolean(email)} error={errors.email && t('registration.component.form.requiredField')} aria-invalid={errors?.email?.type === 'required'} placeholder={t('registration.component.form.emailPlaceholder')} diff --git a/packages/web-ui-registration/src/LoginForm.tsx b/packages/web-ui-registration/src/LoginForm.tsx index 94ea007344f6..55c638df6707 100644 --- a/packages/web-ui-registration/src/LoginForm.tsx +++ b/packages/web-ui-registration/src/LoginForm.tsx @@ -66,7 +66,7 @@ export const LoginForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRoute clearErrors, getValues, formState: { errors }, - } = useForm<{ username: string; password: string }>({ + } = useForm<{ usernameOrEmail: string; password: string }>({ mode: 'onBlur', }); @@ -83,12 +83,12 @@ export const LoginForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRoute useDocumentTitle(t('registration.component.login'), false); const loginMutation = useMutation({ - mutationFn: (formData: { username: string; password: string }) => { - return login(formData.username, formData.password); + mutationFn: (formData: { usernameOrEmail: string; password: string }) => { + return login(formData.usernameOrEmail, formData.password); }, onError: (error: any) => { if ([error.error, error.errorType].includes('error-invalid-email')) { - setError('username', { type: 'invalid-email', message: t('registration.page.login.errors.invalidEmail') }); + setError('usernameOrEmail', { type: 'invalid-email', message: t('registration.page.login.errors.invalidEmail') }); } if ('error' in error && error.error !== 403) { @@ -119,8 +119,8 @@ export const LoginForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRoute ); }; - if (errors.username?.type === 'invalid-email') { - return clearErrors('username')} email={getValues('username')} />; + if (errors.usernameOrEmail?.type === 'invalid-email') { + return clearErrors('usernameOrEmail')} email={getValues('usernameOrEmail')} />; } return ( @@ -144,19 +144,19 @@ export const LoginForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRoute - {errors.username && ( + {errors.usernameOrEmail && ( - {errors.username.message} + {errors.usernameOrEmail.message} )}
From 21dddabbee772462076ee8ddc4bb8cf56f66091d Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Wed, 31 Jan 2024 17:15:49 -0300 Subject: [PATCH 006/207] refactor: Move reaction tooltip to its own component (#31532) --- .../message/content/reactions/Reaction.tsx | 84 +++--------------- .../content/reactions/ReactionTooltip.tsx | 88 +++++++++++++++++++ 2 files changed, 98 insertions(+), 74 deletions(-) create mode 100644 apps/meteor/client/components/message/content/reactions/ReactionTooltip.tsx diff --git a/apps/meteor/client/components/message/content/reactions/Reaction.tsx b/apps/meteor/client/components/message/content/reactions/Reaction.tsx index c3ad037d0ccb..19744674bcd2 100644 --- a/apps/meteor/client/components/message/content/reactions/Reaction.tsx +++ b/apps/meteor/client/components/message/content/reactions/Reaction.tsx @@ -1,36 +1,13 @@ import { MessageReaction as MessageReactionTemplate, MessageReactionEmoji, MessageReactionCounter } from '@rocket.chat/fuselage'; -import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import { useTooltipClose, useTooltipOpen, useTranslation } from '@rocket.chat/ui-contexts'; -import { useQueryClient } from '@tanstack/react-query'; +import { useTooltipClose, useTooltipOpen } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; -import React, { useContext, useRef } from 'react'; +import React, { useRef, useContext } from 'react'; import { getEmojiClassNameAndDataTitle } from '../../../../lib/utils/renderEmoji'; -import { useGetMessageByID } from '../../../../views/room/contextualBar/Threads/hooks/useGetMessageByID'; -import MarkdownText from '../../../MarkdownText'; import { MessageListContext } from '../../list/MessageListContext'; +import ReactionTooltip from './ReactionTooltip'; // TODO: replace it with proper usage of i18next plurals -const getTranslationKey = (users: string[], mine: boolean): TranslationKey => { - if (users.length === 0) { - if (mine) { - return 'You_reacted_with'; - } - } - - if (users.length > 10) { - if (mine) { - return 'You_users_and_more_Reacted_with'; - } - return 'Users_and_more_reacted_with'; - } - - if (mine) { - return 'You_and_users_Reacted_with'; - } - return 'Users_reacted_with'; -}; - type ReactionProps = { hasReacted: (name: string) => boolean; counter: number; @@ -41,7 +18,6 @@ type ReactionProps = { }; const Reaction = ({ hasReacted, counter, name, names, messageId, ...props }: ReactionProps): ReactElement => { - const t = useTranslation(); const ref = useRef(null); const openTooltip = useTooltipOpen(); const closeTooltip = useTooltipClose(); @@ -49,46 +25,8 @@ const Reaction = ({ hasReacted, counter, name, names, messageId, ...props }: Rea const mine = hasReacted(name); - const key = getTranslationKey(names, mine); - const emojiProps = getEmojiClassNameAndDataTitle(name); - const getMessage = useGetMessageByID(); - - const queryClient = useQueryClient(); - - const getNames = async () => { - return queryClient.fetchQuery( - ['chat.getMessage', 'reactions', messageId, names], - async () => { - // This happens if the only reaction is from the current user - if (!names.length) { - return []; - } - - if (!showRealName) { - return names; - } - - const data = await getMessage(messageId); - - const { reactions } = data; - if (!reactions) { - return []; - } - - if (username) { - const index = reactions[name].usernames.indexOf(username); - index >= 0 && reactions[name].names?.splice(index, 1); - return (reactions[name].names || names).filter(Boolean); - } - - return reactions[name].names || names; - }, - { staleTime: 1000 * 60 * 5 }, - ); - }; - return ( 10 ? names.length - 10 : names.length, - users: users?.slice(0, 10).join(', ') || '', - emoji: name, - })} - variant='inline' + , ref.current, ); diff --git a/apps/meteor/client/components/message/content/reactions/ReactionTooltip.tsx b/apps/meteor/client/components/message/content/reactions/ReactionTooltip.tsx new file mode 100644 index 000000000000..e36d5640e7cb --- /dev/null +++ b/apps/meteor/client/components/message/content/reactions/ReactionTooltip.tsx @@ -0,0 +1,88 @@ +import { useTranslation } from '@rocket.chat/ui-contexts'; +import type { TranslationKey } from '@rocket.chat/ui-contexts'; +import { useQuery } from '@tanstack/react-query'; +import React from 'react'; + +import { useGetMessageByID } from '../../../../views/room/contextualBar/Threads/hooks/useGetMessageByID'; +import MarkdownText from '../../../MarkdownText'; + +type ReactionTooltipProps = { + emojiName: string; + usernames: string[]; + username: string | undefined; + mine: boolean; + showRealName: boolean; + messageId: string; +}; + +const getTranslationKey = (users: string[], mine: boolean): TranslationKey => { + if (users.length === 0) { + if (mine) { + return 'You_reacted_with'; + } + } + + if (users.length > 10) { + if (mine) { + return 'You_users_and_more_Reacted_with'; + } + return 'Users_and_more_reacted_with'; + } + + if (mine) { + return 'You_and_users_Reacted_with'; + } + return 'Users_reacted_with'; +}; + +const ReactionTooltip = ({ emojiName, usernames, mine, messageId, showRealName, username }: ReactionTooltipProps) => { + const t = useTranslation(); + + const key = getTranslationKey(usernames, mine); + + const getMessage = useGetMessageByID(); + + const { data: users } = useQuery( + ['chat.getMessage', 'reactions', messageId, usernames], + async () => { + // This happens if the only reaction is from the current user + if (!usernames.length) { + return []; + } + + if (!showRealName) { + return usernames; + } + + const data = await getMessage(messageId); + + const { reactions } = data; + + if (!reactions) { + return []; + } + + if (username) { + const index = reactions[emojiName].usernames.indexOf(username); + index >= 0 && reactions[emojiName].names?.splice(index, 1); + return (reactions[emojiName].names || usernames).filter(Boolean); + } + + return reactions[emojiName].names || usernames; + }, + { staleTime: 1000 * 60 * 5 }, + ); + + return ( + 10 ? usernames.length - 10 : usernames.length, + users: users?.slice(0, 10).join(', ') || '', + emoji: emojiName, + })} + variant='inline' + /> + ); +}; + +export default ReactionTooltip; From 037894b8118ce36388bba9652c33c0d1396309c0 Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Wed, 31 Jan 2024 17:49:11 -0300 Subject: [PATCH 007/207] fix: Composer losing edit state when resizing the window. (#31589) --- .changeset/curly-years-smile.md | 5 +++++ .../client/views/room/composer/messageBox/MessageBox.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/curly-years-smile.md diff --git a/.changeset/curly-years-smile.md b/.changeset/curly-years-smile.md new file mode 100644 index 000000000000..78ce09097844 --- /dev/null +++ b/.changeset/curly-years-smile.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed an issue with the composer losing its edit state and highlighted after resizing the window. diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx b/apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx index 45aabfe9c949..043837f8c8c6 100644 --- a/apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx +++ b/apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx @@ -143,7 +143,7 @@ const MessageBox = ({ const callbackRef = useCallback( (node: HTMLTextAreaElement) => { - if (node === null) { + if (node === null || chat.composer) { return; } chat.setComposerAPI(createComposerAPI(node, storageID)); From 0d7753cba5b8db4663d82c7352aaa77e24dd2014 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Thu, 1 Feb 2024 17:47:19 -0300 Subject: [PATCH 008/207] ci: add back carryforward flag (#31605) --- codecov.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/codecov.yml b/codecov.yml index f9bbd51211c7..ee96be7cf2a1 100644 --- a/codecov.yml +++ b/codecov.yml @@ -12,13 +12,22 @@ coverage: flags: - client flags: + unit: + carryforward: true e2e: paths: - apps/meteor/ + carryforward: true + + e2e-api: + paths: + - apps/meteor/server + carryforward: true client: paths: - apps/meteor/client + carryforward: true comment: layout: 'reach, diff, flags' From 387c7716b11d5df8c86a552985fd82578f4583ff Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Mon, 5 Feb 2024 10:46:27 -0300 Subject: [PATCH 009/207] test: make autotranslate api tests fully independent (#31581) --- .../tests/end-to-end/api/00-autotranslate.js | 251 +++++++++++------- 1 file changed, 148 insertions(+), 103 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/00-autotranslate.js b/apps/meteor/tests/end-to-end/api/00-autotranslate.js index fe2e45ef3612..7397df990849 100644 --- a/apps/meteor/tests/end-to-end/api/00-autotranslate.js +++ b/apps/meteor/tests/end-to-end/api/00-autotranslate.js @@ -6,7 +6,20 @@ import { sendSimpleMessage } from '../../data/chat.helper'; import { updatePermission, updateSetting } from '../../data/permissions.helper'; import { createRoom, deleteRoom } from '../../data/rooms.helper'; import { password } from '../../data/user'; -import { createUser, login } from '../../data/users.helper.js'; +import { createUser, deleteUser, login } from '../../data/users.helper.js'; + +const resetAutoTranslateDefaults = async () => { + await Promise.all([ + updateSetting('AutoTranslate_Enabled', false), + updateSetting('AutoTranslate_AutoEnableOnJoinRoom', false), + updateSetting('Language', ''), + updatePermission('auto-translate', ['admin']), + ]); +}; + +const resetE2EDefaults = async () => { + await Promise.all([updateSetting('E2E_Enabled_Default_PrivateRooms', false), updateSetting('E2E_Enable', false)]); +}; describe('AutoTranslate', function () { this.retries(0); @@ -15,22 +28,23 @@ describe('AutoTranslate', function () { describe('[AutoTranslate]', () => { describe('[/autotranslate.getSupportedLanguages', () => { + before(() => resetAutoTranslateDefaults()); + after(() => resetAutoTranslateDefaults()); + it('should throw an error when the "AutoTranslate_Enabled" setting is disabled', (done) => { - updateSetting('AutoTranslate_Enabled', false).then(() => { - request - .get(api('autotranslate.getSupportedLanguages')) - .set(credentials) - .query({ - targetLanguage: 'en', - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.a.property('success', false); - expect(res.body.error).to.be.equal('AutoTranslate is disabled.'); - }) - .end(done); - }); + request + .get(api('autotranslate.getSupportedLanguages')) + .set(credentials) + .query({ + targetLanguage: 'en', + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.a.property('success', false); + expect(res.body.error).to.be.equal('AutoTranslate is disabled.'); + }) + .end(done); }); it('should throw an error when the user does not have the "auto-translate" permission', (done) => { updateSetting('AutoTranslate_Enabled', true).then(() => { @@ -70,38 +84,48 @@ describe('AutoTranslate', function () { }); }); }); + describe('[/autotranslate.saveSettings', () => { let testGroupId; + let testChannelId; + before(async () => { - await updateSetting('E2E_Enable', true); - await updateSetting('E2E_Enabled_Default_PrivateRooms', true); - const res = await createRoom({ type: 'p', name: `e2etest-autotranslate-${Date.now()}` }); - testGroupId = res.body.group._id; + await Promise.all([ + resetAutoTranslateDefaults(), + updateSetting('E2E_Enable', true), + updateSetting('E2E_Enabled_Default_PrivateRooms', true), + ]); + + testGroupId = (await createRoom({ type: 'p', name: `e2etest-autotranslate-${Date.now()}` })).body.group._id; + testChannelId = (await createRoom({ type: 'c', name: `test-autotranslate-${Date.now()}` })).body.channel._id; }); + after(async () => { - await updateSetting('E2E_Enabled_Default_PrivateRooms', false); - await updateSetting('E2E_Enable', false); - await deleteRoom({ type: 'p', roomId: testGroupId }); + await Promise.all([ + resetAutoTranslateDefaults(), + resetE2EDefaults(), + deleteRoom({ type: 'p', roomId: testGroupId }), + deleteRoom({ type: 'c', roomId: testChannelId }), + ]); }); + it('should throw an error when the "AutoTranslate_Enabled" setting is disabled', (done) => { - updateSetting('AutoTranslate_Enabled', false).then(() => { - request - .post(api('autotranslate.saveSettings')) - .set(credentials) - .send({ - roomId: 'GENERAL', - field: 'autoTranslate', - defaultLanguage: 'en', - value: true, - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.a.property('success', false); - expect(res.body.error).to.be.equal('AutoTranslate is disabled.'); - }) - .end(done); - }); + request + .post(api('autotranslate.saveSettings')) + .set(credentials) + .send({ + roomId: testChannelId, + field: 'autoTranslate', + defaultLanguage: 'en', + value: true, + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.a.property('success', false); + expect(res.body.error).to.be.equal('AutoTranslate is disabled.'); + }) + .end(done); }); it('should throw an error when the user does not have the "auto-translate" permission', (done) => { updateSetting('AutoTranslate_Enabled', true).then(() => { @@ -110,7 +134,7 @@ describe('AutoTranslate', function () { .post(api('autotranslate.saveSettings')) .set(credentials) .send({ - roomId: 'GENERAL', + roomId: testChannelId, defaultLanguage: 'en', field: 'autoTranslateLanguage', value: 'en', @@ -145,7 +169,7 @@ describe('AutoTranslate', function () { .post(api('autotranslate.saveSettings')) .set(credentials) .send({ - roomId: 'GENERAL', + roomId: testChannelId, }) .expect('Content-Type', 'application/json') .expect(400) @@ -159,7 +183,7 @@ describe('AutoTranslate', function () { .post(api('autotranslate.saveSettings')) .set(credentials) .send({ - roomId: 'GENERAL', + roomId: testChannelId, field: 'autoTranslate', }) .expect('Content-Type', 'application/json') @@ -174,7 +198,7 @@ describe('AutoTranslate', function () { .post(api('autotranslate.saveSettings')) .set(credentials) .send({ - roomId: 'GENERAL', + roomId: testChannelId, field: 'autoTranslate', value: 'test', }) @@ -190,7 +214,7 @@ describe('AutoTranslate', function () { .post(api('autotranslate.saveSettings')) .set(credentials) .send({ - roomId: 'GENERAL', + roomId: testChannelId, field: 'autoTranslateLanguage', value: 12, }) @@ -206,7 +230,7 @@ describe('AutoTranslate', function () { .post(api('autotranslate.saveSettings')) .set(credentials) .send({ - roomId: 'GENERAL', + roomId: testChannelId, field: 'invalid', value: 12, }) @@ -257,7 +281,7 @@ describe('AutoTranslate', function () { .post(api('autotranslate.saveSettings')) .set(credentials) .send({ - roomId: 'GENERAL', + roomId: testChannelId, field: 'autoTranslateLanguage', value: 'en', }) @@ -269,36 +293,41 @@ describe('AutoTranslate', function () { .end(done); }); }); + describe('[/autotranslate.translateMessage', () => { let messageSent; + let testChannelId; - before((done) => { - sendSimpleMessage({ - roomId: 'GENERAL', + before(async () => { + await resetAutoTranslateDefaults(); + + testChannelId = (await createRoom({ type: 'c', name: `test-autotranslate-message-${Date.now()}` })).body.channel._id; + const res = await sendSimpleMessage({ + roomId: testChannelId, text: 'Isso é um teste', - }).end((err, res) => { - messageSent = res.body.message; - done(); }); + messageSent = res.body.message; + }); + + after(async () => { + await Promise.all([resetAutoTranslateDefaults(), deleteRoom({ type: 'c', roomId: testChannelId })]); }); it('should throw an error when the "AutoTranslate_Enabled" setting is disabled', (done) => { - updateSetting('AutoTranslate_Enabled', false).then(() => { - request - .post(api('autotranslate.translateMessage')) - .set(credentials) - .send({ - messageId: 'test', - targetLanguage: 'en', - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.a.property('success', false); - expect(res.body.error).to.be.equal('AutoTranslate is disabled.'); - }) - .end(done); - }); + request + .post(api('autotranslate.translateMessage')) + .set(credentials) + .send({ + messageId: 'test', + targetLanguage: 'en', + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.a.property('success', false); + expect(res.body.error).to.be.equal('AutoTranslate is disabled.'); + }) + .end(done); }); it('should throw an error when the bodyParam "messageId" is not provided', (done) => { updateSetting('AutoTranslate_Enabled', true).then(() => { @@ -346,12 +375,14 @@ describe('AutoTranslate', function () { .end(done); }); }); + describe('Autoenable setting', () => { let userA; let userB; let credA; let credB; let channel; + const channelsToRemove = []; const createChannel = async (members, cred) => (await createRoom({ type: 'c', members, name: `channel-test-${Date.now()}`, credentials: cred })).body.channel; @@ -385,28 +416,37 @@ describe('AutoTranslate', function () { ).body.subscription; before(async () => { - await updateSetting('AutoTranslate_Enabled', true); - await updateSetting('AutoTranslate_AutoEnableOnJoinRoom', true); - await updateSetting('Language', 'pt-BR'); + await Promise.all([ + updateSetting('AutoTranslate_Enabled', true), + updateSetting('AutoTranslate_AutoEnableOnJoinRoom', true), + updateSetting('Language', 'pt-BR'), + ]); - channel = await createChannel(); userA = await createUser(); userB = await createUser(); credA = await login(userA.username, password); credB = await login(userB.username, password); + channel = await createChannel(undefined, credA); + await setLanguagePref('en', credB); + channelsToRemove.push(channel); }); after(async () => { - await updateSetting('AutoTranslate_AutoEnableOnJoinRoom', false); - await updateSetting('AutoTranslate_Enabled', false); - await updateSetting('Language', ''); + await Promise.all([ + updateSetting('AutoTranslate_AutoEnableOnJoinRoom', false), + updateSetting('AutoTranslate_Enabled', false), + updateSetting('Language', ''), + deleteUser(userA), + deleteUser(userB), + channelsToRemove.map(() => deleteRoom({ type: 'c', roomId: channel._id })), + ]); }); it("should do nothing if the user hasn't changed his language preference", async () => { - const sub = await getSub(channel._id, credentials); + const sub = await getSub(channel._id, credA); expect(sub).to.not.have.property('autoTranslate'); expect(sub).to.not.have.property('autoTranslateLanguage'); }); @@ -414,69 +454,74 @@ describe('AutoTranslate', function () { it("should do nothing if the user changed his language preference to be the same as the server's", async () => { await setLanguagePref('pt-BR', credA); - const channel = await createChannel(undefined, credA); const sub = await getSub(channel._id, credA); expect(sub).to.not.have.property('autoTranslate'); expect(sub).to.not.have.property('autoTranslateLanguage'); }); + it('should enable autotranslate with the correct language when joining a room', async () => { + await request + .post(api('channels.join')) + .set(credB) + .send({ + roomId: channel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200); + + const sub = await getSub(channel._id, credB); + expect(sub).to.have.property('autoTranslate'); + expect(sub).to.have.property('autoTranslateLanguage').and.to.be.equal('en'); + }); + it('should enable autotranslate with the correct language when creating a new room', async () => { await setLanguagePref('en', credA); - const channel = await createChannel(undefined, credA); - const sub = await getSub(channel._id, credA); + const newChannel = await createChannel(undefined, credA); + const sub = await getSub(newChannel._id, credA); expect(sub).to.have.property('autoTranslate'); expect(sub).to.have.property('autoTranslateLanguage').and.to.be.equal('en'); + channelsToRemove.push(newChannel); }); it('should enable autotranslate for all the members added to the room upon creation', async () => { - const channel = await createChannel([userA.username, userB.username]); - const subA = await getSub(channel._id, credA); + const newChannel = await createChannel([userA.username, userB.username], credA); + const subA = await getSub(newChannel._id, credA); expect(subA).to.have.property('autoTranslate'); expect(subA).to.have.property('autoTranslateLanguage').and.to.be.equal('en'); - const subB = await getSub(channel._id, credB); + const subB = await getSub(newChannel._id, credB); expect(subB).to.have.property('autoTranslate'); expect(subB).to.have.property('autoTranslateLanguage').and.to.be.equal('en'); - }); - - it('should enable autotranslate with the correct language when joining a room', async () => { - await request - .post(api('channels.join')) - .set(credA) - .send({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(200); - - const sub = await getSub(channel._id, credA); - expect(sub).to.have.property('autoTranslate'); - expect(sub).to.have.property('autoTranslateLanguage').and.to.be.equal('en'); + channelsToRemove.push(newChannel); }); it('should enable autotranslate with the correct language when added to a room', async () => { + const newChannel = await createChannel(undefined, credA); await request .post(api('channels.invite')) - .set(credentials) + .set(credA) .send({ - roomId: channel._id, + roomId: newChannel._id, userId: userB._id, }) .expect('Content-Type', 'application/json') .expect(200); - const sub = await getSub(channel._id, credB); + const sub = await getSub(newChannel._id, credB); expect(sub).to.have.property('autoTranslate'); expect(sub).to.have.property('autoTranslateLanguage').and.to.be.equal('en'); + channelsToRemove.push(newChannel); }); it('should change the auto translate language when the user changes his language preference', async () => { await setLanguagePref('es', credA); - const subscription = await getSub(channel._id, credA); + const newChannel = await createChannel(undefined, credA); + const subscription = await getSub(newChannel._id, credA); expect(subscription).to.have.property('autoTranslate', true); expect(subscription).to.have.property('autoTranslateLanguage', 'es'); + channelsToRemove.push(newChannel); }); }); }); From ef683599be43a0fe51b9a33a5c40bc577f5f82ec Mon Sep 17 00:00:00 2001 From: "lingohub[bot]" <69908207+lingohub[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:24:33 +0000 Subject: [PATCH 010/207] =?UTF-8?q?i18n:=20Language=20update=20from=20Ling?= =?UTF-8?q?oHub=20=F0=9F=A4=96=20on=202024-02-05Z=20(#31648)?= 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 | 1 + .../rocketchat-i18n/i18n/ar.i18n.json | 28 +- .../rocketchat-i18n/i18n/az.i18n.json | 1 + .../rocketchat-i18n/i18n/be-BY.i18n.json | 1 + .../rocketchat-i18n/i18n/bg.i18n.json | 1 + .../rocketchat-i18n/i18n/bs.i18n.json | 1 + .../rocketchat-i18n/i18n/ca.i18n.json | 6 +- .../rocketchat-i18n/i18n/cs.i18n.json | 13 +- .../rocketchat-i18n/i18n/cy.i18n.json | 1 + .../rocketchat-i18n/i18n/da.i18n.json | 5 +- .../rocketchat-i18n/i18n/de-AT.i18n.json | 1 + .../rocketchat-i18n/i18n/de.i18n.json | 6 +- .../rocketchat-i18n/i18n/el.i18n.json | 1 + .../rocketchat-i18n/i18n/en.i18n.json | 3 +- .../rocketchat-i18n/i18n/eo.i18n.json | 1 + .../rocketchat-i18n/i18n/es.i18n.json | 23 +- .../rocketchat-i18n/i18n/et.i18n.json | 2 +- .../rocketchat-i18n/i18n/fa.i18n.json | 5 +- .../rocketchat-i18n/i18n/fi.i18n.json | 6 +- .../rocketchat-i18n/i18n/fr.i18n.json | 12 +- .../rocketchat-i18n/i18n/he.i18n.json | 1 + .../rocketchat-i18n/i18n/hr.i18n.json | 1 + .../rocketchat-i18n/i18n/hu.i18n.json | 8 +- .../rocketchat-i18n/i18n/id.i18n.json | 3 +- .../rocketchat-i18n/i18n/it.i18n.json | 7 +- .../rocketchat-i18n/i18n/ja.i18n.json | 7 +- .../rocketchat-i18n/i18n/ka-GE.i18n.json | 3 +- .../rocketchat-i18n/i18n/km.i18n.json | 1 + .../rocketchat-i18n/i18n/ko.i18n.json | 5 +- .../rocketchat-i18n/i18n/ku.i18n.json | 1 + .../rocketchat-i18n/i18n/lo.i18n.json | 1 + .../rocketchat-i18n/i18n/lt.i18n.json | 1 + .../rocketchat-i18n/i18n/lv.i18n.json | 1 + .../rocketchat-i18n/i18n/mn.i18n.json | 1 + .../rocketchat-i18n/i18n/ms-MY.i18n.json | 3 +- .../rocketchat-i18n/i18n/nl.i18n.json | 6 +- .../rocketchat-i18n/i18n/no.i18n.json | 1609 ++++++++++++++++- .../rocketchat-i18n/i18n/pl.i18n.json | 24 +- .../rocketchat-i18n/i18n/pt-BR.i18n.json | 14 +- .../rocketchat-i18n/i18n/pt.i18n.json | 1 + .../rocketchat-i18n/i18n/ro.i18n.json | 1 + .../rocketchat-i18n/i18n/ru.i18n.json | 32 +- .../rocketchat-i18n/i18n/sk-SK.i18n.json | 1 + .../rocketchat-i18n/i18n/sl-SI.i18n.json | 1 + .../rocketchat-i18n/i18n/sq.i18n.json | 1 + .../rocketchat-i18n/i18n/sr.i18n.json | 1 + .../rocketchat-i18n/i18n/sv.i18n.json | 6 +- .../rocketchat-i18n/i18n/ta-IN.i18n.json | 1 + .../rocketchat-i18n/i18n/th-TH.i18n.json | 1 + .../rocketchat-i18n/i18n/tr.i18n.json | 3 +- .../rocketchat-i18n/i18n/ug.i18n.json | 1 + .../rocketchat-i18n/i18n/uk.i18n.json | 7 +- .../rocketchat-i18n/i18n/vi-VN.i18n.json | 3 +- .../rocketchat-i18n/i18n/zh-HK.i18n.json | 1 + .../rocketchat-i18n/i18n/zh-TW.i18n.json | 7 +- .../rocketchat-i18n/i18n/zh.i18n.json | 5 +- 56 files changed, 1770 insertions(+), 118 deletions(-) diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json index 90db76e13637..2ce3f4f8a0ed 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json @@ -2121,6 +2121,7 @@ "Running_Instances": "Running Instances", "Runtime_Environment": "Runtime Environment", "S_new_messages_since_s": "%s nuwe boodskappe sedert%s", + "S_new_messages": "%s nuwe boodskappe", "Same_As_Token_Sent_Via": "Dieselfde as \"Token Sent Via\"", "Same_Style_For_Mentions": "Dieselfde styl vir verwysings", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json index ab55aaba23c1..a31bdc1bd25f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json @@ -583,14 +583,18 @@ "Auditing": "تدقيق", "Auth_Token": "الرمز المميز للمصادقة", "Authentication": "المصادقة", + "Calls_in_queue_two": "{{count}} من المكالمات الانتظار", "Author": "المؤلف", + "Calls_in_queue_few": "{{count}} من المكالمات الانتظار", "Author_Information": "معلومات المؤلف", + "Calls_in_queue_many": "{{count}} من المكالمات الانتظار", "Author_Site": "موقع المؤلف", "Authorization_URL": "عنوان URL للمصادقة", "Authorize": "تصريح", "Auto_Load_Images": "رفع تلقائي للصور", "Auto_Selection": "التحديد التلقائي", "Auto_Translate": "ترجمة تلقائية", + "Calls_in_queue": "{{calls}} من المكالمات الانتظار", "auto-translate": "ترجمة تلقائية", "auto-translate_description": "إذن لاستخدام أداة الترجمة التلقائية", "Automatic_Translation": "ترجمة تلقائية", @@ -708,9 +712,6 @@ "Call_Center": "مركز الاتصال", "Calls_in_queue_zero": "قائمة الانتظار فارغة", "Calls_in_queue_one": "{{count}} من المكالمات الانتظار", - "Calls_in_queue_two": "{{count}} من المكالمات الانتظار", - "Calls_in_queue_few": "{{count}} من المكالمات الانتظار", - "Calls_in_queue_many": "{{count}} من المكالمات الانتظار", "Calls_in_queue_other": "{{count}} من المكالمات الانتظار", "Call_declined": "تم رفض المكالمة!", "Call_Information": "معلومات المكالمة", @@ -928,7 +929,6 @@ "Connection_Closed": "تم إغلاق الاتصال", "Connection_Reset": "إعادة تعيين الاتصال", "Connection_error": "خطأ في الاتصال", - "LDAP_Connection_successful": "اتصال LDAP ناجح", "Connection_failed": "فشل اتصال LDAP", "Connectivity_Services": "خدمات الاتصال", "Consulting": "الاستشارات", @@ -2380,10 +2380,14 @@ "Language_Japanese": "اليابانية", "Language_Latvian": "اللاتفية", "Language_Lithuanian": "اللتوانية", + "message_counter_zero": "{{count}} رسائل", "Language_Not_set": "غير محددة", "Language_Polish": "البولندية", + "message_counter_two": "{{count}} رسائل", "Language_Portuguese": "البرتغالية", + "message_counter_few": "{{count}} رسائل", "Language_Romanian": "الرومانية", + "message_counter_many": "{{count}} رسائل", "Language_Russian": "الروسية", "Language_Slovak": "السلوفاكية", "Language_Slovenian": "السلوفينية", @@ -2421,6 +2425,7 @@ "LDAP_Connection": "الاتصال", "LDAP_Connection_Authentication": "المصادقة", "LDAP_Connection_Encryption": "التشفير", + "LDAP_Connection_successful": "اتصال LDAP ناجح", "LDAP_Connection_Timeouts": "المهلات", "LDAP_UserSearch": "بحث المستخدم", "LDAP_UserSearch_Filter": "عامل تصفية البحث", @@ -2452,10 +2457,14 @@ "LDAP_Background_Sync": "مزامنة الخلفية", "LDAP_Background_Sync_Avatars": "مزامنة خلفية الصورة الرمزية", "LDAP_Background_Sync_Avatars_Description": "تمكين إجراء خلفية منفصلة لمزامنة الصور الرمزية للمستخدم.", + "meteor_status_reconnect_in_zero": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "LDAP_Background_Sync_Avatars_Interval": "الفاصل الزمني لمزامنة خلفية الصورة الرمزية", "LDAP_Background_Sync_Import_New_Users": "مزامنة الخلفية لاستيراد مستخدمين جدد", "LDAP_Background_Sync_Import_New_Users_Description": "سيتم استيراد كل المستخدمين (بناءً على معايير التصفية الخاصة بك) الموجودين في LDAP وغير الموجودين في Rocket.Chat", + "meteor_status_reconnect_in_two": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "LDAP_Background_Sync_Interval": "الفاصل الزمني لمزامنة الخلفية", + "meteor_status_reconnect_in_few": "المحاولة مرة أخرى خلال {{count}} من الثواني...", + "meteor_status_reconnect_in_many": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "LDAP_Background_Sync_Interval_Description": "الفاصل الزمني بين عمليات المزامنة. مثال \"كل 24 ساعة\" أو \"في اليوم الأول من الأسبوع\"، المزيد من الأمثلة على [محلل نص Cron] (http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "مزامنة الخلفية لتحديث المستخدمين الحاليين", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "ستتم مزامنة الصورة الرمزية والحقول واسم المستخدم، وما إلى ذلك (بناءً على التكوين الخاص بك) لجميع المستخدمين الذين تم استيرادهم من LDAP في كل **فاصل مزامنة**", @@ -2850,11 +2859,7 @@ "Message_Characther_Limit": "حد أحرف الرسالة", "Message_Code_highlight": "رمز تمييز قائمة اللغات", "Message_Code_highlight_Description": "قائمة اللغات المفصولة بفواصل (كل اللغات المدعومة على [highlight.js](https://github.com/highlightjs/highlight.js/tree/11.6.0#supported-languages)) التي سيتم استخدامها لتمييز الكتل البرمجية للتعليمات البرمجية", - "message_counter_zero": "{{count}} رسائل", "message_counter_one": "{{count}} رسالة", - "message_counter_two": "{{count}} رسائل", - "message_counter_few": "{{count}} رسائل", - "message_counter_many": "{{count}} رسائل", "message_counter_other": "{{count}} رسائل", "Message_DateFormat": "تنسيق التاريخ", "Message_DateFormat_Description": "انظر أيضًا: [Moment.js](http://momentjs.com/docs/#/displaying/format/)", @@ -2948,11 +2953,7 @@ "meteor_status_connecting": "يتم الاتصال الآن...", "meteor_status_failed": "فشل الاتصال بالخادم", "meteor_status_offline": "وضع عدم الاتصال.", - "meteor_status_reconnect_in_zero": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "meteor_status_reconnect_in_one": "المحاولة مرة أخرى خلال ثانية واحدة...", - "meteor_status_reconnect_in_two": "المحاولة مرة أخرى خلال {{count}} من الثواني...", - "meteor_status_reconnect_in_few": "المحاولة مرة أخرى خلال {{count}} من الثواني...", - "meteor_status_reconnect_in_many": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "meteor_status_reconnect_in_other": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "meteor_status_try_now_offline": "الاتصال مرة أخرى", "meteor_status_try_now_waiting": "المحاولة الآن", @@ -3648,6 +3649,7 @@ "Running_Instances": "مثيلات قيد التشغيل", "Runtime_Environment": "بيئة وقت التشغيل", "S_new_messages_since_s": "%s رسائل جديدة منذ %s", + "S_new_messages": "%s رسائل جديدة", "Same_As_Token_Sent_Via": "مثل \"الرمز المميز المرسل عبر\"", "Same_Style_For_Mentions": "نفس النمط للإشارات", "SAML": "SAML", @@ -4895,4 +4897,4 @@ "Enterprise": "مؤسسة", "UpgradeToGetMore_engagement-dashboard_Title": "التحليلات", "UpgradeToGetMore_auditing_Title": "تدقيق الرسائل" -} +} \ 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 d3c9146f868f..d7e76cd783d9 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json @@ -2121,6 +2121,7 @@ "Running_Instances": "Nümunələr Running", "Runtime_Environment": "Runtime Environment", "S_new_messages_since_s": "%s-dən bəri yeni mesajlar", + "S_new_messages": "%s yeni mesajlar", "Same_As_Token_Sent_Via": "\"Token göndərilən Token\" kimi", "Same_Style_For_Mentions": "Sözlər üçün eyni stil", "SAML": "SAML", 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 84af45c446ff..55a753d7b972 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json @@ -2138,6 +2138,7 @@ "Running_Instances": "запуск асобнікаў", "Runtime_Environment": "Runtime Environment", "S_new_messages_since_s": "%s новых паведамленняў з% з", + "S_new_messages": "%s новых паведамленняў", "Same_As_Token_Sent_Via": "Тое ж, што «токенаў пасланы праз»", "Same_Style_For_Mentions": "Той жа стыль згадвае", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json index a4bdc836a471..455c499adca8 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json @@ -2117,6 +2117,7 @@ "Running_Instances": "Изпълняващи потребителски модели", "Runtime_Environment": "По време на работа", "S_new_messages_since_s": "%s нови съобщения от %s", + "S_new_messages": "%s нови съобщения", "Same_As_Token_Sent_Via": "Същото като \"Token Sent Via\"", "Same_Style_For_Mentions": "Същият стил за споменаванията", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json index ff757e76e6ca..8dd9ddbfbb73 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json @@ -2113,6 +2113,7 @@ "Running_Instances": "Pokrenuta instanca", "Runtime_Environment": "Runtime Environment", "S_new_messages_since_s": "%s novih poruka od %s", + "S_new_messages": "%s novih poruka", "Same_As_Token_Sent_Via": "Isto kao i \"Token poslan putem\"", "Same_Style_For_Mentions": "Isti stil za spominjanje", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json index e9bfe1abc757..dfb6a98cdb5c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json @@ -590,6 +590,7 @@ "Auto_Load_Images": "Carregar automàticament les imatges", "Auto_Selection": "Selecció automàtica", "Auto_Translate": "Traducció automàtica", + "Calls_in_queue": "{{calls}} Trucada a la cua", "auto-translate": "Traducció automàtica", "auto-translate_description": "Permís per fer servir l'eina de traducció automàtica", "Automatic_Translation": "Traducció automàtica", @@ -920,7 +921,6 @@ "Connection_Closed": "Connexió tancada", "Connection_Reset": "Connexió restablerta", "Connection_error": "Error de connexió", - "LDAP_Connection_successful": "Connexió LDAP correcta", "Connection_failed": "Error de connexió LDAP", "Connectivity_Services": "Serveis de connectivitat", "Consulting": "Consultant", @@ -2392,6 +2392,7 @@ "LDAP_Connection": "Connexió", "LDAP_Connection_Authentication": "Autenticació", "LDAP_Connection_Encryption": "Xifrat", + "LDAP_Connection_successful": "Connexió LDAP correcta", "LDAP_Connection_Timeouts": "Temps d'espera", "LDAP_UserSearch": "Cerca d'usuari", "LDAP_UserSearch_Filter": "Filtre de cerca", @@ -3577,6 +3578,7 @@ "Running_Instances": "Instàncies executant-se", "Runtime_Environment": "Entorn d'execució", "S_new_messages_since_s": "%s nous des de: %s", + "S_new_messages": "%s nous", "Same_As_Token_Sent_Via": "El mateix que \"Token enviat a través de\"", "Same_Style_For_Mentions": "Mateix estil per a mencions", "SAML": "SAML", @@ -4688,4 +4690,4 @@ "Enterprise": "Empresa", "UpgradeToGetMore_engagement-dashboard_Title": "Analítiques", "UpgradeToGetMore_auditing_Title": "Auditoria de missatges" -} +} \ 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 d781ed49c144..8a617d173f5d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json @@ -753,7 +753,6 @@ "Connect": "Připojit", "Connection_Closed": "Připojení bylo uzavřeno", "Connection_Reset": "Obnovit připojení", - "LDAP_Connection_successful": "LDAP připojení úspěšné", "Connectivity_Services": "Služby připojení", "Consulting": "Konzultace", "Contact": "Kontakt", @@ -2029,6 +2028,8 @@ "Language_Not_set": "Nespecifikováno", "Language_Polish": "Polština", "Language_Portuguese": "Portugalština", + "message_counter_few": "{{count}} zpráv(y)", + "message_counter_many": "{{count}} zpráv(y)", "Language_Russian": "Ruština", "Language_Spanish": "Španělština", "Language_Version": "Anglická verze", @@ -2056,6 +2057,7 @@ "Layout_Sidenav_Footer_Dark_description": "Velikost zápatí je 260 x 70 pixelů", "Layout_Terms_of_Service": "Podmínky služby", "LDAP": "LDAP", + "LDAP_Connection_successful": "LDAP připojení úspěšné", "LDAP_Advanced_Sync": "Pokročilá synchronizace", "LDAP_Authentication": "Povolit", "LDAP_Authentication_Password": "Heslo", @@ -2068,6 +2070,8 @@ "LDAP_Background_Sync_Import_New_Users": "Synchronizace na pozadí importuje nové uživatele", "LDAP_Background_Sync_Import_New_Users_Description": "Naimportuje všechny uživatele (podle filtrů) nalezených v LDAP a neexistují v Rocket.Chat", "LDAP_Background_Sync_Interval": "Interval synchronizace na pozadí", + "meteor_status_reconnect_in_few": "zkusím znovu za {{count}} sekund...", + "meteor_status_reconnect_in_many": "zkusím znovu za {{count}} sekund...", "LDAP_Background_Sync_Interval_Description": "Interval mezi synchronizacemi. Například `every 24 hours` nebo `on the first day of the week`, více příkladů - [Cron Text Parser](http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Synchronizace na pozadí upravuje existující uživatele", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Synchronizuje avatary, uživatelská pole, uživatelské jméno atd (podle vaší konfigurace) všech uživatelů importovaných z LDAP podle **Interval synchronizace na pozadí**", @@ -2393,8 +2397,6 @@ "Message_BadWordsFilterListDescription": "Zadejte čárkou oddělený seznam sprostých slov k odfiltrování", "Message_Characther_Limit": "Maximální délka zprávy", "message_counter_one": "{{count}} zpráva", - "message_counter_few": "{{count}} zpráv(y)", - "message_counter_many": "{{count}} zpráv(y)", "message_counter_other": "{{count}} zpráv(y)", "Message_DateFormat": "Formát datumu", "Message_DateFormat_Description": "Viz také: [Moment.js](http://momentjs.com/docs/#/displaying/format/)", @@ -2469,8 +2471,6 @@ "meteor_status_failed": "Připojení k serveru selhalo", "meteor_status_offline": "Offline režim.", "meteor_status_reconnect_in_one": "zkusím znovu za sekundu...", - "meteor_status_reconnect_in_few": "zkusím znovu za {{count}} sekund...", - "meteor_status_reconnect_in_many": "zkusím znovu za {{count}} sekund...", "meteor_status_reconnect_in_other": "zkusím znovu za {{count}} sekund...", "meteor_status_try_now_offline": "Znovu připojit", "meteor_status_try_now_waiting": "Zkusit nyní", @@ -3038,6 +3038,7 @@ "Running_Instances": "Spuštěných instancí", "Runtime_Environment": "Běhové prostředí", "S_new_messages_since_s": "%s nových zprávy od %s", + "S_new_messages": "%s nových zpráv", "Same_As_Token_Sent_Via": "Stejné jako \"Token odesílány přes\"", "Same_Style_For_Mentions": "Stejný styl pro zmínky", "SAML": "SAML", @@ -3967,4 +3968,4 @@ "Enterprise": "Korporace", "UpgradeToGetMore_engagement-dashboard_Title": "Analytika", "UpgradeToGetMore_auditing_Title": "Audit zpráv" -} +} \ 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 c3b473dab7ce..4070f86a2896 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json @@ -2115,6 +2115,7 @@ "Running_Instances": "Digwyddiadau Rhedeg", "Runtime_Environment": "Amgylchedd Runtime", "S_new_messages_since_s": "%s negeseuon newydd ers %s", + "S_new_messages": "%s negeseuon newydd", "Same_As_Token_Sent_Via": "Yr un fath â \"Token Sent Via\"", "Same_Style_For_Mentions": "Yr un arddull i'w sôn", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json index 6cebc4b9fd76..0bcfce6c606c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json @@ -757,7 +757,6 @@ "Connect": "Forbind", "Connection_Closed": "Forbindelse lukket", "Connection_Reset": "Nulstilning af forbindelse", - "LDAP_Connection_successful": "LDAP-forbindelse lykkedes", "Connectivity_Services": "Connectivity Services", "Consulting": "Rådgivning", "Contact": "Kontakt", @@ -2066,6 +2065,7 @@ "Layout_Sidenav_Footer_Dark_description": "Footer-størrelse er 260 x 70px", "Layout_Terms_of_Service": "Servicevilkår", "LDAP": "LDAP", + "LDAP_Connection_successful": "LDAP-forbindelse lykkedes", "LDAP_Advanced_Sync": "Avanceret synkronisering", "LDAP_Authentication": "Aktivér", "LDAP_Authentication_Password": "Adgangskode", @@ -3051,6 +3051,7 @@ "Running_Instances": "Running Instances", "Runtime_Environment": "Runtime Environment", "S_new_messages_since_s": "%s nye meddelelser siden%s", + "S_new_messages": "%s nye meddelelser", "Same_As_Token_Sent_Via": "Samme som \"Token Send Via\"", "Same_Style_For_Mentions": "Samme stil til navne", "SAML": "SAML", @@ -3985,4 +3986,4 @@ "Enterprise": "Firma", "UpgradeToGetMore_engagement-dashboard_Title": "Analyse", "UpgradeToGetMore_auditing_Title": "Meddelelsesovervågning" -} +} \ 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 18325e65a0f9..3a3a714b32cb 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json @@ -2123,6 +2123,7 @@ "Running_Instances": "Laufende Instanzen", "Runtime_Environment": "Laufzeitumgebung", "S_new_messages_since_s": "%s neue Nachrichten seit %s", + "S_new_messages": "%s neue Nachrichten", "Same_As_Token_Sent_Via": "Wie \"Token Sent Via\"", "Same_Style_For_Mentions": "Gleicher Stil für Erwähnungen", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json index f14e7fb67c3a..7c86c298ebf3 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json @@ -665,6 +665,7 @@ "Auto_Load_Images": "Automatisches Laden der Bilder", "Auto_Selection": "Automatische Auswahl", "Auto_Translate": "Automatische Übersetzung", + "Calls_in_queue": "{{calls}} Anrufe in der Warteschlange", "auto-translate": "Automatische Übersetzung", "auto-translate_description": "Berechtigung, die automatische Überstzung zu verwenden", "Automatic_Translation": "Automatische Übersetzung", @@ -1046,7 +1047,6 @@ "Connection_Closed": "Verbindung geschlossen", "Connection_Reset": "Verbindung zurücksetzen", "Connection_error": "Verbindungsfehler", - "LDAP_Connection_successful": "LDAP-Verbindung erfolgreich", "Connection_failed": "LDAP-Verbindung fehlgeschlagen", "Connectivity_Services": "Verbindungsdienste", "Consulting": "Beratung", @@ -2704,6 +2704,7 @@ "LDAP_Connection": "Verbindung", "LDAP_Connection_Authentication": "Authentifizierung", "LDAP_Connection_Encryption": "Verschlüsselung", + "LDAP_Connection_successful": "LDAP-Verbindung erfolgreich", "LDAP_Connection_Timeouts": "Zeitüberschreitungen", "LDAP_UserSearch": "Benutzersuche", "LDAP_UserSearch_Filter": "Suchfilter", @@ -4096,6 +4097,7 @@ "Running_Instances": "Laufende Instanzen", "Runtime_Environment": "Laufzeitumgebung", "S_new_messages_since_s": "%s neue Nachrichten seit %s", + "S_new_messages": "%s neue Nachrichten", "Same_As_Token_Sent_Via": "Wie \"Token Sent Via\"", "Same_Style_For_Mentions": "Gleicher Stil für Erwähnungen", "SAML": "SAML", @@ -5514,4 +5516,4 @@ "Enterprise": "Unternehmen", "UpgradeToGetMore_engagement-dashboard_Title": "Analytics", "UpgradeToGetMore_auditing_Title": "Nachrichtenüberprüfung" -} +} \ 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 57c07c0b8129..60f77225fbf0 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json @@ -2129,6 +2129,7 @@ "Running_Instances": "Εκτέλεση παρουσιών", "Runtime_Environment": "Περιβάλλον χρόνου εκτέλεσης", "S_new_messages_since_s": "%s νέα μηνύματα από το %s", + "S_new_messages": "%s νέα μηνύματα", "Same_As_Token_Sent_Via": "Το ίδιο με το \"Token Sent Via\"", "Same_Style_For_Mentions": "Το ίδιο στυλ για τις αναφορές", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index dc893c25c85b..f63a8974030a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -720,6 +720,7 @@ "Auto_Load_Images": "Auto Load Images", "Auto_Selection": "Auto Selection", "Auto_Translate": "Auto-Translate", + "Calls_in_queue": "{{calls}} call in queue", "auto-translate": "Auto Translate", "auto-translate_description": "Permission to use the auto translate tool", "Automatic_Translation": "Automatic Translation", @@ -6288,4 +6289,4 @@ "Seat_limit_reached": "Seat limit reached", "Seat_limit_reached_Description": "Your workspace reached its contractual seat limit. Buy more seats to add more users.", "Buy_more_seats": "Buy more seats" -} +} \ 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 68ec0dbe60f3..0f13dce91a00 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json @@ -2121,6 +2121,7 @@ "Running_Instances": "Rultaj ekzemploj", "Runtime_Environment": "Runtime Medio", "S_new_messages_since_s": "%s novaj mesaĝoj ekde%s", + "S_new_messages": "%s novaj mesaĝoj", "Same_As_Token_Sent_Via": "Same kiel \"Token Sent Via\"", "Same_Style_For_Mentions": "Sama stilo por mencioj", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json index bb39ba5d4c09..d6fa873437dc 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json @@ -10,8 +10,8 @@ "__count__tags__and__count__conversations__period__": "{{count}} etiquetas y {{conversations}} conversaciones, {{period}}", "__departments__departments_and__count__conversations__period__": "{{departments}} departamentos y {{count}} conversaciones, {{period}}", "__usersCount__member_joined_one": "{{count}} miembro se ha unido", - "__usersCount__member_joined_many": "{{count}} miembros se han unido", "__usersCount__member_joined_other": "{{count}} miembros se han unido", + "__usersCount__member_joined_many": "{{count}} miembros se han unido", "__usersCount__people_will_be_invited": "{{usersCount}} miembros sern invitados", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} ya no es {{role}} (por {{user_by}})", "__username__was_set__role__by__user_by_": "{{username}} se ha establecido como {{role}} por {{user_by}}", @@ -607,6 +607,7 @@ "Auto_Load_Images": "Cargar automáticamente las imágenes", "Auto_Selection": "Selección automática", "Auto_Translate": "Traducción automática", + "Calls_in_queue": "{{count}} llamadas en cola", "auto-translate": "Traducción automática", "auto-translate_description": "Permiso para usar la herramienta de traducción automática", "Automatic_Translation": "Traducción automática", @@ -720,7 +721,6 @@ "Call": "Llamada", "Calling": "Llamando", "Call_Center": "Centro de llamadas", - "Calls_in_queue": "{{count}} llamadas en cola", "Call_declined": "Llamada rechazada", "Call_Information": "Información de llamada", "Call_provider": "Proveedor de llamadas", @@ -933,7 +933,6 @@ "Connection_Closed": "Conexión cerrada", "Connection_Reset": "Conexión restablecida", "Connection_error": "Error de conexión", - "LDAP_Connection_successful": "Conexión LDAP correcta", "Connection_failed": "Error de conexión LDAP", "Connectivity_Services": "Servicios de conectividad", "Consulting": "Consultoría", @@ -2378,6 +2377,7 @@ "Language_Polish": "Polaco", "Language_Portuguese": "Portugués", "Language_Romanian": "Rumano", + "message_counter_many": "{{count}} mensajes", "Language_Russian": "Ruso", "Language_Slovak": "Eslovaco", "Language_Slovenian": "Esloveno", @@ -2414,6 +2414,7 @@ "LDAP_Connection": "Conexión", "LDAP_Connection_Authentication": "Autenticación", "LDAP_Connection_Encryption": "Cifrado", + "LDAP_Connection_successful": "Conexión LDAP correcta", "LDAP_Connection_Timeouts": "Tiempos de espera", "LDAP_UserSearch": "Búsqueda de usuarios", "LDAP_UserSearch_Filter": "Filtro de búsqueda", @@ -2449,6 +2450,7 @@ "LDAP_Background_Sync_Import_New_Users": "Sincronización en segundo plano: importar nuevos usuarios", "LDAP_Background_Sync_Import_New_Users_Description": "Importará todos los usuarios (según tus criterios de filtro) que existen en LDAP y no existan en Rocket.Chat", "LDAP_Background_Sync_Interval": "Intervalo de sincronización en segundo plano", + "meteor_status_reconnect_in_many": "intentando de nuevo dentro de {{count}} segundos...", "LDAP_Background_Sync_Interval_Description": "Intervalo entre sincronizaciones, como \"cada 24 horas\" o \"el primer día de la semana\"; más ejemplos en [Cron Text Parser] (http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Sincronización en segundo plano: actualizar usuarios existentes", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Sincronizará el avatar, los campos, el nombre de usuario, etc. (según tu configuración) de todos los usuarios ya importados desde LDAP en cada **Intervalo de sincronización**", @@ -2844,7 +2846,6 @@ "Message_Code_highlight": "Lista de lenguajes para resaltar código", "Message_Code_highlight_Description": "Lista de lenguajes separados por comas (todos los lenguajes admitidos se pueden consultar en [highlight.js](https://github.com/highlightjs/highlight.js/tree/11.6.0#supported-languages)) que se usarán para resaltar bloques de código", "message_counter_one": "{{count}} mensaje", - "message_counter_many": "{{count}} mensajes", "message_counter_other": "{{count}} mensajes", "Message_DateFormat": "Formato de fecha", "Message_DateFormat_Description": "Ver también: [Moment.js](http://momentjs.com/docs/#/displaying/format/)", @@ -2938,7 +2939,6 @@ "meteor_status_failed": "Error de conexión con el servidor", "meteor_status_offline": "Modo de fuera de línea.", "meteor_status_reconnect_in_one": "intentando de nuevo dentro de un segundo...", - "meteor_status_reconnect_in_many": "intentando de nuevo dentro de {{count}} segundos...", "meteor_status_reconnect_in_other": "intentando de nuevo dentro de {{count}} segundos...", "meteor_status_try_now_offline": "Conectar de nuevo", "meteor_status_try_now_waiting": "Intentar ahora", @@ -3628,6 +3628,7 @@ "Running_Instances": "Instancias en ejecución", "Runtime_Environment": "Entorno de ejecución", "S_new_messages_since_s": "%s nuevos mensajes desde %s", + "S_new_messages": "%s nuevos mensajes", "Same_As_Token_Sent_Via": "Igual que \"Token enviado vía\"", "Same_Style_For_Mentions": "Mismo estilo para menciones", "SAML": "SAML", @@ -3939,7 +3940,9 @@ "Star": "Destacar", "Star_Message": "Destacar mensaje", "Starred_Messages": "Mensajes destacados", + "subscription.callout.description.limitsExceeded_many": "Su espacio de trabajo ha superado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "Start": "Iniciar", + "subscription.callout.description.limitsReached_many": "Su espacio de trabajo ha alcanzado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "Start_audio_call": "Iniciar llamada de audio", "Start_Chat": "Iniciar chat", "Start_of_conversation": "Inicio de la conversación", @@ -4029,7 +4032,10 @@ "Take_it": "Atiéndelo", "Take_rocket_chat_with_you_with_mobile_applications": "Llévate Rocket.Chat contigo con nuestras aplicaciones móviles.", "Taken_at": "Atendido a las", + "mentions_counter_many": "{{count}} menciones", "Talk_Time": "Tiempo de conversación", + "threads_counter_many": "{{count}} mensajes en hilo sin leer", + "unread_messages_counter_many": "{{count}} mensajes sin leer", "Target user not allowed to receive messages": "El usuario objetivo no puede recibir mensajes", "TargetRoom": "Room objetivo", "TargetRoom_Description": "Sala a la que se enviarán los mensajes que son el resultado de la activación de este evento. Solo se permite una sala objetivo, que debe existir ya.", @@ -4907,10 +4913,8 @@ "subscription.callout.capabilitiesDisabled": "Características desactivadas", "subscription.callout.description.limitsExceeded_one": "Su espacio de trabajo ha superado el límite de <1> {{val}} . <3> Administre su suscripción para incrementar los límites.", "subscription.callout.description.limitsExceeded_other": "Su espacio de trabajo ha superado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", - "subscription.callout.description.limitsExceeded_many": "Su espacio de trabajo ha superado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "subscription.callout.description.limitsReached_one": "Su espacio de trabajo ha alcanzado el límite <1> {{val}} . <3> Administre su suscripción para incrementar los límites.", "subscription.callout.description.limitsReached_other": "Su espacio de trabajo ha alcanzado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", - "subscription.callout.description.limitsReached_many": "Su espacio de trabajo ha alcanzado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "subscription.callout.allPremiumCapabilitiesDisabled": "Todas las funciones premium desactivadas", "subscription.callout.activeUsers": "puestos", "subscription.callout.guestUsers": "invitados", @@ -5009,13 +5013,10 @@ "cloud.RegisterWorkspace_Setup_Terms_Privacy": "Acepto los <1>términos y condiciones y la <3>política de privacidad", "Uninstall_grandfathered_app": "¿Desinstalar {{appName}}?", "mentions_counter_one": "{{count}} mención", - "mentions_counter_many": "{{count}} menciones", "mentions_counter_other": "{{count}} menciones", "threads_counter_one": "{{count}} mensaje en hilo sin leer", - "threads_counter_many": "{{count}} mensajes en hilo sin leer", "threads_counter_other": "{{count}} mensajes en hilo sin leer", "unread_messages_counter_one": "{{count}} mensaje sin leer", - "unread_messages_counter_many": "{{count}} mensajes sin leer", "unread_messages_counter_other": "{{count}} mensajes sin leer", "Premium": "Premium", "Enterprise": "Premium", @@ -5089,4 +5090,4 @@ "Unlimited_seats": "Puestos ilimitados", "Unlimited_MACs": "Contactos Activos por Mes (MAC) ilimitados", "Unlimited_seats_MACs": "Puestos y Contactos Activos por Mes (MAC) ilimitados" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/et.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/et.i18n.json index 0afbf48dab3f..52ea2c08027c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/et.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/et.i18n.json @@ -76,4 +76,4 @@ "Type_your_name": "Sisestage oma nimi", "Upload_file_question": "Faili üles laadima?", "User_left": "Kasutaja lahkus" -} +} \ 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 a4a6d7e4e879..f5f5e9be472b 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json @@ -1667,7 +1667,7 @@ "Job_Title": "عنوان شغلی", "Join": "پیوستن", "Join_audio_call": "پیوستن به تماس صوتی", - "Join_call":"پیوستن به تماس.", + "Join_call": "پیوستن به تماس.", "Join_Chat": "عضویت در چت", "Join_default_channels": "پیوستن به کانال های پیشفرض", "Join_the_Community": "پیوستن به جامعه", @@ -2431,6 +2431,7 @@ "Running_Instances": "اجرای نمونههای", "Runtime_Environment": "محیط زیست زمان اجرا", "S_new_messages_since_s": "%s پیام جدید از %s را", + "S_new_messages": "%s پیام جدید", "Same_As_Token_Sent_Via": "همانند \"Token Sent Via\"", "Same_Style_For_Mentions": "همان سبک برای اشاره", "SAML": "SAML", @@ -2950,7 +2951,7 @@ "Video_Conference": "ویدیو کنفرانس", "Video_message": "پیام ویدویی", "Videocall_declined": "تماس ویدیویی رد شد", - "video_livechat_started": "شروع یک تماس ویدیویی." , + "video_livechat_started": "شروع یک تماس ویدیویی.", "View_mode": "شیوه نمایش", "View_All": "مشاهده همه", "View_Logs": "نمایش سیاهههای مربوط", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json index 0af608bed7c7..fbcc6cb3d7f4 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json @@ -681,6 +681,7 @@ "Auto_Load_Images": "Lataa kuvat automaattisesti", "Auto_Selection": "Automaattinen valinta", "Auto_Translate": "Automaattikäännös", + "Calls_in_queue": "{{calls}} puhelu jonossa", "auto-translate": "Automaattinen käännös", "auto-translate_description": "Oikeus käyttää automaattista käännöstyökalua", "Automatic_Translation": "Automaattinen käännös", @@ -1067,7 +1068,6 @@ "Connection_Closed": "Yhteys katkaistu", "Connection_Reset": "Yhteyden nollaus", "Connection_error": "Yhteysvirhe", - "LDAP_Connection_successful": "LDAP-yhteys onnistui", "Connection_failed": "LDAP-yhteys epäonnistui", "Connectivity_Services": "Yhteyspalvelut", "Consulting": "Konsultointi", @@ -2749,6 +2749,7 @@ "LDAP_Connection": "Yhteys", "LDAP_Connection_Authentication": "Todennus", "LDAP_Connection_Encryption": "Salaus", + "LDAP_Connection_successful": "LDAP-yhteys onnistui", "LDAP_Connection_Timeouts": "Aikakatkaisut", "LDAP_UserSearch": "Käyttäjähaku", "LDAP_UserSearch_Filter": "Hakusuodatin", @@ -4176,6 +4177,7 @@ "Running_Instances": "Käynnissä olevat esiintymät", "Runtime_Environment": "Suoritusaikainen ympäristö", "S_new_messages_since_s": "%s uutta viestiä %s jälkeen", + "S_new_messages": "%s uutta viestiä", "Same_As_Token_Sent_Via": "Sama kuin Tunnuksen lähetystapa", "Same_Style_For_Mentions": "Sama tyyli maininnoissa", "SAML": "SAML", @@ -5752,4 +5754,4 @@ "Theme_Appearence": "Teeman ulkoasu", "Enterprise": "Yritys", "UpgradeToGetMore_engagement-dashboard_Title": "Analytics" -} +} \ 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 30c7d6020705..9e16888c5502 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json @@ -585,12 +585,14 @@ "Authentication": "Authentification", "Author": "Auteur", "Author_Information": "Informations sur l'auteur", + "Calls_in_queue_many": "{{count}} appels en file d'attente", "Author_Site": "Site de l'auteur", "Authorization_URL": "URL d'autorisation", "Authorize": "Autoriser", "Auto_Load_Images": "Charger automatiquement les images", "Auto_Selection": "Sélection automatique", "Auto_Translate": "Traduction automatique", + "Calls_in_queue": "{{calls}} appel en file d'attente", "auto-translate": "Traduction automatique", "auto-translate_description": "Autorisation d'utiliser l'outil de traduction automatique", "Automatic_Translation": "Traduction automatique", @@ -710,7 +712,6 @@ "Calls": "Appels", "Calls_in_queue_zero": "La file d'attente est vide", "Calls_in_queue_one": "{{count}} appel en file d'attente", - "Calls_in_queue_many": "{{count}} appels en file d'attente", "Calls_in_queue_other": "{{count}} appels en file d'attente", "Call_declined": "Appel refusé !", "Call_Information": "Informations sur l'appel", @@ -929,7 +930,6 @@ "Connection_Closed": "Connexion fermée", "Connection_Reset": "Connexion réinitialisée", "Connection_error": "Erreur de connexion", - "LDAP_Connection_successful": "Connexion LDAP réussie", "Connection_failed": "Échec de la connexion LDAP", "Connectivity_Services": "Services de connectivité", "Consulting": "Conseil", @@ -2377,6 +2377,7 @@ "Language_Polish": "Polonais", "Language_Portuguese": "Portugais", "Language_Romanian": "Roumain", + "message_counter_many": "{{count}} messages", "Language_Russian": "Russe", "Language_Slovak": "Slovaque", "Language_Slovenian": "Slovène", @@ -2413,6 +2414,7 @@ "LDAP_Connection": "Connexion", "LDAP_Connection_Authentication": "Authentification", "LDAP_Connection_Encryption": "Chiffrement", + "LDAP_Connection_successful": "Connexion LDAP réussie", "LDAP_Connection_Timeouts": "Dépassements de délai", "LDAP_UserSearch": "Recherche utilisateur", "LDAP_UserSearch_Filter": "Filtre de recherche", @@ -2448,6 +2450,7 @@ "LDAP_Background_Sync_Import_New_Users": "Synchronisation en arrière-plan de l'importation de nouveaux utilisateurs", "LDAP_Background_Sync_Import_New_Users_Description": "Importe tous les utilisateurs (en fonction de vos critères de filtrage) qui existent dans LDAP et qui n'existent pas dans Rocket.Chat", "LDAP_Background_Sync_Interval": "Intervalle de synchronisation en arrière-plan", + "meteor_status_reconnect_in_many": "nouvelle tentative dans {{count}} secondes...", "LDAP_Background_Sync_Interval_Description": "Intervalle entre les synchronisations. Exemple : `toutes les 24 heures` ou`le premier jour de la semaine`, plus d'exemples sur [Cron Text Parser](http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Synchronisation en arrière-plan de la mise à jour d'utilisateurs existants", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Synchronise l'avatar, les champs, le nom d'utilisateur, etc. (en fonction de votre configuration) de tous les utilisateurs déjà importés depuis LDAP à chaque **intervalle de synchronisation**", @@ -2842,7 +2845,6 @@ "Message_Code_highlight": "Liste des langues de mise en surbrillance du code", "Message_Code_highlight_Description": "Liste de langues séparées par des virgules (toutes les langues prises en charge sont répertoriées sur [highlight.js](https://github.com/highlightjs/highlight.js/tree/11.6.0#supported-languages)) qui seront utilisées pour mettre en surbrillance des blocs de code", "message_counter_one": "{{count}} message", - "message_counter_many": "{{count}} messages", "message_counter_other": "{{count}} messages", "Message_DateFormat": "Format de date", "Message_DateFormat_Description": "Voir aussi : [Moment.js](http://momentjs.com/docs/#/displaying/format/)", @@ -2936,7 +2938,6 @@ "meteor_status_failed": "La connexion au serveur a échoué", "meteor_status_offline": "Mode hors ligne.", "meteor_status_reconnect_in_one": "nouvelle tentative dans une seconde...", - "meteor_status_reconnect_in_many": "nouvelle tentative dans {{count}} secondes...", "meteor_status_reconnect_in_other": "nouvelle tentative dans {{count}} secondes...", "meteor_status_try_now_offline": "Reconnexion", "meteor_status_try_now_waiting": "Essayer maintenant", @@ -3634,6 +3635,7 @@ "Running_Instances": "Instances en cours d'exécution", "Runtime_Environment": "Environnement d'exécution", "S_new_messages_since_s": "%s nouveaux messages depuis %s", + "S_new_messages": "%s nouveaux messages", "Same_As_Token_Sent_Via": "Identique à \"Jeton envoyé via\"", "Same_Style_For_Mentions": "Même style pour les mentions", "SAML": "SAML", @@ -4887,4 +4889,4 @@ "Enterprise": "Entreprise", "UpgradeToGetMore_engagement-dashboard_Title": "Analyses", "UpgradeToGetMore_auditing_Title": "Audit des messages" -} +} \ 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 0f17d3f548c2..b231a2c2ce9a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json @@ -1149,6 +1149,7 @@ "Rooms": "חדרים", "Running_Instances": "הפעלת מופעים", "S_new_messages_since_s": "%s הודעות חדשות מאז %s", + "S_new_messages": "הודעות חדשות %s", "SAML": "SAML", "SAML_Custom_Cert": "תעודה מותאמת אישית", "SAML_Custom_Entry_point": "Entry Point מותאם אישית", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json index e33f8dd815da..41f1d1c5ef1a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json @@ -2254,6 +2254,7 @@ "Running_Instances": "Pokrenuta instanca", "Runtime_Environment": "Runtime Environment", "S_new_messages_since_s": "%s novih poruka od %s", + "S_new_messages": "%s novih poruka", "Same_As_Token_Sent_Via": "Isto kao i \"Token poslan putem\"", "Same_Style_For_Mentions": "Isti stil za spominjanje", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json index e161c6e7a92a..b03d85857fa3 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json @@ -652,6 +652,7 @@ "Auto_Load_Images": "Képek automatikus betöltése", "Auto_Selection": "Automatikus kiválasztás", "Auto_Translate": "Automatikus fordítás", + "Calls_in_queue": "{{calls}} hívás a várólistán", "auto-translate": "Automatikus fordítás", "auto-translate_description": "Jogosultság az automatikus fordítóeszköz használatához", "Automatic_Translation": "Automatikus fordítás", @@ -1028,7 +1029,6 @@ "Connection_Closed": "Kapcsolat lezárva", "Connection_Reset": "Kapcsolat visszaállítva", "Connection_error": "Kapcsolódási hiba", - "LDAP_Connection_successful": "Az LDAP-kapcsolat sikeres", "Connection_failed": "Az LDAP-kapcsolat sikertelen", "Connectivity_Services": "Kapcsolódási szolgáltatások", "Consulting": "Tanácsadás", @@ -2642,6 +2642,7 @@ "LDAP_Connection": "Kapcsolat", "LDAP_Connection_Authentication": "Hitelesítés", "LDAP_Connection_Encryption": "Titkosítás", + "LDAP_Connection_successful": "Az LDAP-kapcsolat sikeres", "LDAP_Connection_Timeouts": "Időkorlátok", "LDAP_UserSearch": "Felhasználó keresése", "LDAP_UserSearch_Filter": "Keresési szűrő", @@ -4017,6 +4018,7 @@ "Running_Instances": "Futó példányok", "Runtime_Environment": "Futtatókörnyezet", "S_new_messages_since_s": "%s új üzenet %s óta", + "S_new_messages": "%s új üzenet", "Same_As_Token_Sent_Via": "Ugyanaz, mint a „Token elküldve ezzel”", "Same_Style_For_Mentions": "Ugyanaz a stílus az említéseknél", "SAML": "SAML", @@ -4141,7 +4143,7 @@ "Search_Provider": "Keresési szolgáltató", "Search_Rooms": "Szobák keresése", "Search_Users": "Felhasználók keresése", - "Seats_Available": "{{seatsLeft}} hely érhető el", + "Seats_Available": "{{seatsLeft, number}}hely érhető el", "Seats_usage": "Helyek használata", "seconds": "másodperc", "Secret_token": "Titkos token", @@ -5423,4 +5425,4 @@ "Enterprise": "Vállalati", "UpgradeToGetMore_engagement-dashboard_Title": "Analitika", "UpgradeToGetMore_auditing_Title": "Üzenet ellenőrzés" -} +} \ 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 541cfa361fe6..56d527dbb0f6 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json @@ -2128,6 +2128,7 @@ "Running_Instances": "menjalankan Contoh", "Runtime_Environment": "Runtime Environment", "S_new_messages_since_s": "%s pesan baru sejak %s", + "S_new_messages": "pesan baru%s", "Same_As_Token_Sent_Via": "Sama seperti \"Token Sent Via\"", "Same_Style_For_Mentions": "Gaya yang sama untuk disebutkan", "SAML": "SAML", @@ -2768,4 +2769,4 @@ "registration.component.form.sendConfirmationEmail": "Kirim email konfirmasi", "Enterprise": "Perusahaan", "UpgradeToGetMore_engagement-dashboard_Title": "Analytics" -} +} \ 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 89e55e3062f6..d041b9c099fa 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json @@ -6,8 +6,8 @@ "__count__message_pruned_many": "{{count}} messaggi eliminati", "__count__message_pruned_other": "{{count}} messaggi eliminati", "__usersCount__member_joined_one": "+ {{count}} membro si è unito", - "__usersCount__member_joined_many": "+ {{count}} membri si sono uniti", "__usersCount__member_joined_other": "+ {{count}} membri si sono uniti", + "__usersCount__member_joined_many": "+ {{count}} membri si sono uniti", "__usersCount__people_will_be_invited": "{{usersCount}} persone saranno invitate", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} non è più {{role}}, da {{user_by}}", "__username__was_set__role__by__user_by_": "A {{username}} è stato assegnato il ruolo di {{role}} da {{user_by}}", @@ -1518,6 +1518,7 @@ "LDAP_Background_Sync_Import_New_Users": "Sincronizzazione in background Importa nuovi utenti", "LDAP_Background_Sync_Import_New_Users_Description": "Importerà tutti gli utenti (in base ai criteri del filtro) esistenti in LDAP e non esiste in Rocket.Chat", "LDAP_Background_Sync_Interval": "Intervallo sincronizzazione sfondo", + "meteor_status_reconnect_in_many": "riprovo tra {{count}} secondi...", "LDAP_Background_Sync_Interval_Description": "L'intervallo tra le sincronizzazioni. Esempio \"ogni 24 ore\" o \"il primo giorno della settimana\", altri esempi su [Cron Text Parser] (http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Aggiornamento in background Aggiorna gli utenti esistenti", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Sincronizza l'avatar, i campi, il nome utente, ecc. (In base alla configurazione) di tutti gli utenti già importati da LDAP su ogni ** Intervallo di sincronizzazione **", @@ -1775,7 +1776,6 @@ "meteor_status_failed": "Impossibile connettersi al server", "meteor_status_offline": "Modalità non in linea.", "meteor_status_reconnect_in_one": "riprovo tra pochi secondi...", - "meteor_status_reconnect_in_many": "riprovo tra {{count}} secondi...", "meteor_status_reconnect_in_other": "riprovo tra {{count}} secondi...", "meteor_status_try_now_offline": "Riprova", "meteor_status_try_now_waiting": "Connetti ora", @@ -2201,6 +2201,7 @@ "Running_Instances": "Istanze in esecuzione", "Runtime_Environment": "Ambiente di runtime", "S_new_messages_since_s": "%s nuovi messaggi dal %s", + "S_new_messages": "%s nuovi messaggi", "Same_As_Token_Sent_Via": "Come \"Token Sent Via\"", "Same_Style_For_Mentions": "Lo stesso stile per le citazioni", "SAML": "SAML", @@ -2865,4 +2866,4 @@ "registration.component.form.sendConfirmationEmail": "Invia email di conferma", "Enterprise": "impresa", "UpgradeToGetMore_engagement-dashboard_Title": "Analytics" -} +} \ 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 efdfcced7757..b5a67a7cc13b 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json @@ -588,6 +588,7 @@ "Auto_Load_Images": "画像の自動ロード", "Auto_Selection": "自動選択", "Auto_Translate": "自動翻訳", + "Calls_in_queue": "キュー内の{{count}}通話", "auto-translate": "自動翻訳", "auto-translate_description": "自動翻訳ツールを使用する権限", "Automatic_Translation": "自動翻訳", @@ -701,7 +702,6 @@ "Call": "通話", "Calling": "通話中", "Call_Center": "コールセンター", - "Calls_in_queue": "キュー内の{{count}}通話", "Call_declined": "通話が拒否されました!", "Call_Information": "通話情報", "Call_provider": "通話プロバイダー", @@ -915,7 +915,6 @@ "Connection_Closed": "接続が閉じられました", "Connection_Reset": "接続のリセット", "Connection_error": "接続エラー", - "LDAP_Connection_successful": "LDAP接続に成功しました", "Connection_failed": "LDAP接続が失敗しました", "Connectivity_Services": "接続サービス", "Consulting": "コンサルティング", @@ -2389,6 +2388,7 @@ "LDAP_Connection": "接続", "LDAP_Connection_Authentication": "認証", "LDAP_Connection_Encryption": "暗号化", + "LDAP_Connection_successful": "LDAP接続に成功しました", "LDAP_Connection_Timeouts": "タイムアウト", "LDAP_UserSearch": "ユーザー検索", "LDAP_UserSearch_Filter": "検索フィルター", @@ -3597,6 +3597,7 @@ "Running_Instances": "実行中のインスタンス", "Runtime_Environment": "ランタイム環境", "S_new_messages_since_s": "%s以降の新規メッセージ:%s件", + "S_new_messages": "%s件の新しいメッセージ", "Same_As_Token_Sent_Via": "「トークン送信経路」と同じ", "Same_Style_For_Mentions": "メンションと同じスタイル", "SAML": "SAML", @@ -4832,4 +4833,4 @@ "Enterprise": "エンタープライズ", "UpgradeToGetMore_engagement-dashboard_Title": "分析", "UpgradeToGetMore_auditing_Title": "メッセージ監査" -} +} \ 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 4bfadce34e0b..5b4b6ae47ef1 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json @@ -2833,6 +2833,7 @@ "Running_Instances": "მიმდინარე პროცესები", "Runtime_Environment": "მიმდინარე გარემო", "S_new_messages_since_s": "%s ახალი მესიჯი %s -დან", + "S_new_messages": "%s ახალი შეტყობინებები", "Same_As_Token_Sent_Via": "იგივეა რაც \"ტოკენი გაგზავნილია\" __-ით", "Same_Style_For_Mentions": "იგივე სტილი ხსენებებისთვის", "SAML_Allowed_Clock_Drift": "პირადობის წარმომდგენისგან დროის დასაშვები ცდომილება", @@ -3669,4 +3670,4 @@ "onboarding.form.registerOfflineForm.title": "ხელით დარეგისტრირება", "UpgradeToGetMore_engagement-dashboard_Title": "ანალიტიკა", "UpgradeToGetMore_auditing_Title": "შეტყობინებების შემოწმება" -} +} \ 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 b58c8c5c5dc1..8d50ef2e44fd 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json @@ -2437,6 +2437,7 @@ "Running_Instances": "រត់", "Runtime_Environment": "បរិស្ថានពេលរត់", "S_new_messages_since_s": "%s សារថ្មីចាប់តាំងពី %s", + "S_new_messages": "សារថ្មី %s បាន", "Same_As_Token_Sent_Via": "ដូចគ្នានឹង \"លេខសំងាត់បានផ្ញើតាម\"", "Same_Style_For_Mentions": "រចនាប័ទ្មដូចគ្នាសម្រាប់ការលើកឡើង", "SAML": "ប្រើ SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json index b9a23f3bc10e..dab99bb28f1e 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json @@ -795,7 +795,6 @@ "Connect": "연결", "Connection_Closed": "연결이 닫혔습니다.", "Connection_Reset": "연결 재설정", - "LDAP_Connection_successful": "LDAP 연결 성공", "Connectivity_Services": "연결 서비스", "Consulting": "컨설팅", "Contact": "연락처", @@ -2113,6 +2112,7 @@ "Layout_Sidenav_Footer_Dark_description": "바닥글 크기는 260x70 입니다.", "Layout_Terms_of_Service": "이용약관", "LDAP": "LDAP", + "LDAP_Connection_successful": "LDAP 연결 성공", "LDAP_Advanced_Sync": "고급 동기화", "LDAP_Authentication": "사용", "LDAP_Authentication_Password": "비밀번호", @@ -3091,6 +3091,7 @@ "Running_Instances": "실행 중인 인스턴스", "Runtime_Environment": "실행 환경", "S_new_messages_since_s": "%s 개의 새 메시지 (%s 이후)", + "S_new_messages": "%의 새 메시지", "Same_As_Token_Sent_Via": "\"Token Sent Via\"와 동일합니다.", "Same_Style_For_Mentions": "언급과 같은 스타일", "SAML": "SAML", @@ -4025,4 +4026,4 @@ "Enterprise": "기업", "UpgradeToGetMore_engagement-dashboard_Title": "분석(에널리틱스)", "UpgradeToGetMore_auditing_Title": "메시지 감사" -} +} \ 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 856732eca0be..f0512de9c5a1 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json @@ -2114,6 +2114,7 @@ "Running_Instances": "bi bez Instances", "Runtime_Environment": "Hawirdora hawirdorê", "S_new_messages_since_s": "%s mesajên nû ji ber ku %s", + "S_new_messages": "%s new messages", "Same_As_Token_Sent_Via": "Wekî \"Token Sent Via\"", "Same_Style_For_Mentions": "Ji bo ramanên heman awayî", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json index 34f308d2ab50..09e0f2e7a6de 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json @@ -2158,6 +2158,7 @@ "Running_Instances": "ແລ່ນກໍລະນີ", "Runtime_Environment": "Runtime Environment", "S_new_messages_since_s": "%s ຂໍ້ຄວາມໃຫມ່ນັບຕັ້ງແ​​ຕ່ %s", + "S_new_messages": "%s ຂໍ້ຄວາມໃຫມ່", "Same_As_Token_Sent_Via": "ຄືກັນກັບ \"Token Sent ຜ່ານ\"", "Same_Style_For_Mentions": "ແບບດຽວກັນສໍາລັບຄໍາແນະນໍາ", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json index 89226d5019d8..764fa95787a5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json @@ -2176,6 +2176,7 @@ "Running_Instances": "Egzempliorių bėgimas", "Runtime_Environment": "Runtime Environment", "S_new_messages_since_s": "%s nauji pranešimai nuo%s", + "S_new_messages": "%s nauji pranešimai", "Same_As_Token_Sent_Via": "Tas pats, kaip \"Token Sent Via\"", "Same_Style_For_Mentions": "Tas pats pamokų stilius", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json index ab594b7d2f69..08277dd8c44f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json @@ -2131,6 +2131,7 @@ "Running_Instances": "Aktīvās instances", "Runtime_Environment": "Runtime vide", "S_new_messages_since_s": "%s jauni ziņojumi kopš %s", + "S_new_messages": "%s jaunie ziņojumi", "Same_As_Token_Sent_Via": "Tā pats, kā \"Žetons nosūtīts izmantojot\"", "Same_Style_For_Mentions": "Tas pats stils pieminējumiem", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json index 19da31a1b2e5..76bcf7616855 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json @@ -2116,6 +2116,7 @@ "Running_Instances": "Ажиллаж байгаа тохиолдлууд", "Runtime_Environment": "Ажиллах орчин", "S_new_messages_since_s": "%s-с хойших %s шинэ зурвасууд", + "S_new_messages": "%s шинэ зурвасууд", "Same_As_Token_Sent_Via": "\"Токенаар дамжуулан илгээсэн\" адил", "Same_Style_For_Mentions": "Сэтгэгдлийн хувьд ижил хэв маяг", "SAML": "SAML", 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 8b20b0968e7e..00245940ea42 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json @@ -2127,6 +2127,7 @@ "Running_Instances": "Running Kejadian", "Runtime_Environment": "Persekitaran Masa Runtime", "S_new_messages_since_s": "%s mesej baru sejak %s", + "S_new_messages": "%s mesej baru", "Same_As_Token_Sent_Via": "Sama seperti \"Token Dihantar Melalui\"", "Same_Style_For_Mentions": "Gaya yang sama untuk disebutkan", "SAML": "SAML", @@ -2764,4 +2765,4 @@ "registration.component.form.sendConfirmationEmail": "Hantar e-mel pengesahan", "Enterprise": "Enterprise", "UpgradeToGetMore_engagement-dashboard_Title": "Analisis" -} +} \ 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 c118770e509d..daf3e2aae7f2 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json @@ -591,6 +591,7 @@ "Auto_Load_Images": "Afbeeldingen automatisch laden", "Auto_Selection": "Automatische selectie", "Auto_Translate": "Automatisch vertalen", + "Calls_in_queue": "{{calls}} oproep in de wachtrij", "auto-translate": "Automatisch vertalen", "auto-translate_description": "Toestemming om de automatische vertaaltool te gebruiken", "Automatic_Translation": "Automatische vertaling", @@ -923,7 +924,6 @@ "Connection_Closed": "Verbinding gesloten", "Connection_Reset": "Verbinding gereset", "Connection_error": "Verbindingsfout", - "LDAP_Connection_successful": "LDAP-verbinding geslaagd", "Connection_failed": "LDAP-verbinding mislukt", "Connectivity_Services": "Connectiviteitsdiensten", "Consulting": "Consultant", @@ -2406,6 +2406,7 @@ "LDAP_Connection": "Verbinding", "LDAP_Connection_Authentication": "Authenticatie", "LDAP_Connection_Encryption": "Encryptie", + "LDAP_Connection_successful": "LDAP-verbinding geslaagd", "LDAP_Connection_Timeouts": "Time-outs", "LDAP_UserSearch": "Gebruikers zoeken", "LDAP_UserSearch_Filter": "Zoekfilter", @@ -3624,6 +3625,7 @@ "Running_Instances": "Instanties", "Runtime_Environment": "Runtime-omgeving", "S_new_messages_since_s": "%s nieuwe berichten sinds %s", + "S_new_messages": "%s nieuwe berichten", "Same_As_Token_Sent_Via": "Hetzelfde als \"Token verzonden via\"", "Same_Style_For_Mentions": "Dezelfde stijl voor vermeldingen", "SAML": "SAML", @@ -4872,4 +4874,4 @@ "Enterprise": "Onderneming", "UpgradeToGetMore_engagement-dashboard_Title": "Analytics", "UpgradeToGetMore_auditing_Title": "Bericht auditing" -} +} \ 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 523be5e4b593..1bdb45345c25 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json @@ -3,6 +3,8 @@ "__agents__agents_and__count__conversations__period__": "{{agents}} agenter og {{count}} samtaler, {{period}}", "__count__empty_rooms_will_be_removed_automatically": "{{count}} tomme rom vil bli fjernet automatisk.", "__count__empty_rooms_will_be_removed_automatically__rooms__": "{{count}} tomme rom vil bli fjernet automatisk:
{{rooms}}.", + "__count__message_pruned_one": "{{count}} melding beskjært", + "__count__message_pruned_other": "{{count}} meldinger beskjært", "__count__conversations__period__": "{{count}} samtaler, {{period}}", "__count__tags__and__count__conversations__period__": "{{count}}-tagger og {{conversations}}-samtaler, {{period}}", "__departments__departments_and__count__conversations__period__": "{{departments}} avdelinger og {{count}} samtaler, {{period}}", @@ -20,6 +22,7 @@ "This_room_encryption_has_been_disabled_by__username_": "Dette rommets kryptering har blitt deaktivert av {{username}}", "Third_party_login": "Tredjeparts innlogging", "Enabled_E2E_Encryption_for_this_room": "aktivert E2E-kryptering for dette rommet", + "Enable_business_hours": "Aktiver åpningstider", "disabled": "deaktivert", "Disabled_E2E_Encryption_for_this_room": "deaktivert E2E-kryptering for dette rommet", "@username": "@brukernavn", @@ -97,9 +100,11 @@ "Accounts_CustomFieldsToShowInUserInfo": "Egendefinerte felt å vise i brukerinformasjon", "Accounts_Default_User_Preferences": "Standard brukerinnstillinger", "Accounts_Default_User_Preferences_audioNotifications": "Lydvarsler Standardvarsel", + "Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Tillat bruker å velge \"Også send melding i kanal\"-adferden", "Accounts_Default_User_Preferences_desktopNotifications": "Standardvarsling for stasjonære meldinger", "Accounts_Default_User_Preferences_pushNotifications": "Standardvarsler for mobilvarsler", "Accounts_Default_User_Preferences_not_available": "Kunne ikke hente brukerinnstillinger fordi de ennå ikke er konfigurert av brukeren", + "Accounts_Default_User_Preferences_showThreadsInMainChannel_Description": "Når aktivert, vil alle svar under en tråd også vises direkte i hovedrommet. Når den er deaktivert, vil svar i tråd vises basert på avsenderens valg.", "Accounts_DefaultUsernamePrefixSuggestion": "Standard brukernavn Prefix Suggestion", "Accounts_denyUnverifiedEmail": "Avvis unverifisert e-post", "Accounts_Directory_DefaultView": "Standard katalogoppføring", @@ -148,6 +153,7 @@ "Accounts_OAuth_Custom_Name_Field": "Felt for navn", "Accounts_OAuth_Custom_Roles_Claim": "Roller / Grupper feltnavn", "Accounts_OAuth_Custom_Roles_To_Sync": "Roller som skal synkroniseres", + "Accounts_OAuth_Custom_Roles_To_Sync_Description": "OAuth roller som blir synkronisert på bruker innlogging og opprettelse (kommaseparert)", "Accounts_OAuth_Custom_Scope": "omfang", "Accounts_OAuth_Custom_Secret": "Hemmelig", "Accounts_OAuth_Custom_Show_Button_On_Login_Page": "Vis knapp på innloggingsside", @@ -263,6 +269,7 @@ "Accounts_SetDefaultAvatar_Description": "Trenger å bestemme standard avatar basert på OAuth-konto eller Gravatar", "Accounts_ShowFormLogin": "Vis skjemabasert pålogging", "Accounts_TwoFactorAuthentication_By_TOTP_Enabled": "Aktiver tofaktorautentisering via TOTP", + "Accounts_TwoFactorAuthentication_By_TOTP_Enabled_Description": "Brukere kan konfigurere sin tofaktorautentisering ved å bruke en hvilken som helst TOTP-app, som Google Authenticator eller Authy.", "Accounts_TwoFactorAuthentication_By_Email_Auto_Opt_In": "Automatisk aktiver tofaktor via e-post for nye brukere", "Accounts_TwoFactorAuthentication_By_Email_Auto_Opt_In_Description": "Nye brukere vil ha tofaktorautentisering via e-post aktivert som standard. De vil kunne deaktivere det på profilsiden.", "Accounts_TwoFactorAuthentication_By_Email_Code_Expiration": "Tid i sekunder før koden sendt i e-post utløper", @@ -271,10 +278,13 @@ "Accounts_TwoFactorAuthentication_Enabled": "Aktiver to faktorautentiseringer", "Accounts_TwoFactorAuthentication_Enabled_Description": "Brukere kan konfigurere tofaktorautentisering ved å bruke hvilken som helst TOTP-app, for eksempel Google Authenticator eller Authy", "Accounts_TwoFactorAuthentication_Enforce_Password_Fallback": "Håndhev passordreservering", + "Accounts_TwoFactorAuthentication_Enforce_Password_Fallback_Description": "Brukere vil bli pålagt å skrive inn passordet sitt, for viktige handlinger, hvis ingen annen tofaktorautentiseringsmetode er aktivert for den brukeren og et passord er satt for vedkommende.", "Accounts_TwoFactorAuthentication_MaxDelta": "Maksimal Delta", "Accounts_TwoFactorAuthentication_MaxDelta_Description": "Maksimum Delta bestemmer hvor mange tokens er gyldige til enhver tid. Tokene genereres hvert 30. sekund, og gjelder for (30 * Maks. Delta) sekunder. \nEksempel: Med et maksimalt Delta-sett på 10, kan hver token brukes opptil 300 sekunder før eller etter tidsstempel. Dette er nyttig når klientens klokke ikke er riktig synkronisert med serveren.", "Accounts_TwoFactorAuthentication_RememberFor": "Husk tofaktor i (sekunder)", "Accounts_TwoFactorAuthentication_RememberFor_Description": "Ikke be om ny tofaktorautorisasjonskode hvis en allerede har blitt gitt før på dette tidsintervallet.", + "Accounts_TwoFactorAuthentication_Max_Invalid_Email_Code_Attempts": "Maks antall ugyldig engangskoder fra e-post tillatt", + "Accounts_TwoFactorAuthentication_Max_Invalid_Email_Code_Attempts_Description": "Systemet tillater et maksimalt antall ugyldige engangskoder fra e-post, hvoretter en ny kode automatisk genereres. Vi anbefaler på det sterkeste å bruke denne innstillingen sammen med 'Blokkér mislykkede påloggingsforsøk etter brukernavn'.", "Accounts_UseDefaultBlockedDomainsList": "Bruk standard blokkert domener liste", "Accounts_UseDNSDomainCheck": "Bruk DNS Domain Check", "API_EmbedDisabledFor": "Deaktiver embed for brukere", @@ -321,6 +331,7 @@ "add-team-channel_description": "Tillatelse til å legge til en kanal i et team", "add-team-member": "Legg til et teammedlem", "add-team-member_description": "Tillatelse til å legge til medlemmer i et team", + "Add_them": "Legg dem til", "add-user": "Legg til bruker", "add-user_description": "Tillatelse til å legge til nye brukere på serveren via brukerskjermbildet", "add-user-to-any-c-room": "Legg til bruker til hvilken som helst offentlig kanal", @@ -329,23 +340,42 @@ "add-user-to-any-p-room_description": "Tillatelse til å legge til en bruker til enhver privat kanal", "add-user-to-joined-room": "Legg til bruker til noen tilknyttet kanal", "add-user-to-joined-room_description": "Tillatelse til å legge til en bruker i en tilkoblet kanal", + "added__roomName__to_team": "lagt til #{{roomName}} i dette teamet", + "Added__username__to_team": "lagt til @{{user_added}} til dette teamet", + "added__roomName__to_this_team": "la #{{roomName}} til dette teamet", "Apps_Framework_enabled": "Aktiver App Framework", + "Added__username__to_this_team": "la til @{{user_added}} til dette teamet", "Adding_OAuth_Services": "Legge til OAuth-tjenester", "Adding_permission": "Legger til rettigheter", + "Adjustable_layout": "Justerbart oppsett", "Adding_user": "Legger til bruker", "Additional_emails": "Ekstra e-postadresser", "Additional_Feedback": "Ekstra tilbakemelding", "additional_integrations_Bots": "Hvis du leter etter hvordan du integrerer din egen bot, så se ikke lenger enn vår Hubot-adapter. https://github.com/RocketChat/hubot-rocketchat", "Admin_disabled_encryption": "Din administrator har ikke aktivert ende-til-ende kryptering.", "Admin_Info": "Admin Info", + "admin-no-active-video-conf-provider": "**Konferansesamtale ikke aktivert**: Konfigurer konferansesamtaler for å gjøre det tilgjengelig på dette arbeidsområdet.", + "admin-video-conf-provider-not-configured": "**Konferansesamtale ikke aktivert**: Konfigurer konferansesamtaler for å gjøre det tilgjengelig på dette arbeidsområdet.", + "admin-no-videoconf-provider-app": "**Konferansesamtale ikke aktivert**: Konferansesamtaler-apper er tilgjengelige på Rocket.Chat-markedet.", "Administration": "Administrasjon", + "Address": "Adresse", + "Adjustable_font_size": "Justerbar skriftstørrelse", + "Adjustable_font_size_description": "Designet for de som foretrekker større eller mindre tekst for bedre lesbarhet. Denne fleksibiliteten fremmer inkludering ved å gi brukerne mulighet til å skreddersy brukergrensesnittet til deres spesifikke behov.", "Adult_images_are_not_allowed": "Voksenbilder er ikke tillatt", + "Aerospace_and_Defense": "Luftfart og forsvar", "After_OAuth2_authentication_users_will_be_redirected_to_this_URL": "Etter OAuth2-godkjenning, blir brukerne omdirigert til denne nettadressen", + "After_guest_registration": "Etter gjesteregistrering", "Agent": "Agent", "Agent_added": "Lagt til agent", "Agent_Info": "Agentinfo", + "Agent_messages": "Agentbeskjed", + "Agent_Name": "Agentnavn", + "Agent_Name_Placeholder": "Vennligst skriv inn et agentnavn...", "Agent_removed": "Fjernet agent", + "Agent_deactivated": "Agenten ble deaktivert", + "Agent_Without_Extensions": "Agent uten utvidelser", "Agents": "Agenter", + "Agree": "Godta", "Alerts": "varsler", "Alias": "Alias", "Alias_Format": "Aliasformat", @@ -357,8 +387,10 @@ "AutoLinker_Phone_Description": "Automatisk koblet til telefonnumre. f.eks `(123) 456-7890`", "All": "Alle", "AutoLinker_StripPrefix": "AutoLinker Strip Prefix", + "All_Apps": "Alle apper", "AutoLinker_StripPrefix_Description": "Kortvisning. f.eks https://rocket.chat => rocket.chat", "All_added_tokens_will_be_required_by_the_user": "Alle tilsatte tokens vil bli pålagt av brukeren", + "All_categories": "Alle Kategorier", "AutoLinker_Urls_Scheme": "AutoLinker Scheme: // URLs", "All_channels": "Alle kanaler", "AutoLinker_Urls_TLD": "AutoLinker TLD URLs", @@ -396,16 +428,22 @@ "Analytics_features_users_Description": "Sporer tilpassede hendelser relatert til handlinger relatert til brukere (passord tilbakestilt ganger, profil bilde endring, etc).", "Analytics_Google": "Google Analytics", "Analytics_Google_id": "Sporings-ID", + "Analytics_page_briefing_first_paragraph": "Rocket.Chat samler inn anonyme brukserdata, som tjeneste. og tidsbruk, for å forbedre produktet for alle.", + "Analytics_page_briefing_second_paragraph": "Vi beskytter ditt personvern ved å aldri samle inn personlige eller sensitive data. Denne delen viser hva som samles inn, og tydeliggjør vår forpliktelse til åpenhet og tillit.", + "Analyze_practical_usage": "Analysere praktisk bruksstatistikk om brukere, meldinger og kanaler", "and": "og", "And_more": "Og {{length}} mer", "Animals_and_Nature": "Dyr og natur", "Announcement": "Kunngjøring", + "Anonymous": "Anonym", + "Answer_call": "Svar anrop", "API": "API", "API_Add_Personal_Access_Token": "Legg til ny personlig Access Token", "API_Allow_Infinite_Count": "Tillat å få alt", "API_Allow_Infinite_Count_Description": "Bør samtaler til REST API få lov til å returnere alt i en samtale?", "API_Analytics": "Analytics", "API_CORS_Origin": "CORS Origin", + "API_Apply_permission_view-outside-room_on_users-list": "Tildel tillatelsen `view-outside-room` til api `users.list`", "API_Default_Count": "Standard Count", "API_Default_Count_Description": "Standardtallet for REST API-resultater oppstår hvis forbrukeren ikke oppgav noen.", "API_Drupal_URL": "Drupal Server URL", @@ -421,12 +459,15 @@ "API_Enable_CORS": "Aktiver CORS", "API_Enable_Direct_Message_History_EndPoint": "Aktiver sluttpunkt for direkte meldingshistorikk", "API_Enable_Direct_Message_History_EndPoint_Description": "Dette gjør det mulig for `/ api / v1 / im.history.others` som lar visning av direkte meldinger sendt av andre brukere som den som ringer ikke er en del av.", + "API_Enable_Personal_Access_Tokens": "Aktiver personlige adgangstokener til REST APIet", + "API_Enable_Personal_Access_Tokens_Description": "Aktiver personlige adgangstokener for bruk med REST APIet", "API_Enable_Rate_Limiter": "Aktiver Rate Limit", "API_Enable_Rate_Limiter_Dev": "Aktiver Rate Limit i utvikling", "API_Enable_Rate_Limiter_Dev_Description": "Bør begrense antallet kall til endepunktene i utviklingsmiljøet?", "API_Enable_Rate_Limiter_Limit_Calls_Default": "Standard antall kall til rate-limiter", "API_Enable_Rate_Limiter_Limit_Calls_Default_Description": "Tillatt antall kall innenfor hvert tidsrom for hvert endepunkt i REST API-et.", "API_Enable_Rate_Limiter_Limit_Time_Default": "Standard tidsgrense for rate-limiter (i ms)", + "API_Enable_Rate_Limiter_Limit_Time_Default_Description": "Standard tidsavbrudd for å begrense antall kall til hvert endepunkt i REST APIet (i ms)", "API_Enable_Shields": "Aktiver skjold", "API_Enable_Shields_Description": "Aktiver skjold som er tilgjengelige på `/ api / v1 / shield.svg`", "API_GitHub_Enterprise_URL": "Server URL", @@ -439,17 +480,21 @@ "API_Personal_Access_Tokens_Regenerate_Modal": "Hvis du har mistet eller glemt tokenet ditt kan du regenerere det, men husk at alle applikasjoner som bruker dette tokenet må oppdateres", "API_Personal_Access_Tokens_Remove_Modal": "Er du sikker på at du vil fjerne denne access-tokenen?", "API_Personal_Access_Tokens_To_REST_API": "Personlig access token til REST API", + "API_Rate_Limiter": "API-kallfrekvensbegrensing ", "API_Shield_Types": "Skjoldtyper", "API_Shield_Types_Description": "Typer skjold for å aktivere som en kommaseparert liste, velg fra `online`,` kanal` eller `*` for alle", "Apps_Framework_Development_Mode": "Aktiver utviklermodus", "API_Token": "API Token", + "Apps_Framework_Development_Mode_Description": "Utviklingsmodus tillater installasjon av apper som ikke er fra Rocket.Chats markedsplass.", "API_Tokenpass_URL": "Tokenpass Server URL", "API_Tokenpass_URL_Description": "Eksempel: `https://domain.com` (unntatt trailing slash)", "API_Upper_Count_Limit": "Maks. Rekordbeløp", "API_Upper_Count_Limit_Description": "Hva er det maksimale antall poster som REST API skal returnere (når det ikke er ubegrenset)?", + "API_Use_REST_For_DDP_Calls": "Bruk REST i stedet for websocket for Meteor-kall", "API_User_Limit": "Brukergrense for å legge til alle brukere til kanal", "API_Wordpress_URL": "WordPress URL", "api-bypass-rate-limit": "Forbigå rate-limit for REST API", + "api-bypass-rate-limit_description": "Tillatelse til å kalle APIet uten kallfrekvensbegrensning ", "Apiai_Key": "Api.ai Key", "Apiai_Language": "Api.ai Språk", "APIs": "APIer", @@ -494,11 +539,93 @@ "Apps_Count_Enabled_other": "{{count}} apper aktivert", "Private_Apps_Count_Enabled_one": "{{count}} privat app aktivert", "Private_Apps_Count_Enabled_other": "{{count}} private apper aktivert", + "Apps_Count_Enabled_tooltip": "Community-arbeidsområder kan aktivere opptil {{number}} {{context}} apper", + "Apps_disabled_when_Premium_trial_ended": "Apper som ble deaktivert da Premium-prøveperioden avsluttet", + "Apps_disabled_when_Premium_trial_ended_description": "Community-arbeidsområder kan ha opptil 5 markedsplassapper og 3 private apper aktivert. Be arbeidsområdeadministratoren din om å reaktivere apper.", + "Apps_disabled_when_Premium_trial_ended_description_admin": "Community-arbeidsområder kan ha opptil 5 markedsplassapper og 3 private apper aktivert. Aktiver appene du trenger på nytt.", "Apps_Engine_Version": "Versjon av Apps Engine", + "Apps_Error_private_app_install_disabled": "Installasjon og oppdateringer av private apper er deaktivert for dette arbeidsområdet", + "Apps_Essential_Alert": "Denne appen er viktig for følgende hendelser:", + "Apps_Framework_Source_Package_Storage_Type_Description": "Velg hvor alle appenes kildekode skal lagres. Apper kan ha flere megabyte i størrelse hver.", + "Apps_Framework_Source_Package_Storage_Type_Alert": "Hvis du endrer hvor appene er lagret, kan det føre til ustabilitet i apper som allerede er installert", + "Apps_Framework_Source_Package_Storage_FileSystem_Path": "Katalog for lagring av app-kildepakke", + "Apps_Framework_Source_Package_Storage_FileSystem_Alert": "Sørg for at den valgte katalogen eksisterer og at Rocket.Chat har tilgang til den (f.eks. tillatelse til å lese/skrive)", "Apps_Game_Center": "Spillsenter", "Apps_Game_Center_Back": "Tilbake til spillsenteret", "Apps_Game_Center_Invite_Friends": "Inviter vennene dine til å bli med", + "Apps_Game_Center_Play_Game_Together": "@here La oss spille {{name}} sammen!", + "Apps_Interface_IPostExternalComponentClosed": "Hendelse som skjer etter at en ekstern komponent er lukket", + "Apps_Interface_IPostExternalComponentOpened": "Hendelse som skjer etter at en ekstern komponent er åpnet", + "Apps_Interface_IPostMessageDeleted": "Hendelse som skjer etter at en melding er slettet", + "Apps_Interface_IPostMessageSent": "Hendelse som skjer etter at en melding er sendt", + "Apps_Interface_IPostMessageUpdated": "Hendelse som skjer etter at en melding er oppdatert", + "Apps_Interface_IPostRoomCreate": "Hendelse som skjer etter at et rom er opprettet", + "Apps_Interface_IPostRoomDeleted": "Hendelse som skjer etter at et rom er slettet", + "Apps_Interface_IPostRoomUserJoined": "Hendelse som skjer etter at en bruker blir med i et rom (privat gruppe, offentlig kanal)", + "Apps_Interface_IPreMessageDeletePrevent": "Hendelse som skjer før en melding slettes", + "Apps_Interface_IPreMessageSentExtend": "Hendelse som skjer før en melding sendes", + "Apps_Interface_IPreMessageSentModify": "Hendelse som skjer før en melding sendes", + "Apps_License_Message_maxSeats": "Lisensen tar ikke imot gjeldende antall aktive brukere. Vennligst øk antall seter", + "Apps_License_Message_publicKey": "Det har oppstått en feil under dekryptering av lisensen. Synkroniser arbeidsområdet ditt i Connectivity Service og prøv på nytt", + "Apps_License_Message_renewal": "Lisensen er utløpt og må fornyes", + "Apps_Logs_TTL": "Antall dager å lagre logger fra apper", + "Apps_Logs_TTL_7days": "syv dager", + "Apps_Logs_TTL_14days": "14 dager", + "Apps_Logs_TTL_30days": "30 dager", + "Apps_Logs_TTL_Alert": "Avhengig av størrelsen på loggsamlingen, kan endring av denne innstillingen føre til treghet i noen øyeblikk", + "Apps_Marketplace_Deactivate_App_Prompt": "Vil du virkelig deaktivere denne appen?", + "Apps_Marketplace_Login_Required_Description": "Kjøp av apper fra Rocket.Chat Marketplace krever registrering av arbeidsområdet og innlogging.", + "Apps_Marketplace_Login_Required_Title": "Markedsplass-pålogging er påkrevd", + "Apps_Marketplace_Modify_App_Subscription": "Endre abonnement", + "Apps_Marketplace_pricingPlan_monthly": "{{price}} / måned", + "Apps_Marketplace_pricingPlan_monthly_perUser": "{{price}} / måned per bruker", + "Apps_Marketplace_pricingPlan_monthly_trialDays": "{{price}} / måned-{{trialDays}}-dagers prøveperiode", + "Apps_Marketplace_pricingPlan_monthly_perUser_trialDays": "{{price}} / måned per bruker-{{trialDays}}-dagers prøveperiode", + "Apps_Marketplace_pricingPlan_+*_monthly": " {{price}}+* / måned", + "Apps_Marketplace_pricingPlan_+*_monthly_trialDays": " {{price}}+* / måned-{{trialDays}}-dagers prøveperiode", + "Apps_Marketplace_pricingPlan_+*_monthly_perUser": " {{price}}+* / måned per bruker", + "Apps_Marketplace_pricingPlan_+*_monthly_perUser_trialDays": " {{price}}+* / måned per bruker-{{trialDays}}-dagers prøveperiode", + "Apps_Marketplace_pricingPlan_+*_yearly": " {{price}}+* / år", + "Apps_Marketplace_pricingPlan_+*_yearly_trialDays": " {{price}}+* / år-{{trialDays}}-dagers prøveperiode", + "Apps_Marketplace_pricingPlan_+*_yearly_perUser": " {{price}}+* / år per bruker", + "Apps_Marketplace_pricingPlan_+*_yearly_perUser_trialDays": " {{price}}+* / år per bruker-{{trialDays}}-dagers prøveperiode", + "Apps_Marketplace_pricingPlan_yearly_trialDays": "{{price}} / år-{{trialDays}}-dagers prøveperiode", + "Apps_Marketplace_pricingPlan_yearly_perUser_trialDays": "{{price}} / år per bruker-{{trialDays}}-dagers prøveperiode", + "Apps_Marketplace_Uninstall_App_Prompt": "Er du sikker på at du vil avinstallere denne appen?", + "Apps_Marketplace_Uninstall_Subscribed_App_Anyway": "Avinstaller det uansett", + "Apps_Marketplace_Uninstall_Subscribed_App_Prompt": "Denne appen har et aktivt abonnement. Avinstallering av appen vil ikke kansellere abonnementet. Hvis du ønsker å avinstallere appen, må du endre abonnementet ditt før du avinstallerer.", + "Apps_Permissions_Review_Modal_Title": "Nødvendige tillatelser", + "Apps_Permissions_Review_Modal_Subtitle": "Denne appen vil ha tilgang til følgende tillatelser. Aksepterer du?", + "Apps_Permissions_No_Permissions_Required": "Appen trenger ingen ytterligere tillatelser", + "Apps_Permissions_user_read": "Tilgang til brukerinformasjon", + "Apps_Permissions_user_write": "Endre brukerinformasjon", + "Apps_Permissions_upload_read": "Tilgang til filer lastet opp til denne serveren", + "Apps_Permissions_upload_write": "Last opp filer til denne serveren", + "Apps_Permissions_server-setting_read": "Tilgang til innstillingene på denne serveren", + "Apps_Permissions_server-setting_write": "Endre innstillingene på denne serveren", + "Apps_Permissions_room_read": "Tilgang til rominformasjon", + "Apps_Permissions_room_write": "Opprett og modifiser rom", + "Apps_Permissions_message_read": "Tilgang til meldinger", + "Apps_Permissions_message_write": "Sende og endre meldinger", + "Apps_Permissions_livechat-status_read": "Tilgang til Livechat-statusinformasjon", + "Apps_Permissions_livechat-custom-fields_write": "Endre Livechat egendefinert felt-instillinger", + "Apps_Permissions_livechat-visitor_read": "Tilgang til Livechat-besøksinformasjon", + "Apps_Permissions_livechat-visitor_write": "Endre Livechat-besøksinformasjon", + "Apps_Permissions_livechat-message_read": "Tilgang til Livechat-meldingsinformasjon", + "Apps_Permissions_livechat-message_write": "Endre Livechat-meldingsinformasjon", + "Apps_Permissions_livechat-room_read": "Tilgang til rominformasjon for Livechat", + "Apps_Permissions_livechat-room_write": "Endre Livechat rominformasjon", + "Apps_Permissions_livechat-department_read": "Tilgang til informasjon om Livechat-avdelingen", + "Apps_Permissions_livechat-department_multiple": "Tilgang til informasjon om flere Livechat-avdelinger", + "Apps_Permissions_slashcommand": "Registrer nye skråstrekkommandoer", + "Apps_Permissions_api": "Registrer nye HTTP-endepunkter", + "Apps_Permissions_networking": "Tilgang til dette servernettverket", + "Apps_Permissions_ui_interact": "Samhandle med brukergrensesnittet", "Apps_Settings": "Appens innstillinger", + "Apps_Manual_Update_Modal_Title": "Denne appen er allerede installert", + "Apps_Manual_Update_Modal_Body": "Vil du oppdatere den?", + "Apps_User_Already_Exists": "Brukernavnet \"{{username}}\" brukes allerede. Gi nytt navn til eller fjern brukeren som bruker navnet, for å installere denne appen", + "AutoLinker": "AutoLinker", "Apps_WhatIsIt": "Apper: Hva er de?", "Apps_WhatIsIt_paragraph1": "Et nytt ikon i administrasjonsområdet! Hva betyr dette og hva er apper?", "Apps_WhatIsIt_paragraph2": "For det første refererer Apper i denne sammenheng ikke til mobilapplikasjonene. Faktisk vil det være best å tenke på dem når det gjelder plugins eller avanserte integrasjoner.", @@ -509,15 +636,31 @@ "archive-room": "Arkiv-rom", "archive-room_description": "Tillatelse til å arkivere en kanal", "are_typing": "skriver", + "are_playing": "spiller av", + "is_playing": "spiller av", + "are_uploading": "laster opp", + "are_recording": "tar opp", + "is_uploading": "laster opp", + "is_recording": "tar opp", "Are_you_sure": "Er du sikker?", + "Are_you_sure_delete_department": "Er du sikker på at du vil slette denne avdelingen? Denne handlingen kan ikke reverseres. Skriv inn avdelingsnavnet for å bekrefte.", + "Are_you_sure_you_want_to_close_this_chat": "Er du sikker på at du vil lukke denne chatten?", "Are_you_sure_you_want_to_delete_your_account": "Er du sikker på at du vil slette din konto?", "Are_you_sure_you_want_to_disable_Facebook_integration": "Er du sikker på at du vil deaktivere Facebook-integrasjon?", + "Are_you_sure_you_want_to_pin_this_message": "Er du sikker på at du vil feste denne meldingen?", + "Are_you_sure_you_want_to_reset_the_name_of_all_priorities": "Er du sikker på at du vil tilbakestille navnet på alle prioriteringer?", + "Assets_Description": "Tilpass arbeidsområdets logo, ikon, favorittikon og mer.", "Assign_admin": "Tilordne admin", + "Assign_new_conversations_to_bot_agent": "Tilordne nye samtaler til robot-agenten", "assign-admin-role": "Tilordne Admin-rolle", "assign-admin-role_description": "Tillatelse til å tilordne administrasjonsrollen til andre brukere", + "assign-roles": "Tildel roller", + "assign-roles_description": "Tilgang til å tildele roller til andre brukere", + "Associate": "Forbinde", "at": "på", "At_least_one_added_token_is_required_by_the_user": "Minst ett tilleggstegn kreves av brukeren", "AtlassianCrowd": "Atlassian Crowd", + "AtlassianCrowd_Description": "Integrer Atlassian Crowd.", "Attachment_File_Uploaded": "Filopplastet", "Attribute_handling": "Attributthåndtering", "Audio": "Audio", @@ -525,68 +668,190 @@ "Audio_Notification_Value_Description": "Kan være hvilken som helst egendefinert lyd eller standard: bipp, chelle, ding, dråpe, høyttaler, årstider", "Audio_Notifications_Default_Alert": "Lydvarsler Standardvarsel", "Audio_Notifications_Value": "Standard melding melding lyd", + "Audio_record": "Lydopptak", + "Audit": "Revider", + "Auth": "Tilgangsstyring", "Auth_Token": "Auth Token", + "Authentication": "Autentisering", "Author": "Forfatter", "Author_Information": "Forfatterinformasjon", + "Author_Site": "Forfatterside", "Authorization_URL": "Autorisasjonsadresse", "Authorize": "Autorisere", + "Authorize_access_to_your_account": "Gi tilgang til din konto", + "Automatic_translation_not_available": "Automatisk oversettelse er ikke tilgjengelig", + "Automatic_translation_not_available_info": "Ende-til-ende-kryptering er aktivert for dette rommet, oversettelser fungerer ikke på krypterte meldinger ", "Auto_Load_Images": "Auto Load Images", + "Auto_Selection": "Automatisk valg", "Auto_Translate": "Auto-Trans", + "Calls_in_queue": "{{calls}} anrop i kø", "auto-translate": "Automatisk Oversett", "auto-translate_description": "Tillatelse til å bruke automatisk oversettelsesverktøy", "Automatic_Translation": "Automatisk oversettelse", "AutoTranslate": "Auto-Trans", "AutoTranslate_APIKey": "API-nøkkel", "AutoTranslate_Change_Language_Description": "Hvis du endrer språk for automatisk oversettelse, oversetter du ikke tidligere meldinger.", + "AutoTranslate_DeepL": "DeepL", + "AutoTranslate_Disabled_for_room": "Automatisk oversettelse er deaktivert for #{{roomName}}", "AutoTranslate_Enabled": "Aktiver automatisk-translate", "AutoTranslate_Enabled_Description": "Aktivering av automatisk oversettelse gjør at folk med `automatisk oversetter` tillatelse til å få alle meldinger automatisk oversatt til deres valgte språk. Avgifter kan gjelde, se [Googles dokumentasjon](https://cloud.google.com/translate/pricing)", + "AutoTranslate_Enabled_for_room": "Automatisk oversettelse er aktivert for #{{roomName}}", + "AutoTranslate_AutoEnableOnJoinRoom": "Automatisk oversettelse for ikke-standardspråklige medlemmer", + "AutoTranslate_AutoEnableOnJoinRoom_Description": "Dersom aktivert: Når en bruker blir med i et rom med et annet standardspråk enn brukerens språkpreferanse, blir meldinger automatisk oversatt for brukeren. ", + "AutoTranslate_Google": "Google", + "AutoTranslate_language_set_to": "Automatisk oversettelsesspråk satt til {{language}}", + "AutoTranslate_Microsoft": "Microsoft", + "AutoTranslate_Microsoft_API_Key": "Ocp-Apim-Abonnementsnøkkel", + "AutoTranslate_ServiceProvider": "Tjenesteleverandør", "Available": "Tilgjengelig", "Available_agents": "Tilgjengelige agenter", "Available_departments": "Tilgjengelige avdelinger", "Avatar": "Avatar", + "Avatars": "Avatarer", "Avatar_changed_successfully": "Avatar ble endret", "Avatar_URL": "Avatar-URL", + "Avatar_format_invalid": "Ugyldig format. Kun bildetype er tillatt", "Avatar_url_invalid_or_error": "Den oppgitte nettadressen er ugyldig eller ikke tilgjengelig. Vennligst prøv igjen, men med en annen url.", + "Avg_chat_duration": "Gjennomsnittlig chatvarighet", + "Avg_first_response_time": "Gjennomsnitt av førstebesvarelsestid", + "Avg_of_abandoned_chats": "Gjennomsnitt av forlatte chatter", + "Avg_of_available_service_time": "Gjennomsnitt av tjenestens tilgjengelige tid", + "Avg_of_chat_duration_time": "Gjennomsnittlig chatvarighetstid", + "Avg_of_service_time": "Gjennomsnitt av tjenestetid", + "Avg_of_waiting_time": "Gjennomsnitt av ventetid", + "Avg_reaction_time": "Gjennomsnitt av reaksjonstid", + "Avg_response_time": "Gjennomsnitt av responstid", "away": "borte", "Away": "Borte", "Back": "Tilbake", "Back_to_applications": "Tilbake til programmer", + "Back_to_calendar": "Tilbake til kalenderen", "Back_to_chat": "Tilbake til chat", + "Back_to_imports": "Tilbake til import", "Back_to_integration_detail": "Tilbake til integrasjonsdetaljene", "Back_to_integrations": "Tilbake til integrasjoner", "Back_to_login": "Tilbake til login", "Back_to_Manage_Apps": "Tilbake til Administrer apper", "Back_to_permissions": "Tilbake til rettigheter", + "Back_to_room": "Tilbake til Room", + "Back_to_threads": "Tilbake til tråder", "Backup_codes": "Sikkerhetskopieringskoder", "ban-user": "Ban forbruker", "ban-user_description": "Tillatelse til å forby en bruker fra en kanal", + "BBB_End_Meeting": "Avslutt møte", + "BBB_Enable_Teams": "Aktiver for Teams", + "BBB_Join_Meeting": "Bli med i møtet", + "BBB_Start_Meeting": "Start møte", + "BBB_Video_Call": "BBB-videoanrop", + "BBB_You_have_no_permission_to_start_a_call": "Du har ikke tillatelse til å starte en samtale", + "Be_the_first_to_join": "Bli med som den første", + "Belongs_To": "Tilhører", + "Best_first_response_time": "Beste første responstid", "Beta_feature_Depends_on_Video_Conference_to_be_enabled": "Beta-funksjonen. Avhenger av videokonferanse for å være aktivert.", + "Better": "Bedre", + "Bio": "Bio", + "Bio_Placeholder": "Bio-plassholder", + "Block": "Blokker", + "Block_Multiple_Failed_Logins_Attempts_Until_Block_By_Ip": "Antall mislykkede forsøk før blokkering av IP-adresse", + "Block_Multiple_Failed_Logins_Attempts_Until_Block_by_User": "Antall mislykkede forsøk før blokkering av bruker", + "Block_Multiple_Failed_Logins_By_Ip": "Blokker mislykkede påloggingsforsøk for IP", + "Block_Multiple_Failed_Logins_By_User": "Blokker mislykkede påloggingsforsøk for brukernavn", + "Block_Multiple_Failed_Logins_Enable_Collect_Login_data_Description": "Lagrer IP og brukernavn fra innloggingsforsøk til en samling i databasen", + "Block_Multiple_Failed_Logins_Enabled": "Aktiver innsamling av innloggingsdata", + "Block_Multiple_Failed_Logins_Ip_Whitelist": "IP-tillitsliste ", + "Block_Multiple_Failed_Logins_Time_To_Unblock_By_Ip_In_Minutes": "Varighet av IP-adresseblokkering (i minutter)", + "Block_Multiple_Failed_Logins_Time_To_Unblock_By_Ip_In_Minutes_Description": "Dette er hvor lenge en IP-adresse er blokkert, og tiden det tar før telleren for antall feilede forsøk tilbakestilles ", + "Block_Multiple_Failed_Logins_Time_To_Unblock_By_User_In_Minutes": "Varighet av brukerblokkering (i minutter)", + "Block_Multiple_Failed_Logins_Time_To_Unblock_By_User_In_Minutes_Description": "Dette er varigheten brukeren er blokkert, og tiden mellom mislykkede forsøk før telleren tilbakestilles", + "Block_Multiple_Failed_Logins_Notify_Failed": "Varsle om mislykkede påloggingsforsøk", + "Block_Multiple_Failed_Logins_Notify_Failed_Channel": "Channel for å sende varslene", + "Block_Multiple_Failed_Logins_Notify_Failed_Channel_Desc": "Dette er hvor varslinger vil bli mottatt. Sørg for at kanalen eksisterer. Kanalnavnet skal ikke inneholde \"#\"", "Block_User": "Blokker bruker", "Blockchain": "Blockchain", + "block-ip-device-management_description": "Tillatelse til å blokkere en IP-adresse", + "Block_IP_Address": "Blokker IP-adresse", + "Blocked_IP_Addresses": "Blokkerte IP-adresser", + "Blockstack_Description": "Gi arbeidsområdets medlemmer muligheten til å logge på uten å være avhengig av tredjeparter eller eksterne servere.", + "Blockstack_Auth_Description": "Authentiseringsbeskrivelse", + "Blockstack_Generate_Username": "Generer brukernavn", "Body": "Kropp", + "Bold": "Fet", "bot_request": "Bot forespørsel", "BotHelpers_userFields": "Brukerfelt", "BotHelpers_userFields_Description": "CSV av brukerfelt som kan nås ved hjelp av botshjelpemetoder.", "Bots": "bots", + "Bots_Description": "Angi feltene som kan refereres til og brukes når du utvikler roboter.", "Branch": "Branch", + "Broadcast": "Kringkaste", "Broadcast_channel": "Broadcast Channel", "Broadcast_channel_Description": "Kun autoriserte brukere kan skrive nye meldinger, men de andre brukerne vil kunne svare", "Broadcast_Connected_Instances": "Broadcast Connected Instances", + "Broadcasting_api_key": "Kringkastings-API-nøkkel", + "Broadcasting_client_id": "Kringkastingsklient-ID", + "Broadcasting_client_secret": "Kringkastingsklienthemmelighet", + "Broadcasting_enabled": "Kringkasting aktivert", + "Broadcasting_media_server_url": "Nettadresse for kringkastingsmedieserver", + "Browse_Files": "Bla gjennom filer", + "Browser_does_not_support_audio_element": "Nettleseren din støtter ikke lydelementet.", + "Browser_does_not_support_video_element": "Nettleseren din støtter ikke videoelementet.", + "Browser_does_not_support_recording_video": "Nettleseren din støtter ikke opptak av video", "Bugsnag_api_key": "Bugsnag API-nøkkel", "Build_Environment": "Bygg miljø", "bulk-register-user": "Bulk Create Channels", "bulk-register-user_description": "Tillatelse til å lage kanaler i bulk", + "Busiest_day": "Travleste dagen", + "Busiest_time": "Travleste tiden", + "Business_Hour": "Kontortid", + "Business_Hour_Removed": "Kontortid fjernet", + "Business_Hours": "Kontortid", + "Business_hours_enabled": "Forretningstid aktivert", + "Business_hours_is_disabled": "Forretningstid er deaktivert ", + "Business_hours_is_disabled_description": "Aktiver åpningstider i administrasjonspanelet for arbeidsområdet, for å fortelle kundene når du er tilgjengelig og når de kan forvente et svar.", "busy": "opptatt", "Busy": "Opptatt", + "Buy": "Kjøp", + "By": "Av", "by": "av", "cache_cleared": "Cache ryddet", + "Calendar_MeetingUrl_Regex": "Regulæruttrykk for møte-URL ", + "Calendar_MeetingUrl_Regex_Description": "Uttrykk som brukes til å oppdage møte-URLer i hendelsesbeskrivelser. Den første matchende gruppen med en gyldig URL vil bli brukt. HTML-kodede nettadresser vil bli dekodet automatisk.", + "Calendar_settings": "Kalenderinnstillinger", + "Call": "Ring", + "Call_again": "Ring igjen", + "Call_back": "Ring tilbake", + "Call_not_found": "Anropet ble ikke funnet", + "Call_not_found_error": "Dette kan skje når anrops-URLen ikke er gyldig, eller du har tilkoblingsproblemer. Sjekk med kilden til anrops-URLen og prøv igjen, eller snakk med administratoren for ditt arbeidsområde hvis problemet vedvarer", + "Calling": "Ringer", + "Call_ended": "Anrop avsluttet", + "Calls": "Samtaler", + "Calls_in_queue_zero": "Køen er tom", + "Call_declined": "Anrop avvist!", + "Call_history_provides_a_record_of_when_calls_took_place_and_who_joined": "Samtalehistorikk gir en oversikt over når samtaler fant sted og hvem som var med.", + "Call_Information": "Anropsinformasjon", + "Call_Already_Ended": "Samtale allerede avsluttet", + "Call_number_premium_only": "Ring nummer (kun Premium-abonnementer)", + "call-management_description": "Tillatelse til å starte et møte", + "Call_ongoing": "Samtale pågår", + "Call_started": "Samtale startet", + "Call_was_not_answered": "Anropet ble ikke besvart", + "Caller": "Innringer", + "Camera_access_not_allowed": "Kameratilgang ble ikke tillatt, sjekk nettleserinnstillingene.", + "Cam_on": "Kamera på", + "Cam_off": "Kamera av", "Cancel": "Avbryt", "Cancel_message_input": "Avbryt", + "Canceled": "Avbrutt", + "Cancel_subscription": "Avbryt abonnement", + "Create_department": "Opprett avdeling", + "Create_direct_message": "Opprett direkte melding", + "Create_SLA_policy": "Lag SLA-retningslinjer", "Cannot_invite_users_to_direct_rooms": "Kan ikke invitere brukere til å lede rom", "Cannot_open_conversation_with_yourself": "Kan ikke sende melding til deg selv", + "Cannot_share_your_location": "Kan ikke dele din posisjonen...", + "Cant_join": "Kan ikke bli med", "CAS_autoclose": "Autoclose Login Popup", "CAS_base_url": "SSO Base URL", - "CAS_base_url_Description": "Basisadressen til din eksterne SSO-tjeneste, for eksempel: https: //sso.example.undef/sso/", + "CAS_base_url_Description": "URLen til din eksterne SSO-tjeneste, for eksempel: https: //sso.example.undef/sso/", "CAS_button_color": "Innloggingsknapp Bakgrunnsfarge", "CAS_button_label_color": "Innloggingsknapp Tekstfarge", "CAS_button_label_text": "Innloggingsknappetikett", @@ -602,8 +867,12 @@ "CAS_Sync_User_Data_FieldMap_Description": "Bruk denne JSON-inngangen til å bygge interne attributter (nøkkel) fra eksterne attributter (verdi). Eksterne attributtnavn vedlagt med% vil bli interpolert i verdi strenger. \nEksempel, `{\"email\":\"%e-post% \", \"navn\":\"%firstname%, %lastname% \"}` \n \nAttributtkartet er alltid interpolert. I CAS 1.0 er kun «brukernavnet» attributtet tilgjengelig. Tilgjengelige interne attributter er: brukernavn, navn, e-post, rom; rom er en kommaseparert liste over rom for å delta i brukeropprettelsen, for eksempel: `{\"rooms\": \"% team%,%avdeling%\"}` vil bli med i CAS-brukere ved opprettelse til deres team- og avdelingskanal.", "CAS_version": "CAS versjon", "CAS_version_Description": "Bruk bare en støttet CAS-versjon som støttes av din CAS SSO-tjeneste.", + "Categories": "Kategorier", + "Categories*": "Kategorier*", "CDN_PREFIX": "CDN Prefix", "Certificates_and_Keys": "Sertifikater og nøkler", + "changed_room_announcement_to__room_announcement_": "endret romkunngjøring til: {{room_announcement}}", + "changed_room_description_to__room_description_": "endret rombeskrivelse til: {{room_description}}", "Change_Room_Type": "Endring av romtypen", "Changing_email": "Endre e-post", "channel": "kanal", @@ -619,20 +888,32 @@ "Channel_to_listen_on": "Kanal å lytte på", "Channel_Unarchived": "Kanal med navn `#%s` har blitt unarchived successfully", "Channels": "kanaler", + "Channels_added": "Kanaler ble lagt til", "Channels_are_where_your_team_communicate": "Kanaler er hvor teamet ditt kommuniserer", "Channels_list": "Liste over offentlige kanaler", "Chat_button": "Chat-knapp", "Chat_closed": "Chat avsluttet", + "Chat_closed_by_agent": "Chat stengt av agent", "Chat_closed_successfully": "Chat sluttet vellykket", + "Chat_History": "Chat historikk", "Chat_Now": "Chat nå", + "Chat_On_Hold_Successfully": "Denne chatten ble satt på vent", + "Chat_resumed": "Chat gjenopptatt", + "Chat_started": "Chat startet", "Chat_window": "Chat-vindu", "Chatops_Enabled": "Aktiver Chatops", "Chatops_Title": "Chatops Panel", "Chatops_Username": "Chatops Brukernavn", + "Chat_Duration": "Chattens varighet", + "Chats_removed": "Chatter fjernet", + "Check_if_the_spelling_is_correct": "Sjekk om stavemåten er riktig", + "Check_device_activity": "Sjekk enhetsaktivitet", "Choose_a_room": "Velg et rom", "Choose_messages": "Velg meldinger", "Choose_the_alias_that_will_appear_before_the_username_in_messages": "Velg aliaset som vil vises før brukernavnet i meldinger.", "Choose_the_username_that_this_integration_will_post_as": "Velg brukernavnet som denne integrasjonen vil legge inn som.", + "Choose_users": "Velg brukere", + "Clean_Usernames": "Fjern brukernavn", "clean-channel-history": "Rengjør kanalhistorikken", "clean-channel-history_description": "Tillatelse til å slette historien fra kanaler", "clear": "Clear", @@ -640,53 +921,131 @@ "clear_cache_now": "Fjern cache nå", "clear_history": "Slett logg", "Click_here": "Klikk her", + "Click_here_for_more_details_or_contact_sales_for_a_new_license": "Klikk her for mer informasjon eller kontakt {{email}} for en ny lisens.", "Click_here_for_more_info": "Klikk her for mer info", + "Click_here_to_clear_the_selection": "Klikk her for å fjerne valget", + "Click_here_to_enter_your_encryption_password": "Klikk her for å angi krypteringspassordet ditt", + "Click_here_to_view_and_copy_your_password": "Klikk her for å se og kopiere passordet ditt.", "Click_the_messages_you_would_like_to_send_by_email": "Klikk på meldingene du vil sende via e-post", "Click_to_join": "Klikk for å bli med!", + "Click_to_load": "Klikk for å laste", "Client_ID": "klient-ID", "Client_Secret": "Klientshemmelighet", + "Client": "Klient", "Clients_will_refresh_in_a_few_seconds": "Klienter vil oppdatere om noen få sekunder", "close": "Lukk", "Close": "Lukk", + "Close_chat": "Lukk chat", + "Close_room_description": "Du er i ferd med å lukke denne chatten. Er du sikker på at du vil fortsette?", "close-livechat-room": "Lukk Livechat Room", "close-livechat-room_description": "Tillatelse til å lukke den nåværende LiveChat-kanalen", "Close_menu": "Lukk meny", "close-others-livechat-room": "Lukk Livechat Room", "close-others-livechat-room_description": "Tillatelse til å lukke andre LiveChat-kanaler", + "Close_Window": "Lukk vindu", "Closed": "Lukket", + "Closed_At": "Stengt klokken", + "Closed_automatically": "Lukket automatisk av systemet", + "Closed_automatically_because_chat_was_onhold_for_seconds": "Lukket automatisk fordi chatten var på vent i {{onHoldTime}} sekunder", + "Closed_automatically_chat_queued_too_long": "Automatisk lukket av systemet (maksimal tid i kø overskredet)", "Closed_by_visitor": "Stengt av besøkende", "Closing_chat": "Avsluttende chat", + "Closing_chat_message": "Lukker chat-melding", + "Cloud_Apply_Offline_License": "Bruk frakoblet lisens", + "Cloud_Change_Offline_License": "Endre frakoblet lisens", + "Cloud_Invalid_license": "Ugyldig lisens!", + "Cloud_Apply_license": "Bruk lisens", + "Cloud_error_code": "Kode: {{errorCode}}", + "Cloud_registration_required": "Registrering nødvendig", + "Cloud_resend_email": "Send e-post på nytt", + "Cloud_Service_Agree_PrivacyTerms_Description": "Jeg godtar [vilkårene](https://rocket.chat/terms) og [personvernreglene](https://rocket.chat/privacy)", + "Cloud_status_page_description": "Hvis en bestemt skytjeneste har problemer, kan du se etter kjente problemer på statussiden vår på", + "Cloud_troubleshooting": "Feilsøking", + "Cloud_update_email": "Oppdater e-post", + "Cloud_what_is_it": "Hva er dette?", + "Copy_Link": "Kopier lenke", + "Copy_password": "Kopier passord", + "Cloud_what_is_it_additional": "I tillegg vil du kunne administrere lisenser, fakturering og support fra Rocket.Chat Cloud Console.", + "Cloud_what_is_it_description": "Rocket.Chat Cloud Connect lar deg koble ditt selvhostede Rocket.Chat Workspace til tjenester vi tilbyr i vår nettsky.", + "Cloud_what_is_it_services_like": "Tjenester som:", + "Cloud_workspace_connected": "Arbeidsområdet ditt er koblet til Rocket.Chat Cloud. Hvis du logger på Rocket.Chat Cloud-kontoen din her, kan du samhandle med tjenester som markedsplass.", + "Cloud_workspace_connected_plus_account": "Arbeidsområdet ditt er nå koblet til Rocket.Chat Cloud og en konto er tilknyttet.", + "Cloud_workspace_connected_without_account": "Arbeidsområdet ditt er nå koblet til Rocket.Chat Cloud. Hvis du vil, kan du logge på Rocket.Chat Cloud og knytte arbeidsområdet ditt til Cloud-kontoen din.", + "Cloud_workspace_disconnect": "Dersom du ikke lenger ønsker å bruke skytjenester, kan du koble fra arbeidsområdet ditt fra Rocket.Chat Cloud.", + "Cloud_workspace_support": "Hvis du har problemer med en skytjeneste, prøv å synkronisere først. Hvis problemet vedvarer, kan du kontakte support i Cloud Console.", + "Collaborative": "Samarbeidende", + "Collapse": "Kollapse", "Collapse_Embedded_Media_By_Default": "Skjul innebygd media som standard", "color": "Farge", "Color": "Farge", + "Colors": "Farger", "Commands": "kommandoer", "Comment_to_leave_on_closing_session": "Kommenter å forlate på avslutnings sesjon", + "Comment": "Kommentar", "Common_Access": "Felles tilgang", "Community": "Samfunnet", + "Free_Edition": "Gratisversjon", + "Composer_not_available_phone_calls": "Meldinger er ikke tilgjengelige på telefonsamtaler", "Condensed": "kondensert", + "Condition": "Betingelse", + "Completed": "Fullført", "Computer": "Datamaskin", + "Conference_call_has_ended": "_Samtalen er avsluttet._", + "Configure_Incoming_Mail_IMAP": "Konfigurer innkommende e-post (IMAP)", + "Configure_Outgoing_Mail_SMTP": "Konfigurer utgående e-post (SMTP)", + "Confirm": "Bekreft", + "Confirm_new_encryption_password": "Bekreft nytt krypteringspassord", "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", + "Confirm_configuration_update": "Bekreft konfigurasjonsoppdatering", + "Confirm_new_workspace_description": "Identifikasjonsdata og skytilkoblingsdata vil bli tilbakestilt.

Advarsel: Lisensen kan bli påvirket hvis du endrer nettadressen til arbeidsområdet.", + "Confirm_new_workspace": "Bekreft nytt arbeidsområde", + "Confirmation": "Bekreftelse", + "Configure_video_conference": "Konfigurer konferansesamtale", + "Configuration_update_confirmed": "Konfigurasjonsoppdatering bekreftet", + "Configuration_update": "Konfigurasjonsoppdatering", + "Connect": "Koble til", + "Connected": "Tilkoblet", + "Connect_SSL_TLS": "Koble til med SSL/TLS", "Connection_Closed": "Tilkoblingen er stengt", "Connection_Reset": "Tilbakestilling av tilkobling", + "Connection_error": "Tilkoblingsfeil", + "Connection_failed": "LDAP-tilkobling feilet", "Consulting": "Consulting", + "Contact": "Kontakt", + "Contacts": "Kontakter", + "Contact_Name": "kontakt navn", "Contains_Security_Fixes": "Inneholder sikkerhetsoppdateringer", + "Contact_Info": "Kontaktinformasjon", "Content": "Innhold", "Continue": "Fortsette", + "Continue_Adding": "Fortsette å legge til?", "Continuous_sound_notifications_for_new_livechat_room": "Kontinuerlige lydvarsler for nytt livechat-rom", "Conversation": "Samtale", "Conversation_closed": "Samtalen avsluttet: {{comment}}.", + "Conversation_closed_without_comment": "Samtalen ble avsluttet", "Conversation_finished": "Samtalen er avsluttet", "Conversation_finished_message": "Samtalen avsluttet melding", "conversation_with_s": "samtalen med %s", + "Conversations": "Samtaler", + "Conversations_per_day": "Samtaler per dag", + "Convert": "Konverter", "Convert_Ascii_Emojis": "Konverter ASCII til Emoji", + "Convert_to_channel": "Konverter til Channel", + "Converted__roomName__to_team": "konverterte #{{roomName}} til et team", + "Converted__roomName__to_channel": "konverterte #{{roomName}} til en kanal", + "Converted__roomName__to_a_team": "konverterte #{{roomName}} til et team", + "Converted__roomName__to_a_channel": "konverterte #{{roomName}} til en kanal", + "Converting_team_to_channel": "Konverterer team til kanal", "Copied": "kopiert", "Copy": "Kopi", + "Copy_text": "Kopier tekst", "Copy_to_clipboard": "Kopiere til utklippstavle", "COPY_TO_CLIPBOARD": "KOPIERE TIL UTKLIPPSTAVLE", "Count": "Telle", + "Counters": "Tellere", "Country": "Land", "Country_Afghanistan": "Afghanistan", "Country_Albania": "Albania", @@ -930,69 +1289,121 @@ "Country_Zambia": "Zambia", "Country_Zimbabwe": "Zimbabwe", "Create": "Skape", + "Create_custom_field": "Opprett egendefinert felt", + "Create_channel": "Opprett Channel", + "Create_channels": "Opprett kanaler", "Create_A_New_Channel": "Opprett en ny kanal", "Create_new": "Lag ny", + "Create_new_members": "Opprett nye medlemmer", "Create_unique_rules_for_this_channel": "Opprett unike regler for denne kanalen", + "Create_unit": "Opprett enhet", "create-c": "Opprett offentlige kanaler", "create-c_description": "Tillatelse til å opprette offentlige kanaler", "create-d": "Lag direkte meldinger", "create-d_description": "Tillatelse til å starte direkte meldinger", + "create-invite-links": "Lag invitasjonslenker", + "create-invite-links_description": "Tillatelse til å opprette invitasjonslenker til kanaler", "create-p": "Opprett private kanaler", "create-p_description": "Tillatelse til å lage private kanaler", + "create-personal-access-tokens": "Opprett personlige tilgangstokener", + "create-personal-access-tokens_description": "Tillatelse til å opprette personlige tilgangstokener", + "create-team": "Opprett team", + "create-team_description": "Tillatelse til å opprette teams", "create-user": "Opprett bruker", "create-user_description": "Tillatelse til å opprette brukere", + "Created": "Opprettet", + "Created_as": "Opprettet som", "Created_at": "Opprettet på", "Created_at_s_by_s": "Opprettet på %s etter %s", "Created_at_s_by_s_triggered_by_s": "Laget til %s etter %s utløst av %s", + "Created_by": "Opprettet av", "CRM_Integration": "CRM Integrasjon", + "CROWD_Allow_Custom_Username": "Tillat egendefinerte brukernavn i Rocket.Chat", "CROWD_Reject_Unauthorized": "Avvis Uautorisert", "Crowd_sync_interval_Description": "Intervallet mellom synkroniseringer. Eksempel \"hver 24. time\" eller \"på den første dagen i uken\", flere eksempler på [Cron Text Parser] (http://bunkat.github.io/later/parsers.html#text)", + "CSV": "CSV", "Current_Chats": "Nåværende Chatter", + "Current_File": "Gjeldene fil", "Current_Status": "Nåværende status", + "Currently_we_dont_support_joining_servers_with_this_many_people": "For øyeblikket støtter vi ikke å koble sammen servere med så mange mennesker", "Custom": "Tilpasset", "Custom CSS": "Tilpasset CSS", "Custom_agent": "Tilpasset agent", + "Custom_dates": "Egendefinerte datoer", "Custom_Emoji": "Egendefinert Emoji", "Custom_Emoji_Add": "Legg til ny emoji", "Custom_Emoji_Added_Successfully": "Tilpasset emoji ble lagt til", "Custom_Emoji_Delete_Warning": "Slette en emoji kan ikke fortrykkes.", "Custom_Emoji_Error_Invalid_Emoji": "Ugyldig emoji", "Custom_Emoji_Error_Name_Or_Alias_Already_In_Use": "Den egendefinerte emoji eller en av aliasene er allerede i bruk.", + "Custom_Emoji_Error_Same_Name_And_Alias": "Det egendefinerte emojinavnet og aliasene deres skal være forskjellige.", "Custom_Emoji_Has_Been_Deleted": "Den egendefinerte emoji er slettet.", "Custom_Emoji_Info": "Egendefinert Emoji Info", "Custom_Emoji_Updated_Successfully": "Egendefinert emoji ble oppdatert", "Custom_Fields": "Egendefinerte felt", + "Custom_Field_Removed": "Egendefinert felt er fjernet", + "Custom_Field_Not_Found": "Egendefinert felt ble ikke funnet", + "Custom_Integration": "Tilpasset integrasjon", + "Custom_OAuth_has_been_added": "Egendefinert OAuth er lagt til", + "Custom_OAuth_has_been_removed": "Tilpasset OAuth er fjernet", "Custom_oauth_helper": "Når du konfigurerer OAuth-leverandøren din, må du informere en tilbakekallingsadresse. Bruk
%s
.", "Custom_oauth_unique_name": "Egendefinert oauth unikt navn", + "Custom_roles": "Egendefinerte roller", + "Custom_roles_upsell_add_custom_roles_workspace_description": "Egendefinerte roller lar deg angi tillatelser for personene i arbeidsområdet ditt. Angi alle rollene du trenger for å sikre at folk har et trygt miljø å jobbe i.", "Custom_Script_Logged_In": "Tilpasset script for logget inn brukere", "Custom_Script_Logged_Out": "Tilpasset script for logget ut brukere", "Custom_Scripts": "Egendefinerte skript", "Custom_Sound_Add": "Legg til tilpasset lyd", "Custom_Sound_Delete_Warning": "Slette en lyd kan ikke fortrykkes.", + "Custom_Sound_Edit": "Rediger egendefinert lyd", "Custom_Sound_Error_Invalid_Sound": "Ugyldig lyd", "Custom_Sound_Error_Name_Already_In_Use": "Det egendefinerte lydnavnet er allerede i bruk.", "Custom_Sound_Has_Been_Deleted": "Den egendefinerte lyden er slettet.", "Custom_Sound_Info": "Tilpasset lydinfo", "Custom_Sound_Saved_Successfully": "Tilpasset lyd lagret vellykket", + "Custom_Status": "Egendefinert status", "Custom_Translations": "Tilpassede oversettelser", "Custom_Translations_Description": "Bør være en gyldig JSON der nøkler er språk som inneholder en nøkkelord og oversettelser. Eksempel: \n `{\"en\": {\"Channels\": \"Rooms\"},\"pt\": {\"Channels\": \"Salas\"}}`", + "Custom_User_Status": "Egendefinert brukerstatus", + "Custom_User_Status_Add": "Legg til egendefinert brukerstatus", + "Custom_User_Status_Edit": "Rediger egendefinert brukerstatus", + "Custom_User_Status_Error_Invalid_User_Status": "Ugyldig brukerstatus", + "Custom_User_Status_Has_Been_Deleted": "Egendefinert brukerstatus er slettet", + "Customer_without_registered_email": "Kunden har ikke registrert e-postadresse", "Customize": "Tilpass", + "Customize_Content": "Tilpass innhold", "CustomSoundsFilesystem": "Egendefinert lyds filsystem", + "CustomSoundsFilesystem_Description": "Spesifiser hvordan egendefinerte lyder lagres.", "Daily_Active_Users": "Daglig aktive brukere", "Dashboard": "Dashbord", + "Data_processing_consent_text": "Samtykketekst for databehandling", "Date": "Dato", "Date_From": "Fra", "Date_to": "til", "DAU_value": "DAU {{value}}", "days": "dager", + "Days": "Dager", "DB_Migration": "Databaseoverføring", "DB_Migration_Date": "Databaseoverføringsdato", + "DDP_Rate_Limit_IP_Enabled": "Begrensning for IP: aktivert", + "DDP_Rate_Limit_IP_Interval_Time": "Begrensning for IP: intervalltid", + "DDP_Rate_Limit_IP_Requests_Allowed": "Begrensning for IP: forespørsler tillatt", "Deactivate": "Deaktiver", "Decline": "Avslå", + "default": "standard", "Default": "Misligholde", + "Default_provider": "Standardleverandør", + "Default_value": "Standardverdi", "Delete": "Slett", + "Deleting": "Sletter", + "Delete_account": "Slett konto", + "Delete_account?": "Slett konto?", + "Delete_all_closed_chats": "Slett alle lukkede chatter", + "Delete_Department?": "Vil du slette avdelingen?", "Delete_message": "Slett melding", "Delete_my_account": "Slett kontoen min", + "Delete_Role_Warning": "Dette kan ikke angres", + "Delete_Role_Warning_Not_Enterprise": "Dette kan ikke angres. Du vil ikke kunne opprette en ny egendefinert rolle, siden den funksjonaliteten ikke lenger er tilgjengelig for ditt nåværende abonnement.", "Delete_Room_Warning": "Hvis du sletter et rom, slettes alle meldinger som er lagt inn i rommet. Dette kan ikke angres.", "Delete_User_Warning": "Hvis du sletter en bruker, slettes alle meldinger fra den aktuelle brukeren. Dette kan ikke angres.", "Delete_User_Warning_Delete": "Hvis du sletter en bruker, slettes alle meldinger fra den aktuelle brukeren. Dette kan ikke angres.", @@ -1004,14 +1415,24 @@ "delete-d_description": "Tillatelse til å slette direkte meldinger", "delete-message": "Slett melding", "delete-message_description": "Tillatelse til å slette en melding i et rom", + "delete-own-message": "Slett egen melding", + "delete-own-message_description": "Tillatelse til å slette egen melding", "delete-p": "Slett private kanaler", "delete-p_description": "Tillatelse til å slette private kanaler", + "delete-team": "Slett team", + "delete-team_description": "Tillatelse til å slette team", "delete-user": "Slett bruker", "delete-user_description": "Tillatelse til å slette brukere", "Deleted": "Slettet!", + "Deleted_user": "Slettet bruker", + "Deleted__roomName__": "slettet #{{roomName}}", + "Deleted__roomName__room": "slettet #{{roomName}}", "Department": "Avdeling", + "Department_archived": "Avdeling arkivert", + "Department_name": "Avdelingsnavn", "Department_not_found": "Avdeling ikke funnet", "Department_removed": "Avdelingen fjernet", + "Department_Removal_Disabled": "Slettalternativet er deaktivert av administrator", "Departments": "avdelinger", "Deployment_ID": "Distribusjons-ID", "Description": "Beskrivelse", @@ -1023,12 +1444,34 @@ "Desktop_Notifications_Duration": "Tidsavbrudd for stasjonær varsling", "Desktop_Notifications_Duration_Description": "Sekunder for å vise skrivebordsmeddelelse. Dette kan påvirke OS X Notification Center. Angi 0 for å bruke standard nettleserinnstillinger og ikke påvirke OS X Notification Center.", "Desktop_Notifications_Enabled": "Bordmeldinger er aktivert", + "Details": "Detaljer", + "Device_Management": "Enhetsstyring", + "Device_Management_Allow_Login_Email_preference": "Tillat medlemmer av arbeidsområdet å slå av e-poster for innloggingsforsøk", + "Device_Management_Client": "Klient", + "Device_Management_Device": "Enhet", "line": "linje", + "Device_Management_Device_Unknown": "Ukjent", + "Device_Management_Enable_Login_Emails": "Aktiver e-poster for registrering av pålogging", + "Device_Management_Enable_Login_Emails_Description": "E-poster sendes til arbeidsområdemedlemmer hver gang nye pålogginger oppdages på deres kontoer.", + "Device_Management_IP": "IP", + "Device_Management_OS": "OS", + "Device_ID": "Enhets-ID", + "Device_Info": "Enhetsinformasjon", + "Device_Logged_Out": "Enheten logget ut", + "Devices": "Enheter", + "Device_settings": "Enhetsinnstillinger", + "Dialed_number_doesnt_exist": "Oppringt nummer eksisterer ikke", + "Dialed_number_is_incomplete": "Oppringt nummer er ufullstendig", "Different_Style_For_User_Mentions": "Ulike stil for brukeren nevner", "Livechat_Facebook_API_Key": "OmniChannel API-nøkkel", + "Direct": "Direkte", + "Direction": "Retning", "Livechat_Facebook_API_Secret": "OmniChannel API Secret", + "Direct_Message": "Direktemelding", "Livechat_Facebook_Enabled": "Facebook integrasjon aktivert", + "Direct_message_creation_description": "Du er i ferd med å opprette en chat med flere brukere. Legg til de du vil snakke med på ett sted via direktemeldinger.", "Direct_message_someone": "Direkte melding noen", + "Direct_message_you_have_joined": "Du har blitt med i en ny direktemelding med", "Direct_Messages": "Direktemeldinger", "Direct_Reply": "Direkte svar", "Direct_Reply_Debug": "Feilsøk Direkte Svar", @@ -1047,30 +1490,59 @@ "Direct_Reply_Username": "Brukernavn", "Direct_Reply_Username_Description": "Bruk absolutt e-post, tagging er ikke tillatt, det ville være over-skrevet", "Directory": "Directory", + "Disable": "Deaktiver", "Disable_Facebook_integration": "Deaktiver Facebook-integrasjon", "Disable_Notifications": "Deaktiver varslinger", "Disable_two-factor_authentication": "Deaktiver tofaktorautentisering", + "Disable_two-factor_authentication_email": "Deaktiver tofaktorautentisering via e-post", "Disabled": "Funksjonshemmet", "Disallow_reacting": "Tillat ikke å reagere", "Disallow_reacting_Description": "Tillater ikke å reagere", + "Discard": "Forkast", + "Disconnect": "Koble fra", + "Discussion": "Diskusjon", + "Discussion_Description": "Diskusjoner er en ekstra måte å organisere samtaler på, som gjør det mulig å invitere brukere fra eksterne kanaler til å delta i bestemte samtaler.", + "Discussion_description": "Hjelp til å holde oversikt over hva som skjer! Ved å opprette en diskusjon opprettes en underkanal til kanalen du valgte og begge kobles sammen.", + "Discussion_first_message_disabled_due_to_e2e": "Du kan begynne å sende ende-til-ende-krypterte meldinger i denne diskusjonen etter at den er opprettet.", + "Discussion_first_message_title": "Din melding", + "Discussion_name": "Diskusjonsnavn", + "Discussion_start": "Start en diskusjon", + "Discussion_target_channel": "Overordnet kanal eller gruppe", + "Discussion_target_channel_description": "Velg en kanal som er relatert til det du vil spørre om", + "Discussion_target_channel_prefix": "Du oppretter en diskusjon i", + "Discussion_title": "Opprett diskusjon", + "discussion-created": "{{message}}", "Discussions": "Diskusjoner", + "Display_avatars": "Vis avatarer", + "Display_chat_permissions": "Vis chattillatelser", "Display_offline_form": "Vis frakoblet skjema", "Display_unread_counter": "Vis antall uleste meldinger", "Displays_action_text": "Viser handlingstekst", + "Do_It_Later": "Gjør det senere", "Do_not_display_unread_counter": "Ikke vis noen teller på denne kanalen", + "Do_not_provide_this_code_to_anyone": "Ikke oppgi denne koden til noen.", + "Do_Nothing": "Ikke gjør noe", + "Do_nothing": "Gjør ingenting", "Do_you_want_to_accept": "Ønsker du å godta?", "Do_you_want_to_change_to_s_question": "Vil du bytte til %s?", + "Documentation": "Dokumentasjon", "Document_Domain": "Dokumentdomenet", "Domain": "Domene", "Domain_added": "domenet er lagt til", "Domain_removed": "Domene fjernet", "Domains": "domener", "Domains_allowed_to_embed_the_livechat_widget": "Kommaseparert liste over domener får lov til å legge inn livechat-widgeten. La være tom for å tillate alle domener.", + "Done": "Ferdig", "Dont_ask_me_again": "Ikke spør meg igjen!", "Dont_ask_me_again_list": "Ikke spør meg igjen listen", "Download": "Last ned", + "Download_Destkop_App": "Last ned desktop-appen", + "Download_Info": "Nedlastingsinformasjon", "Download_My_Data": "Last ned mine data", + "Download_Pending_Avatars": "Last ned ventende avatarer", + "Download_Pending_Files": "Last ned ventende filer", "Download_Snippet": "Last ned", + "Downloading_file_from_external_URL": "Laster ned fil fra ekstern URL", "Drop_to_upload_file": "Drop for å laste opp fil", "Dry_run": "Tørrkjøring", "Dry_run_description": "Vil bare sende en e-post til samme adresse som i Fra. E-posten må tilhøre en gyldig bruker.", @@ -1080,39 +1552,74 @@ "Duplicate_archived_private_group_name": "En arkivert Privatgruppe med navn '%s' eksisterer", "Duplicate_channel_name": "En kanal med navn '%s' eksisterer", "Markdown_Marked_GFM": "Aktiver merket GFM", + "Duplicate_file_name_found": "Duplikatfilnavn funnet.", "Markdown_Marked_Pedantic": "Aktiver merket pedantisk", "Markdown_Marked_SmartLists": "Aktiver merkede smarte lister", "Duplicate_private_group_name": "En privat gruppe med navn '%s' eksisterer", "Markdown_Marked_Smartypants": "Aktiver merkede Smartypants", + "Duplicated_Email_address_will_be_ignored": "Duplisert e-postadresse vil bli ignorert.", "Markdown_Marked_Tables": "Aktiver merkede tabeller", + "E2E Encryption": "E2E-kryptering", + "E2E_Encryption_enabled_for_room": "Ende-til-Ende-kryptering er aktivert for #{{roomName}}", + "E2E_Encryption_disabled_for_room": "Ende-til-Ende-kryptering deaktivert for #{{roomName}}", "Markdown_Parser": "Markdown Parser", "Markdown_SupportSchemesForLink": "Markdown Støtteordninger for Link", + "E2E Encryption_Description": "Hold samtaler private, sørger for at bare avsender og tiltenkte mottakere kan lese dem.", "Markdown_SupportSchemesForLink_Description": "Kommaseparert liste over tillatte ordninger", + "E2E_enable": "Aktiver E2E", + "E2E_disable": "Deaktiver E2E", + "E2E_Enable_description": "Aktiver alternativet for å opprette krypterte grupper og kunne endre grupper og direktemeldinger som skal krypteres", + "E2E_Enabled": "E2E aktivert", + "E2E_Enabled_Default_DirectRooms": "Aktiver kryptering for Direkterom som standard", + "E2E_Encryption_Password_Explanation": "Du kan nå opprette krypterte private grupper og direktemeldinger. Du kan også endre eksisterende private grupper eller direktemeldinger til krypterte.

Dette er ende-til-ende-kryptering, dvs. at nøkkelen til å kode/dekode meldingene dine vil ikke bli lagret på serveren. Av den grunn må du lagre passordet ditt et trygt sted. Du vil bli bedt om å angi den på andre enheter du ønsker å bruke ende-til-ende-kryptering på.", + "E2E_message_encrypted_placeholder": "Denne meldingen er ende-til-ende-kryptert. For å se den må du skrive inn krypteringsnøkkelen i kontoinnstillingene.", + "E2E_password_request_text": "For å få tilgang til dine krypterte private grupper og direktemeldinger, skriv inn krypteringspassordet ditt.
Du må skrive inn dette passordet for å kode/dekode meldingene dine på hver klient du bruker, siden nøkkelen ikke er lagret på serveren.", + "E2E_password_reveal_text": "Lag sikre private rom og direktemeldinger med ende-til-ende-kryptering.

Lagre passordet ditt på en sikker måte, siden nøkkelen til å kode/dekode meldingene dine ikke blir lagret på serveren. Du må angi den på andre enheter for å bruke e2e-kryptering. Finn ut mer

Endre passord når som helst fra hvilken som helst nettleser du har skrevet det inn på. Husk å lagre passordet ditt før du avviser denne meldingen.

Passordet ditt er: {{randomPassword}}", + "E2E_Reset_Email_Content": "Du er automatisk logget ut. Når du logger på igjen, vil Rocket.Chat generere en ny nøkkel og gjenopprette tilgangen din til et hvilket som helst kryptert rom som har ett eller flere medlemmer online. På grunn av E2E-krypteringens natur, vil Rocket.Chat ikke kunne gjenopprette tilgangen til et kryptert rom som ikke har noen medlemmer online.", + "E2E_Reset_Key_Explanation": "Dette alternativet vil fjerne din nåværende E2E-nøkkel og logge deg ut.
Når du logger på igjen, genererer Rocket.Chat deg en ny nøkkel og gjenoppretter tilgangen din til et hvilket som helst kryptert rom som har ett eller flere medlemmer online.
På grunn av E2E-krypteringens natur, vil Rocket.Chat ikke kunne gjenopprette tilgang til noe kryptert rom som ikke har noen medlemmer online.", + "E2E_Reset_Other_Key_Warning": "Tilbakestill gjeldende E2E-nøkkel vil logge ut brukeren. Når brukeren logger på igjen, vil Rocket.Chat generere en ny nøkkel og gjenopprette brukertilgangen til et hvilket som helst kryptert rom som har ett eller flere medlemmer online. På grunn av E2E-krypteringens natur, vil Rocket.Chat ikke kunne gjenopprette tilgangen til et kryptert rom som ikke har noen medlemmer online.", "Edit": "Rediger", + "Edit_Business_Hour": "Rediger arbeidstid", "Edit_Custom_Field": "Rediger egendefinert felt", "Edit_Department": "Rediger avdeling", "Message_AllowSnippeting": "Tillat meldingsutklipp", + "Edit_Invite": "Rediger invitasjon", "Edit_previous_message": "`%s` - Rediger forrige melding", + "Edit_Priority": "Rediger prioritet", + "Edit_SLA_Policy": "Rediger SLA-retningslinjer", + "Edit_Status": "Rediger status", "Edit_Tag": "Endre tagg", "Edit_Trigger": "Rediger utløser", "Edit_Unit": "Endre enhet", "Message_Attachments_GroupAttach": "Knapper for gruppevedlegg", "Message_Attachments_GroupAttachDescription": "Dette grupperer ikonene under en utvidbar meny. Tar opp mindre skjermplass.", + "Edit_User": "Rediger bruker", "edit-message": "Rediger melding", "edit-message_description": "Tillatelse til å redigere en melding i et rom", "edit-other-user-active-status": "Rediger annen brukeraktiv status", "edit-other-user-active-status_description": "Tillatelse til å aktivere eller deaktivere andre kontoer", + "edit-other-user-e2ee": "Rediger annen bruker E2E-kryptering", + "edit-other-user-e2ee_description": "Tillatelse til å endre andre brukeres E2E-kryptering.", "edit-other-user-info": "Rediger annen brukerinformasjon", "edit-other-user-info_description": "Tillatelse til å endre andre brukerens navn, brukernavn eller e-postadresse.", "edit-other-user-password": "Rediger annet brukerpassord", "edit-other-user-password_description": "Tillatelse til å endre andre brukeres passord. Krever redigering-andre-bruker-info tillatelse.", "edit-privileged-setting": "Rediger privilegert innstilling", "edit-privileged-setting_description": "Tillatelse til å redigere innstillinger", + "edit-team": "Rediger team", + "edit-team_description": "Tillatelse til å redigere team", + "edit-team-channel": "Rediger teamkanal", + "edit-team-channel_description": "Tillatelse til å redigere et teams kanal", + "edit-team-member": "Rediger teammedlem", + "edit-team-member_description": "Tillatelse til å redigere et teams medlemmer", "edit-room": "Rediger rom", "edit-room_description": "Tillatelse til å redigere et roms navn, emne, type (privat eller offentlig status) og status (aktiv eller arkivert)", + "edit-room-avatar": "Rediger romavatar", + "edit-room-avatar_description": "Tillatelse til å redigere et roms avatar.", "edit-room-retention-policy": "Rediger romets retensjonspolicy", "edit-room-retention-policy_description": "Tillatelse til å redigere et roms retensjonspolicy, for å automatisk slette meldinger i den", "multi_line": "multi line", + "Edit_Contact_Profile": "Rediger kontaktprofil", "edited": "redigert", "Editing_room": "Redigeringsrom", "Editing_user": "Redigerer bruker", @@ -1120,34 +1627,71 @@ "Education": "Utdannelse", "Message_ShowFormattingTips": "Vis formateringstips", "Email": "E-post", + "Email_Description": "Konfigurasjoner for å sende kringkastede e-poster fra Rocket.Chat.", "Email_address_to_send_offline_messages": "E-postadresse for å sende utkoblede meldinger", "Email_already_exists": "E-post finnes allerede", "Email_body": "E-post kroppen", "Email_Change_Disabled": "Din Rocket.Chat-administrator har deaktivert endringen av e-post", + "Email_Changed_Description": "Du kan bruke følgende plassholdere:\n - `[email]` for brukerens e-post.\n- `[Site_Name]` og `[Site_URL]` for applikasjonsnavn og URL.", + "Email_Changed_Email_Subject": "[Site_Name] – E-postadressen er endret", + "Email_changed_section": "E-postadresse endret", "Email_Footer_Description": "Du kan bruke følgende plassholdere: \n - `[Site_Name]` og `[Site_URL]` for henholdsvis programnavnet og nettadressen. ", "Email_from": "Fra", "Email_Header_Description": "Du kan bruke følgende plassholdere: \n - `[Site_Name]` og `[Site_URL]` for henholdsvis programnavnet og nettadressen. ", + "Email_Inbox": "E-post-innboks", + "Email_Inboxes": "E-post-innbokser", + "Email_Inbox_has_been_added": "E-postinnboks er lagt til", + "Email_Inbox_has_been_removed": "E-postinnboks er fjernet", "Email_Notification_Mode": "Frakoblede e-postvarsler", "Email_Notification_Mode_All": "Hver Nevn / DM", "Email_Notification_Mode_Disabled": "Funksjonshemmet", + "Email_notification_show_message": "Vis melding i e-postvarsel", + "Email_Notifications_Change_Disabled": "Rocket.Chat-administratoren din har deaktivert e-postvarsel", "Email_or_username": "E-post eller brukernavn", "Email_Placeholder": "Vennligst skriv inn E-postadressen din...", "Email_Placeholder_any": "Vennligst skriv inn e-postadresser ...", + "email_plain_text_only": "Send bare ren tekst-e-post", + "Enterprise_Description": "Oppdater Premium-lisensen din manuelt.", "Email_subject": "Emne", "Enterprise_License": "Enterpriselisens", "Enterprise_License_Description": "Hvis arbeidsområdet ditt er registrert og lisensen er levert av Rocket.Chat Cloud trenger du ikke å oppdatere lisensen manuelt her.", "Email_verified": "E-post bekreftet", + "Email_sent": "E-post sendt", "Emoji": "Emoji", + "Emoji_picker": "Emoji-velger", "EmojiCustomFilesystem": "Egendefinert Emoji-filsystem", + "EmojiCustomFilesystem_Description": "Spesifiser hvordan emojier lagres.", "Empty_title": "Tom tittel", "Enable": "Aktiver", "Enable_Auto_Away": "Aktiver automatisk unna", + "Extra_CSP_Domains": "Ekstra CSP-domener", "Enable_Desktop_Notifications": "Aktiver skrivebordsvarsler", + "Enable_omnichannel_auto_close_abandoned_rooms": "Aktiver automatisk stenging av rom som er forlatt av de besøkende", + "Enable_Password_History": "Aktiver passordhistorikk", "Enable_Svg_Favicon": "Aktiver SVG favicon", "Enable_two-factor_authentication": "Aktiver tofaktorautentisering", + "Enable_two-factor_authentication_email": "Aktiver tofaktorautentisering via e-post", "Enabled": "aktivert", + "Encrypted": "Kryptert", + "Encrypted_channel_Description": "Ende-til-ende kryptert kanal. Søk fungerer ikke med krypterte kanaler, og varsler viser kanskje ikke meldingsinnholdet.", + "Encrypted_key_title": "Klikk her for å deaktivere ende-til-ende-kryptering for denne kanalen (krever e2e-tillatelse)", "Encrypted_message": "Kryptert melding", + "Encrypted_not_available": "Ikke tilgjengelig for offentlige Channel", + "Encryption_key_saved_successfully": "Krypteringsnøkkelen din ble lagret.", + "EncryptionKey_Change_Disabled": "Du kan ikke angi et passord for krypteringsnøkkelen din, da din private nøkkel ikke er tilgjengelig på denne klienten. For å sette et nytt passord må du laste inn din private nøkkel ved å bruke ditt eksisterende passord eller bruke en klient der nøkkelen allerede er lastet inn.", + "End": "Avslutt", + "End_suspicious_sessions": "Avslutt alle mistenkelige økter", + "End_call": "Avslutt samtale", + "End_conversation": "Avslutt samtale", + "Expand_view": "Utvid visningen", + "Explore": "Utforsk", + "Explore_the_marketplace_to_find_awesome_apps": "Utforsk Marketplace for å finne fantastiske apper for Rocket.Chat", + "Export": "Eksporter", + "End_Call": "Avslutt samtale", "End_OTR": "Avslutt OTR", + "Ensure_secure_workspace_access": "Sørg for sikker tilgang til arbeidsområdet", + "Enter_a_custom_message": "Skriv inn en egendefinert melding", + "Enter_a_name": "Skriv inn et navn", "Enter_a_regex": "Skriv inn en regex", "Enter_a_room_name": "Skriv inn et romnavn", "Enter_a_username": "Skriv inn et brukernavn", @@ -1155,38 +1699,68 @@ "Enter_authentication_code": "Skriv inn autentiseringskode", "Enter_Behaviour": "Skriv inn nøkkeladferd", "Enter_Behaviour_Description": "Dette endres hvis enter-tasten sender en melding eller gjør en linjeskift", + "Enter_E2E_password": "Skriv inn Ende-Til-Ende-passord", "Enter_name_here": "Skriv inn navn her", "Enter_Normal": "Normal modus (send med Enter)", "Enter_to": "Skriv inn til", + "Enter_your_E2E_password": "Skriv inn ditt Ende-Til-Ende-passord", + "Enter_your_password_to_delete_your_account": "Skriv inn ditt passord for å slette kontoen din. Dette kan ikke angres.", + "Enter_your_username_to_delete_your_account": "Skriv inn ditt brukernavn for å slette kontoen. Dette kan ikke angres.", + "Premium_License": "Premium-lisens", + "Premium_License_alert": "Hvis en lisens fjernes, må arbeidsområdet startes på nytt for å tre i kraft.
Hvis arbeidsområdet er koblet til skyen, bør lisensen kanselleres der først, ellers vil skyen gi lisensen til arbeidsområdet igjen under omstart.", + "Premium_only": "Kun premium", "Entertainment": "Underholdning", "Error": "Feil", + "Error_something_went_wrong": "Oops! Noe gikk galt. Last inn siden på nytt eller kontakt en administrator.", "Error_404": "Feil: 404", "Error_changing_password": "Feil ved endring av passord", "Error_loading_pages": "Feil ved lasting av sider", + "Error_login_blocked_for_ip": "Innlogging er midlertidig blokkert for denne IP-adressen", + "Error_login_blocked_for_user": "Innlogging er midlertidig blokkert for denne brukeren", "Error_RocketChat_requires_oplog_tailing_when_running_in_multiple_instances": "Feil: Rocket.Chat krever oplog tailing når du kjører i flere tilfeller", "Error_RocketChat_requires_oplog_tailing_when_running_in_multiple_instances_details": "Sørg for at MongoDB er på ReplicaSet-modus og MONGO_OPLOG_URL miljøvariabel er definert riktig på applikasjonsserveren", + "Error_Site_URL": "Ugyldig Site_Url", "error-action-not-allowed": "{{action}} er ikke tillatt", "error-application-not-found": "Søknad ikke funnet", "error-archived-duplicate-name": "Det er en arkivert kanal med navn '{{room_name}}'", "error-avatar-invalid-url": "Ugyldig avatar URL: {{url}}", "error-avatar-url-handling": "Feil under behandling av avatarinnstilling fra en URL ({{url}}) for {{username}}", + "error-business-hour-finish-time-before-start-time": "Sluttid må være etter starttid", + "error-business-hour-finish-time-equals-start-time": "Start- og sluttid kan ikke være det samme", + "error-blocked-username": "**{{field}}** er blokkert og kan ikke brukes!", + "error-cannot-delete-app-user": "Sletting av appbruker er ikke tillatt. Avinstaller den tilsvarende appen for å fjerne den.", "error-cant-invite-for-direct-room": "Kan ikke invitere brukeren til å lede rom", "error-channels-setdefault-is-same": "Kanalinnstillingen er den samme som hva den ville bli endret til.", "error-channels-setdefault-missing-default-param": "BodyParam 'standard' er påkrevd", "error-could-not-change-email": "Kunne ikke endre e-post", "error-could-not-change-name": "Kunne ikke endre navn", "error-could-not-change-username": "Kunne ikke endre brukernavn", + "error-comment-is-required": "Kommentar er påkrevd", + "error-custom-field-name-already-exists": "Det egendefinerte feltnavnet er allerede i bruk", "error-delete-protected-role": "Kan ikke slette en beskyttet rolle", "error-department-not-found": "Avdeling ikke funnet", "error-direct-message-file-upload-not-allowed": "Fildeling ikke tillatt i direkte meldinger", "error-duplicate-channel-name": "En kanal med navn '{{channel_name}}' eksisterer", + "error-duplicate-priority-name": "En prioritet med samme navn finnes allerede", "error-edit-permissions-not-allowed": "Redigering av tillatelser er ikke tillatt", "error-email-domain-blacklisted": "E-postdomenet er svartelistet", + "error-email-body-not-initialized": "E-postteksten er ikke initialisert. Konfigurer e-postens topp- og bunntekst på e-postinnstillinger før du sender omfattende e-poster", "error-email-send-failed": "Feil ved å prøve å sende e-post: {{message}}", + "error-essential-app-disabled": "Feil: en Rocket.Chat-app som er avgjørende for dette er deaktivert. Kontakt administratoren din", + "error-failed-to-delete-department": "Kunne ikke slette avdelingen", "error-field-unavailable": "{{field}} er allerede i bruk :(", "error-file-too-large": "Filen er for stor", + "error-forwarding-chat": "Noe gikk galt under videresending av chatten. Prøv igjen senere.", + "error-forwarding-chat-same-department": "Den valgte avdelingen og den aktuelle romavdelingen er like", + "error-forwarding-department-target-not-allowed": "Videresending til målavdelingen er ikke tillatt.", + "error-guests-cant-have-other-roles": "Gjestebrukere kan ikke ha andre roller.", + "error-import-file-extract-error": "Kunne ikke pakke ut importfilen.", + "error-import-file-is-empty": "Importert fil ser ut til å være tom.", + "error-import-file-missing": "Filen som skal importeres ble ikke funnet på den angitte plassen.", "error-importer-not-defined": "Importøren ble ikke definert riktig, det mangler importklassen.", "error-input-is-not-a-valid-field": "{{input}} er ikke gyldig {{field}}", + "error-insufficient-permission": "Feil! Du har ikke ' {{permission}} ' tillatelsen, som er nødvendig for å utføre denne handlingen", + "error-invalid-account": "Ugyldig konto", "error-invalid-actionlink": "Ugyldig handlingskobling", "error-invalid-arguments": "Ugyldige argumenter", "error-invalid-asset": "Ugyldig ressurs", @@ -1194,11 +1768,15 @@ "error-invalid-channel-start-with-chars": "Ugyldig kanal. Start med @ eller #", "error-invalid-custom-field": "Ugyldig tilpasset felt", "error-invalid-custom-field-name": "Ugyldig egendefinert feltnavn. Bruk bare bokstaver, tall, bindestreker og understreker.", + "error-invalid-custom-field-value": "Ugyldig verdi for {{field}}-feltet", "error-invalid-date": "Ugyldig dato oppgitt.", + "error-invalid-dates": "\"Fra-dato\" kan ikke være etter \"til-dato\"", "error-invalid-description": "Ugyldig beskrivelse", "error-invalid-domain": "Ugyldig domene", "error-invalid-email": "Ugyldig e-post {{email}}", "error-invalid-email-address": "Ugyldig epostadresse", + "error-invalid-email-inbox": "Ugyldig e-postinnboks", + "error-email-inbox-not-found": "Finner ikke e-postinnboks", "error-invalid-file-height": "Ugyldig filhøyde", "error-invalid-file-type": "ugyldig filtype", "error-invalid-file-width": "Ugyldig filbredde", @@ -1208,7 +1786,11 @@ "error-invalid-method": "Ugyldig metode", "error-invalid-name": "Ugyldig navn", "error-invalid-password": "Ugyldig passord", + "error-invalid-param": "Ugyldig parameter", + "error-invalid-params": "Ugyldige parametere", "error-invalid-permission": "Ugyldig tillatelse", + "error-invalid-port-number": "Ugyldig portnummer", + "error-invalid-priority": "Ugyldig prioritet", "error-invalid-redirectUri": "Ugyldig omadresseringUri", "error-invalid-role": "Ugyldig rolle", "error-invalid-room": "Ugyldig rom", @@ -1221,9 +1803,14 @@ "error-invalid-urls": "Ugyldige nettadresser", "error-invalid-user": "Ugyldig bruker", "error-invalid-username": "Ugyldig brukernavn", + "error-invalid-value": "ugyldig verdi", "error-invalid-webhook-response": "Webhook-nettadressen reagerte med en annen status enn 200", + "error-license-user-limit-reached": "Maksimalt antall brukere er nådd.", "error-logged-user-not-in-room": "Du er ikke på rommet `%s`", + "error-max-departments-number-reached": "Du har nådd det maksimale antallet avdelinger som tillates av lisensen din. Kontakt sale@rocket.chat for en ny lisens.", + "error-max-guests-number-reached": "Du har nådd maksimalt antall gjestebrukere tillatt av lisensen din. Kontakt sale@rocket.chat for en ny lisens.", "error-max-number-simultaneous-chats-reached": "Det maksimale antallet samtidige chatter per agent er nådd.", + "error-max-rooms-per-guest-reached": "Maksimalt antall rom per gjest er nådd.", "error-message-deleting-blocked": "Meldingen slettet er blokkert", "error-message-editing-blocked": "Meldingsredigering er blokkert", "error-message-size-exceeded": "Meldingsstørrelsen overstiger Message_MaxAllowedSize", @@ -1231,7 +1818,10 @@ "error-no-tokens-for-this-user": "Det er ingen tokens for denne brukeren", "error-not-allowed": "Ikke tillatt", "error-not-authorized": "Ikke autorisert", + "Estimated_due_time": "Estimert forfallstid", + "error-password-in-history": "Oppgitt passord er tidligere brukt", "error-password-policy-not-met": "Passordet oppfyller ikke serverens retningslinjer", + "Estimated_due_time_in_minutes": "Estimert forfallstid (tid i minutter)", "error-password-policy-not-met-maxLength": "Passordet oppfyller ikke serverens policy med maksimal lengde (passord for lenge)", "error-password-policy-not-met-minLength": "Passordet oppfyller ikke serverens policy med minimumslengde (passord for kort)", "error-password-policy-not-met-oneLowercase": "Passordet oppfyller ikke serverens policy med minst en liten bokstav", @@ -1240,54 +1830,126 @@ "Please_go_to_the_Administration_page_then_Livechat_Facebook": "Gå til administrasjonssiden og deretter Livechat> Facebook", "error-password-policy-not-met-oneUppercase": "Passordet oppfyller ikke serverens policy med minst en stor bokstav", "error-password-policy-not-met-repeatingCharacters": "Passordet oppfyller ikke serverens retningslinjer for forbudte gjentatte tegn (du har for mange av de samme tegnene ved siden av hverandre)", + "error-password-same-as-current": "Inntastet passord er likt nåværende passord", + "error-personal-access-tokens-are-current-disabled": "Personlige tilgangstokener er for øyeblikket deaktivert", "error-push-disabled": "Push er deaktivert", "error-remove-last-owner": "Dette er den siste eieren. Vennligst sett inn en ny eier før du fjerner denne.", "error-role-in-use": "Kan ikke slette rolle fordi den er i bruk", "error-role-name-required": "Rolle navn er nødvendig", + "error-room-does-not-exist": "Dette rommet eksisterer ikke", + "error-role-already-present": "En rolle med dette navnet finnes allerede", + "error-room-already-closed": "Room er allerede stengt", "error-room-is-not-closed": "Rommet er ikke lukket", + "error-room-onHold": "Feil! Room er på vent", + "error-room-is-already-on-hold": "Feil! Room er allerede på vent", + "error-room-not-on-hold": "Feil! Room er ikke på vent", + "error-selected-agent-room-agent-are-same": "Den valgte agenten og romagenten er de samme", "error-the-field-is-required": "Feltet {{field}} er påkrevd.", "error-this-is-not-a-livechat-room": "Dette er ikke et Livechat-rom", + "error-this-is-a-premium-feature": "Dette er fra en premium-funksjon", + "error-token-already-exists": "Et token med dette navnet finnes alt", + "error-token-does-not-exists": "Tokenet finnes ikke", + "error-too-many-requests": "Feil, for mange forespørsler. Vennligst senke farten. Du må vente {{seconds}} sekunder før du prøver igjen.", + "error-user-deactivated": "Brukeren er ikke aktiv", "error-user-has-no-roles": "Brukeren har ingen roller", "error-user-is-not-activated": "Brukeren er ikke aktivert", + "error-user-is-offline": "Brukeren er frakoblet", "error-user-limit-exceeded": "Antall brukere du prøver å invitere til #kanalnavn overskrider grensen satt av administratoren", + "error-user-not-belong-to-department": "Bruker tilhører ikke denne avdelingen", "error-user-not-in-room": "Brukeren er ikke i dette rommet", "error-user-registration-disabled": "Brukerregistrering er deaktivert", "error-user-registration-secret": "Brukerregistrering er bare tillatt via hemmelig URL", + "error-no-owner-channel": "Bare eiere kan legge til denne kanalen i teamet", + "error-unable-to-update-priority": "Kan ikke oppdatere prioritering", "error-you-are-last-owner": "Du er den siste eieren. Vennligst sett inn ny eier før du forlater rommet.", + "error-saving-sla": "Det oppstod en feil under lagring av SLA", + "error-duplicated-sla": "En SLA med samme navn eller forfallstid eksisterer allerede", + "error-cannot-place-chat-on-hold": "Du kan ikke sette chatten på vent", + "error-unserved-rooms-cannot-be-placed-onhold": "Rommet kan ikke settes på vent før betjening", + "Workspace_exceeded_MAC_limit_disclaimer": "Arbeidsområdet har overskredet den månedlige grensen for aktive kontakter. Snakk med arbeidsområdeadministratoren din for å løse dette problemet.", + "You_do_not_have_permission_to_do_this": "Du har ikke tillatelse til å gjøre dette", + "You_do_not_have_permission_to_execute_this_command": "Du har ikke nødvendige tillatelser til å utføre kommandoen: `/{{command}}`", + "You_have_reached_the_limit_active_costumers_this_month": "Du har nådd grensen for aktive kunder denne måneden", + "Errors_and_Warnings": "Feil og advarsler", "Esc_to": "Esc til", + "Estimated_wait_time": "Beregnet ventetid", + "Event_notifications": "Hendelsesvarsler", "Event_Trigger": "Event Trigger", "Event_Trigger_Description": "Velg hvilken type hendelse som utløser denne Utgående WebHook-integrasjonen", "every_5_minutes": "En gang hvert 5. minutt", "every_10_seconds": "En gang hvert 10. sekund", + "every_30_seconds": "En gang hvert 30. sekund", + "every_10_minutes": "En gang hvert 10. minutt", "every_30_minutes": "En gang hvert 30. minutt", "every_day": "En gang hver dag", "every_hour": "En gang i timen", "every_minute": "En gang i minuttet", "every_second": "En gang hvert sekund", "every_six_hours": "En gang hver sjette time", + "every_12_hours": "En gang hver 12. time", + "every_24_hours": "En gang hver 24. timer", + "every_48_hours": "En gang hver 48. time", "Everyone_can_access_this_channel": "Alle kan få tilgang til denne kanalen", + "Exact": "Nøyaktig", "Example_s": "Eksempel: %s", "except_pinned": "(unntatt de som er festet)", "Exclude_Botnames": "Ekskluder Bots", "Exclude_Botnames_Description": "Ikke propagere meldinger fra bots hvis navn samsvarer med det vanlige uttrykket ovenfor. Hvis tomt er tomt, vil alle meldinger fra bots bli spredt.", "Exclude_pinned": "Ekskluder pinnede meldinger", "Execute_Synchronization_Now": "Utfør synkronisering nå", + "Exit_Full_Screen": "Avslutt fullskjerm", + "Expand": "Utvid", + "Experimental_Feature_Alert": "Dette er en eksperimentell funksjon! Vær oppmerksom på at den kan endres, gå i stykker eller til og med bli fjernet i fremtiden uten varsel.", + "Expired": "Utløpt", + "Expiration": "Utløp", + "Expiration_(Days)": "Utløp (dager)", + "Export_as_file": "Eksporter som fil", + "Export_Messages": "Eksporter meldinger", "Export_My_Data": "Eksporter mine data", + "expression": "Uttrykk", "Extended": "Utvidet", + "Extensions": "Utvidelser", + "Extension_Number": "Utvidelsesnummer", + "Extension_Status": "Utvidelsesstatus", + "External": "Ekstern", + "External_Domains": "Eksterne domener", "External_Queue_Service_URL": "Ekstern køtjeneste-URL", "External_Service": "Ekstern tjeneste", + "External_Users": "Eksterne brukere", + "Extremely_likely": "Ekstremt sannsynlig", + "Facebook": "Facebook", "Facebook_Page": "Facebook-side", + "Failed": "Mislyktes", + "Failed_to_activate_invite_token": "Kunne ikke aktivere invitasjonstoken", + "Failed_to_add_monitor": "Kunne ikke legge til monitor", + "Failed_To_Download_Files": "Kunne ikke laste ned filer", + "Failed_to_generate_invite_link": "Kunne ikke generere invitasjonslenke", "False": "Nei", "Favorite": "Favoritt", "Favorite_Rooms": "Aktiver favorittlokaler", "Favorites": "Favoritter", "Feature_Depends_on_Livechat_Visitor_navigation_as_a_message_to_be_enabled": "Denne funksjonen avhenger av \"Send besøkende navigasjonshistorikk som en melding\" for å være aktivert.", + "Federation_Example_matrix_server": "Eksempel: matrix.org", "FEDERATION_Domain": "Domene", + "FEDERATION_Public_Key": "Offentlig nøkkel", "FEDERATION_Status": "Status", "Retry_Count": "Prøv på nytt", + "Federation_Matrix_id": "AppService-ID", + "Federation_Matrix_hs_token": "Hjemmeserver-token", + "Federation_Matrix_as_token": "AppService-token", + "Federation_Matrix_homeserver_url": "Hjemmeserver-URL", + "Federation_Matrix_registration_file": "Registreringsfil", + "Federation_Matrix_giving_same_permission_warning": "Du gir denne brukeren de samme rettighetene som deg selv, du vil ikke kunne angre denne endringen. Vil du fortsette?", + "Federation_Matrix_losing_privileges": "Mister privilegier", + "Federation_Matrix_losing_privileges_warning": "Du vil ikke kunne angre denne handlingen, siden du nedgraderer deg selv. Hvis du er den siste privilegerte brukeren, vil du ikke kunne gjenvinne dette privilegiet. Ønsker du fortsatt å utføre handlingen?", + "Federation_Matrix_not_allowed_to_change_moderator": "Du har ikke lov til å endre moderator", + "Federation_Matrix_not_allowed_to_change_owner": "Du har ikke lov til å endre eier", + "Federation_Matrix_max_size_of_public_rooms_users_Alert": "Husk at jo større rommet du tillater brukere å bli med i, jo mer tid vil det ta å bli med i rommet, i tillegg til hvor mye ressurs det vil bruke. Les mer", "Field": "Felt", "Field_removed": "Felt fjernet", "Field_required": "Felt kreves", + "File": "Fil", + "File_Downloads_Started": "Filnedlastinger startet", "File_exceeds_allowed_size_of_bytes": "Filen overskrider tillatt størrelse på {{size}}.", "File_name_Placeholder": "Søk filer ...", "File_not_allowed_direct_messages": "Fildeling ikke tillatt i direkte meldinger.", @@ -1295,10 +1957,18 @@ "File_removed_by_prune": "Fil fjernet av beskjæring", "File_type_is_not_accepted": "Filtype er ikke akseptert.", "File_uploaded": "Fil opplastet", + "File_Upload_Disabled": "Filopplasting er deaktivert", + "File_URL": "Fil-URL", "files": "filer", "Files_only": "Bare fjern vedlagte filer, hold meldinger", + "FileSize_Bytes": "{{fileSize}} Bytes", + "FileSize_KB": "{{fileSize}} KB", + "FileSize_MB": "{{fileSize}} MB", "FileUpload": "Filopplasting", + "FileUpload_Cannot_preview_file": "Kan ikke forhåndsvise filen", "FileUpload_Disabled": "Filopplastinger er deaktivert.", + "FileUpload_Enable_json_web_token_for_files": "Aktiver Json Web Tokens-beskyttelse for filopplastinger", + "FileUpload_Restrict_to_room_members": "Begrens filer til rommenes medlemmer", "FileUpload_Enabled": "Filopplastinger aktivert", "FileUpload_Enabled_Direct": "Filopplastinger aktivert i direkte meldinger", "FileUpload_Error": "Filopplastingsfeil", @@ -1308,6 +1978,7 @@ "FileUpload_GoogleStorage_AccessId_Description": "Tilgangs-ID-en er vanligvis i et e-postformat, for eksempel: \"`example-test@example.iam.gserviceaccount.com`\"", "FileUpload_GoogleStorage_Bucket": "Google Storage Bucket Name", "FileUpload_GoogleStorage_Bucket_Description": "Navnet på bøtte som filene skal lastes opp til.", + "FileUpload_GoogleStorage_ProjectId": "Prosjekt-ID", "FileUpload_GoogleStorage_Proxy_Avatars": "Proxy Avatars", "FileUpload_GoogleStorage_Proxy_Avatars_Description": "Proxy-avatar-filoverføringer via serveren din i stedet for direkte tilgang til aktivets nettadresse", "FileUpload_GoogleStorage_Proxy_Uploads": "Proxy-opplastinger", @@ -1316,11 +1987,16 @@ "FileUpload_GoogleStorage_Secret_Description": "Vennligst følg [disse instruksjonene](https://github.com/CulturalMe/meteor-slingshot#google-cloud) og lim inn resultatet her.", "FileUpload_MaxFileSize": "Maksimal filopplastingsstørrelse (i byte)", "FileUpload_MaxFileSizeDescription": "Sett den til -1 for å fjerne begrensningen for filstørrelsen.", + "FileUpload_MediaType_NotAccepted__type__": "Medietypen er ikke akseptert: {{type}}", "FileUpload_MediaType_NotAccepted": "Medietyper ikke akseptert", + "FileUpload_MediaTypeBlackList": "Blokkerte medietyper", + "FileUpload_MediaTypeBlackListDescription": "Kommaseparert liste over medietyper. Denne innstillingen har prioritet over de aksepterte medietypene.", "FileUpload_MediaTypeWhiteList": "Godkjente medietyper", "FileUpload_MediaTypeWhiteListDescription": "Kommaseparert liste over medietyper. La det være tomt for å akseptere alle medietyper.", "FileUpload_ProtectFiles": "Beskytt opplastede filer", "FileUpload_ProtectFilesDescription": "Kun autentiserte brukere vil ha tilgang", + "FileUpload_RotateImages": "Roter bilder ved opplasting", + "FileUpload_RotateImages_Description": "Aktivering av denne innstillingen kan føre til tap av bildekvalitet", "FileUpload_S3_Acl": "Acl", "FileUpload_S3_AWSAccessKeyId": "Tilgangsnøkkel", "FileUpload_S3_AWSSecretAccessKey": "Hemmelig nøkkel", @@ -1347,10 +2023,17 @@ "FileUpload_Webdav_Upload_Folder_Path_Description": "WebDAV mappebane som filene skal lastes opp til", "FileUpload_Webdav_Username": "WebDAV Brukernavn", "Filter": "Filter", + "Filter_by_category": "Filtrer etter kategori", + "Filter_by_Custom_Fields": "Filtrer etter egendefinerte felt", + "Filter_By_Price": "Filtrer etter pris", + "Filter_By_Status": "Filtrer etter status", "Filters": "Filtre", "Financial_Services": "Finansielle tjenester", + "Finish": "Fullfør", + "Finish_Registration": "Fullfør registreringen", "First_Channel_After_Login": "Første kanal etter innlogging", "Flags": "Flags", + "Follow_message": "Følg melding", "Follow_social_profiles": "Følg våre sosiale profiler, gaffel oss på github og del dine tanker om rocket.chat-appen på vår trello bord.", "Fonts": "fonter", "Food_and_Drink": "Mat drikke", @@ -1364,35 +2047,78 @@ "Force_SSL_Description": "* Forsiktig! * _Force SSL_ skal aldri brukes med omvendt proxy. Hvis du har en omvendt proxy, bør du gjøre omadresseringen der. Dette alternativet finnes for distribusjoner som Heroku, som ikke tillater viderekoblingskonfigurasjonen på omvendt proxy.", "force-delete-message": "Tving slett melding", "force-delete-message_description": "Tillatelse til å slette en melding omgå alle begrensninger", + "Font_size": "Skriftstørrelse", "Forgot_password": "Glemt passordet", "Forgot_Password_Description": "Du kan bruke følgende plassholdere: \n - `[Forgot_Password_Url]` for URL-adressen for passordgjenoppretting. \n - [navn], [fname], [lname] for brukerens fulle navn, fornavn eller etternavn. \n - `[email]` for brukerens e-postadresse. \n - `[Site_Name]` og `[Site_URL]` for henholdsvis programnavnet og nettadressen.", "Forgot_Password_Email": "Klikk herfor å tilbakestille passordet ditt.", "Forgot_Password_Email_Subject": "[Site_Name] - Passordgjenoppretting", "Forgot_password_section": "Glemt passord", + "Format": "Format", "Forward": "Framover", "Forward_chat": "Videresend chat", + "Forward_message": "Videresend melding", "Forward_to_department": "Videresend til avdeling", "Forward_to_user": "Videresend til bruker", + "Forwarding": "Videresending", + "Free": "Gratis", + "Free_Apps": "Gratis-apper", "Frequently_Used": "Ofte brukt", "Friday": "fredag", "From": "Fra", "From_Email": "Fra e-post", "From_email_warning": "Advarsel: Feltet Fra er underlagt e-postserverinnstillingene dine.", + "Full_Name": "Fullt navn", + "Full_Screen": "Fullskjerm", "Gaming": "Gaming", "General": "Generell", + "General_Settings": "Generelle innstillinger", + "Generate_new_key": "Generer en ny nøkkel", + "Generate_New_Link": "Generer ny lenke", + "Generating_key": "Genererer nøkkel", + "Copy_link": "Kopier lenke", + "get-password-policy-forbidRepeatingCharacters": "Passordet bør ikke inneholde gjentakende tegn", + "get-password-policy-forbidRepeatingCharactersCount": "Passordet bør ikke inneholde mer enn {{forbidRepeatingCharactersCount}} gjentatte tegn", + "get-password-policy-maxLength": "Passordet bør maksimalt inneholde {{maxLength}} tegn", + "get-password-policy-minLength": "Passordet bør inneholde minst {{minLength}} tegn", + "get-password-policy-mustContainAtLeastOneLowercase": "Passordet bør inneholde minst én liten bokstav", + "get-password-policy-mustContainAtLeastOneNumber": "Passordet bør inneholde minst ett tall", + "get-password-policy-mustContainAtLeastOneSpecialCharacter": "Passordet bør inneholde minst ett spesialtegn", + "get-password-policy-mustContainAtLeastOneUppercase": "Passordet bør inneholde minst én stor bokstav", + "get-password-policy-minLength-label": "Minst {{limit}} tegn", + "get-password-policy-maxLength-label": "Maks {{limit}} tegn", + "get-password-policy-forbidRepeatingCharactersCount-label": "Maks. {{limit}} gjentatte tegn", + "get-password-policy-mustContainAtLeastOneLowercase-label": "Minst én liten bokstav", + "get-password-policy-mustContainAtLeastOneUppercase-label": "Minst en stor bokstav", + "get-password-policy-mustContainAtLeastOneNumber-label": "Minst ett tall", + "get-password-policy-mustContainAtLeastOneSpecialCharacter-label": "Minst ett symbol", + "get-server-info": "Hente serverinformasjon", + "get-server-info_description": "Tillatelse til å hente serverinformasjon", "github_no_public_email": "Du har ingen e-post som offentlig e-post i din GitHub-konto", + "github_HEAD": "HEAD", "Give_a_unique_name_for_the_custom_oauth": "Gi et unikt navn til den egendefinerte oauth", "strike": "streik", "Give_the_application_a_name_This_will_be_seen_by_your_users": "Gi søknaden et navn. Dette vil bli sett av brukerne.", "Global": "Global", + "Global Policy": "Global retningslinje ", "Global_purge_override_warning": "En global retensjonspolitikk er på plass. Hvis du lar \"Overstyr global retensjonspolitikk\" av, kan du bare bruke en policy som er strengere enn den globale politikken.", "Global_Search": "Globalt søk", "Go_to_your_workspace": "Gå til arbeidsområdet ditt", + "Go_to_accessibility_and_appearance": "Gå til tilgjengelighet og utseende", + "Google_Meet_Premium_only": "Google Meet (kun Premium)", + "Google_Play": "Google Play", + "Hold_Call": "Sett samtale på vent", + "Hold_Call_Premium_only": "Sett samtale på vent (kun Premium)", "GoogleCloudStorage": "Google Cloud Storage", "GoogleNaturalLanguage_ServiceAccount_Description": "Tjenestekonto-nøkkel JSON-fil. Mer informasjon finner du her [https://cloud.google.com/natural-language/docs/common/auth#set_up_a_service_account)", "GoogleTagManager_id": "Google Tag Manager ID", "Government": "Regjering", + "Graphql_CORS": "GraphQL CORS", + "Graphql_Enabled": "GraphQL aktivert", + "Graphql_Subscription_Port": "GraphQL abonnementsport", + "Grid_view": "Rutenett visning", "Snippet_Messages": "Utskriftsmeldinger", + "Group": "Gruppe", + "Group_by": "Grupper etter", "Group_by_Type": "Gruppe etter type", "snippet-message": "Utskriftsmelding", "snippet-message_description": "Tillatelse til å opprette tekstmelding", @@ -1400,41 +2126,63 @@ "Group_favorites": "Gruppe favoritter", "Group_mentions_disabled_x_members": "Gruppe nevner `@ alle` og` @ her` har blitt deaktivert for rom med flere enn {{total}} medlemmer.", "Group_mentions_only": "Gruppe nevner bare", + "Grouping": "Gruppering", + "Guest": "Gjest", "Hash": "hash", "Header": "Overskrift", "Header_and_Footer": "Topptekst og bunntekst", + "Pharmaceutical": "Farmasøytisk", + "Healthcare": "Helsevesen", "Helpers": "Hjelpere", + "Here_is_your_authentication_code": "Her er din autentiseringskode:", "Hex_Color_Preview": "Hex-fargeforhåndsvisning", + "Hi": "Hei", + "Hi_username": "Hei [navn]", "Hidden": "skjult", "Hide": "Skjul rom", "Hide_counter": "Skjul teller", "Hide_flextab": "Skjul høyre sidefelt med klikk", "Hide_Group_Warning": "Er du sikker på at du vil gjemme gruppen \"%s\"?", "Hide_Livechat_Warning": "Er du sikker på at du vil gjemme livechat med \"%s\"?", + "Hide_On_Workspace": "Skjul på arbeidsområdet", "Hide_Private_Warning": "Er du sikker på at du vil gjemme diskusjonen med \"%s\"?", "Hide_roles": "Skjul roller", "Hide_room": "Skjul rom", "Hide_Room_Warning": "Er du sikker på at du vil gjemme rommet \"%s\"?", + "Hide_System_Messages": "Skjul systemmeldinger", "Hide_Unread_Room_Status": "Skjul ulest romstatus", "Hide_usernames": "Skjul brukernavn", + "Hide_video": "Skjul video", + "High": "Høy", + "Highest": "Høyest", "Highlights": "Høydepunkter", "Highlights_How_To": "For å bli varslet når noen nevner et ord eller en setning, legg den til her. Du kan skille ord eller setninger med kommaer. Høydeord Ordene er ikke sosialfølsomme.", "Highlights_List": "Fremhev ord", "History": "Historie", + "Hold_Premium_only": "Vent (kun Premium-abonnement)", "Home": "Hjem", + "Homepage": "Hjemmeside", "Host": "Vert", "hours": "timer", "Hours": "timer", + "How_and_why_we_collect_usage_data": "Hvordan og hvorfor bruksdata samles inn", "How_friendly_was_the_chat_agent": "Hvor vennlig var chatteagenten?", "How_knowledgeable_was_the_chat_agent": "Hvor kunnskapsrik var chatagenten?", "How_long_to_wait_after_agent_goes_offline": "Hvor lenge å vente etter agent går offline", "How_responsive_was_the_chat_agent": "Hvor responsiv var chatagenten?", "How_satisfied_were_you_with_this_chat": "Hvor fornøyd var du med denne chatten?", "How_to_handle_open_sessions_when_agent_goes_offline": "Slik håndterer du åpne økter når agent går utenom", + "Http_timeout": "HTTP-tidsavbrudd (i millisekunder)", + "Http_timeout_value": "5000", + "HTML": "HTML", + "Icon": "Ikon", + "I_Saved_My_Password": "Jeg har lagret passordet mitt", "Idle_Time_Limit": "Inaktiv tidsbegrensning", "Idle_Time_Limit_Description": "Periode til status endres til vekk. Verdien må være i sekunder.", "if_they_are_from": "(hvis de er fra %s)", "If_this_email_is_registered": "Hvis denne e-posten er registrert, sender vi instruksjoner om hvordan du tilbakestiller passordet ditt. Hvis du ikke mottar en epost, vennligst kom tilbake og prøv igjen.", + "If_you_didnt_ask_for_reset_ignore_this_email": "Hvis du ikke har bedt om å tilbakestille passordet ditt, kan du ignorere denne e-posten.", + "If_you_didnt_try_to_login_in_your_account_please_ignore_this_email": "Hvis du ikke prøvde å logge på kontoen din, kan du ignorere denne e-posten.", "Iframe_Integration": "Iframe Integrasjon", "Iframe_Integration_receive_enable": "Aktiver mottak", "Iframe_Integration_receive_enable_Description": "Tillat foreldrevinduet å sende kommandoer til Rocket.Chat.", @@ -1446,12 +2194,15 @@ "Iframe_Integration_send_target_origin_Description": "Opprinnelse med protokollprefikset, hvilke kommandoer sendes til f.eks. 'https: // localhost', eller * for å tillate sending til hvor som helst.", "Ignore": "Overse", "Ignored": "ignorert", + "Ignore_Two_Factor_Authentication": "Ignorer tofaktorautentisering", + "Images": "Bilder", "IMAP_intercepter_already_running": "IMAP-intercepter kjører allerede", "IMAP_intercepter_Not_running": "IMAP-intercepter Ikke kjører", "Impersonate_next_agent_from_queue": "Legg til neste agent fra køen", "Impersonate_user": "Forsink brukeren", "Impersonate_user_description": "Når aktivert, integrering innlegg som brukeren som utløste integrasjon", "Import": "Import", + "Import_New_File": "Importer ny fil", "Importer_Archived": "arkivert", "Importer_CSV_Information": "CSV-importøren krever et bestemt format, vennligst les dokumentasjonen for hvordan du strukturerer zip-filen din:", "Importer_done": "Importerer komplett!", @@ -1462,6 +2213,7 @@ "Importer_import_cancelled": "Import avbrutt.", "Importer_import_failed": "Det oppsto en feil under kjøring av importen.", "Importer_importing_channels": "Importerer kanalene.", + "Importer_importing_files": "Importerer filene.", "Importer_importing_messages": "Importerer meldingene.", "Importer_importing_started": "Starter importen.", "Importer_importing_users": "Importerer brukerne.", @@ -1475,30 +2227,69 @@ "Importer_setup_error": "Det oppsto en feil under oppsett av importøren.", "Importer_Slack_Users_CSV_Information": "Filen som lastes opp må være Slack's Users-eksportfil, som er en CSV-fil. Se her for mer informasjon:", "Importer_Source_File": "Kildefilvalg", + "importer_status_done": "Fullført", + "importer_status_downloading_file": "Laster ned fil", + "importer_status_file_loaded": "Filen er lastet inn", "importer_status_finishing": "Nesten ferdig", + "importer_status_import_cancelled": "Avbrutt", "importer_status_import_failed": "Feil", + "importer_status_importing_channels": "Importerer kanaler", + "importer_status_importing_files": "Importerer filer", + "importer_status_importing_messages": "Importerer meldinger", + "importer_status_importing_started": "Importerer data", + "importer_status_importing_users": "Importerer brukere", + "importer_status_new": "Ikke startet", + "importer_status_preparing_started": "Leser filer", + "importer_status_preparing_users": "Leser brukerfil", + "importer_status_uploading": "Laster opp fil", + "importer_status_user_selection": "Klar til å velge hva som skal importeres", + "Importer_Upload_FileSize_Message": "Serverinnstillingene dine tillater opplasting av filer i alle størrelser opptil {{maxFileSize}}.", + "Importer_Upload_Unlimited_FileSize": "Deres serverinnstillinger tillater opplasting av filer i alle størrelser.", + "Importing_channels": "Importerer kanaler", + "Importing_Data": "Importerer data", + "Importing_messages": "Importerer meldinger", + "Importing_users": "Importerer brukere", + "Inactivity_Time": "Inaktivitetstid", + "In_progress": "Pågår", + "inbound-voip-calls": "Innkommende VoIP-anrop", + "inbound-voip-calls_description": "Tillatelse til innkommende VoIP-samtaler", + "Inbox_Info": "Innboks info", + "Include_Offline_Agents": "Inkluder frakoblede agenter", "Inclusive": "Inklusive", + "Incoming": "Innkommende", + "Incoming_call_from": "Innkommende anrop fra", "Incoming_Livechats": "Innkommende Livechats", "Incoming_WebHook": "Innkommende WebHook", "Industry": "Industri", + "Info": "Info", "initials_avatar": "Initialer Avatar", + "Inline_code": "Innline-kode", + "Install": "Installer", + "Install_anyway": "Installer allikevel ", "Install_Extension": "Installer utvidelse", "Install_FxOs": "Installer Rocket.Chat på din Firefox", "Install_FxOs_done": "Flott! Du kan nå bruke Rocket.Chat via ikonet på startskjermen. Ha det gøy med Rocket.Chat!", "Install_FxOs_error": "Beklager, det fungerte ikke som ønsket! Følgende feil oppstod:", "Install_FxOs_follow_instructions": "Vennligst bekreft appinstallasjonen på enheten din (trykk på \"Installer\" når du blir bedt om det).", + "Installing": "Installerer", "Install_package": "Installer pakken", "Installation": "Installasjon", + "Installed": "Installert", "Installed_at": "Installert på", "Instance_Record": "Instans Record", + "Instructions": "Instruksjoner", "Instructions_to_your_visitor_fill_the_form_to_send_a_message": "Instruksjoner til din besøkende fyller skjemaet for å sende en melding", + "Insert_Contact_Name": "Skriv inn kontaktnavn", + "Install_rocket_chat_on_your_preferred_desktop_platform": "Installer Rocket.Chat på din foretrukne skrivebordsplattform.", "Insurance": "Forsikring", "Integration_added": "Integrasjon er lagt til", "Integration_Advanced_Settings": "Avanserte innstillinger", + "Integration_Delete_Warning": "Sletting av en integrasjon kan ikke angres.", "Integration_disabled": "Integrasjon deaktivert", "Integration_History_Cleared": "Integrasjonshistorikk ble vellykket", "Integration_Incoming_WebHook": "Innkommende WebHook Integrasjon", "Integration_New": "Ny integrasjon", + "integration-scripts-disabled": "Integrasjonsskript er deaktivert", "Integration_Outgoing_WebHook": "Utgående WebHook Integrasjon", "Integration_Outgoing_WebHook_History": "Utgående WebHook Integration History", "Integration_Outgoing_WebHook_History_Data_Passed_To_Trigger": "Data bestått til integrasjon", @@ -1523,7 +2314,7 @@ "Integration_updated": "Integrasjon har blitt oppdatert.", "Integration_Word_Trigger_Placement": "Ordplassering hvor som helst", "Integration_Word_Trigger_Placement_Description": "Bør Ordet bli utløst når det plasseres hvor som helst i setningen annet enn begynnelsen?", - "Integrations": "integrasjoner", + "Integrations": "Integrasjoner", "Integrations_for_all_channels": "Skriv inn all_public_channels for å lytte på alle offentlige kanaler, all_private_groups for å lytte på alle private grupper, og all_direct_messages for å lytte til alle direkte meldinger.", "Integrations_Outgoing_Type_FileUploaded": "Filopplastet", "Integrations_Outgoing_Type_RoomArchived": "Rom arkivert", @@ -1545,10 +2336,14 @@ "Invalid_Department": "Ugyldig avdeling", "Invalid_email": "E-posten som er oppgitt, er ugyldig", "Invalid_Export_File": "Filen lastet opp er ikke en gyldig%s eksportfil.", + "Invalid_field": "Feltet må fylles ut", "Invalid_Import_File_Type": "Ugyldig import filtype.", "Invalid_name": "Navnet må ikke være tomt", "Invalid_notification_setting_s": "Ugyldig varslingsinnstilling:%s", + "Invalid_OAuth_client": "Ugyldig OAuth-klient", + "Invalid_or_expired_invite_token": "Ugyldig eller utløpt invitasjonstoken", "Invalid_pass": "Passordet må ikke være tomt", + "Invalid_password": "Ugyldig passord", "Invalid_reason": "Grunnen til å bli med må ikke være tom", "Invalid_room_name": "%s er ikke et gyldig romnavn", "Invalid_secret_URL_message": "Nettadressen som er oppgitt, er ugyldig.", @@ -1563,10 +2358,19 @@ "Invitation_HTML_Default": "

Du har blitt invitert til [Site_Name]

Gå til [Site_URL] og prøv den beste open source chat-løsningen tilgjengelig i dag!

", "Invitation_Subject": "Invitasjonsfag", "Invitation_Subject_Default": "Du har blitt invitert til [Site_Name]", + "Invite": "Invitasjon", + "Invites": "Invitasjoner", + "Invite_and_add_members_to_this_workspace_to_start_communicating": "Inviter og legg til medlemmer i dette arbeidsområdet for å begynne å kommunisere.", + "Invite_Link": "Invitasjonslenke", + "link": "lenke", + "Invite_link_generated": "Invitasjonslenken er generert", + "Invite_removed": "Invitasjonen ble fjernet", "Invite_user_to_join_channel": "Be en bruker til å bli med på denne kanalen", "Invite_user_to_join_channel_all_from": "Inviter alle brukere fra [#kanal] for å bli med på denne kanalen", "Invite_user_to_join_channel_all_to": "Inviter alle brukere fra denne kanalen til å delta i [#kanal]", "Invite_Users": "Invitere brukere", + "IP": "IP", + "IP_Address": "IP-adresse", "IRC_Channel_Join": "Output av kommandoen JOIN.", "IRC_Channel_Leave": "Output av DEL-kommandoen.", "IRC_Channel_Users": "Output av kommandoen NAMES.", @@ -1575,6 +2379,7 @@ "IRC_Enabled": "Forsøk på å integrere IRC-støtte. Hvis du endrer denne verdien, må du starte Rocket.Chat på nytt.", "IRC_Enabled_Alert": "IRC Support er et pågående arbeid. Bruk på et produksjonssystem anbefales ikke på dette tidspunktet.", "IRC_Federation": "IRC-føderasjonen", + "IRC_Federation_Description": "Koble til andre IRC-servere.", "IRC_Federation_Disabled": "IRC-føderasjonen er deaktivert.", "IRC_Hostname": "IRC-vertsserveren for å koble til.", "IRC_Login_Fail": "Output på en mislykket forbindelse til IRC-serveren.", @@ -1589,23 +2394,37 @@ "IssueLinks_LinkTemplate": "Mal for utgavekoblinger", "IssueLinks_LinkTemplate_Description": "Mal for utgavekoblinger; %s vil bli erstattet av problemnummeret.", "It_works": "Det fungerer", + "It_Security": "IT-ikkerhet", + "Italic": "Kursiv", "italics": "kursiv", + "Items_per_page:": "Elementer per side:", "Job_Title": "Jobbtittel", "Join": "Bli med", + "Join_with_password": "Bli med med passord", "Join_audio_call": "Bli med på lydanrop", + "Join_call": "Bli med i samtalen", "Join_Chat": "Bli med på Chat", "Join_default_channels": "Bli med i standardkanaler", "Join_the_Community": "Bli med i Fellesskapet", "Join_the_given_channel": "Bli med på den oppgitte kanalen", + "Join_rooms": "Bli med rom", "Join_video_call": "Bli med på videoanrop", + "Join_my_room_to_start_the_video_call": "Bli med i rommet mitt for å starte videosamtalen", "join-without-join-code": "Bli med uten å delta koden", "join-without-join-code_description": "Tillatelse til å omgå tilkoblingskoden i kanaler med tilkoblingskode aktivert", "Joined": "Ble med", + "joined": "ble med", + "Joined_at": "Ble med klokken", + "JSON": "JSON", "Jump": "Hoppe", "Jump_to_first_unread": "Gå til første uleste", "Jump_to_message": "Hopp til meldingen", "Jump_to_recent_messages": "Hopp til siste meldinger", "Just_invited_people_can_access_this_channel": "Bare inviterte personer kan få tilgang til denne kanalen.", + "kick-user-from-any-c-room": "Kast ut bruker fra alle offentlige Channel", + "kick-user-from-any-c-room_description": "Tillatelse til å sparke ut brukere fra alle offentlige kanaler", + "kick-user-from-any-p-room": "Spark brukeren fra private Channel", + "kick-user-from-any-p-room_description": "Tillatelse til å sparke en bruker fra private kanaler", "Katex_Dollar_Syntax": "Tillat Dollar Syntax", "Katex_Dollar_Syntax_Description": "Tillat bruk av $ katex blokk $ $ og $ inline katex $ syntakser", "Katex_Enabled": "Katex Aktivert", @@ -1621,6 +2440,8 @@ "Keyboard_Shortcuts_Keys_5": "Kommando(eller Alt) + Høyre pil", "Keyboard_Shortcuts_Keys_6": "Kommando(eller Alt) + Pil ned", "Keyboard_Shortcuts_Keys_7": "Skift+ Skriv inn", + "Keyboard_Shortcuts_Keys_8": "Shift (eller Ctrl) + ESC", + "Keyboard_Shortcuts_Mark_all_as_read": "Merker alle meldinger (i alle kanaler) som lest", "Keyboard_Shortcuts_Move_To_Beginning_Of_Message": "Flytt til begynnelsen av meldingen", "Keyboard_Shortcuts_Move_To_End_Of_Message": "Flytt til slutten av meldingen", "Keyboard_Shortcuts_New_Line_In_Message": "Ny linje i meldingen komponerer inngang", @@ -1629,23 +2450,79 @@ "Knowledge_Base": "Kunnskapsbase", "Label": "Etiketten", "Language": "Språk", + "Language_Bulgarian": "Bulgarsk", + "Language_Chinese": "Kinesisk", + "Language_Czech": "Tsjekkisk", + "Language_Danish": "Dansk", + "Language_Dutch": "Nederlandsk", + "Language_English": "Engelsk", + "Language_Estonian": "Estisk", + "Language_Finnish": "Finsk", + "Language_French": "Fransk", + "Language_German": "Tysk", + "Language_Greek": "Gresk", + "Language_Hungarian": "Ungarsk", + "Language_Italian": "Italiensk", + "Language_Japanese": "Japansk", + "Language_Latvian": "Latvisk", + "Language_Lithuanian": "Litauisk", "Language_Not_set": "Ingen spesifikk", + "Language_Polish": "Polsk", + "Language_Portuguese": "Portugisisk", + "Language_Romanian": "Rumensk", + "Language_Russian": "Russisk", + "Language_Slovak": "Slovakisk", + "Language_Slovenian": "Slovensk", + "Language_Spanish": "Spansk", + "Language_Swedish": "Svensk", "Language_Version": "Engelsk versjon", + "Last_7_days": "Siste 7 dager", + "Last_15_days": "Siste 15 dager", + "Last_30_days": "Siste 30 dager", + "Last_90_days": "Siste 90 dager", + "Last_6_months": "Siste 6 måneder", + "Last_year": "I fjor", + "Last_active": "Sist aktiv", + "Last_Call": "Siste samtale", "Last_login": "Siste innlogging", "Last_Message": "Siste melding", "Last_Message_At": "Siste melding på", "Last_seen": "Sist sett", "Launched_successfully": "Lansert vellykket", "Layout": "Oppsett", - "Layout_Home_Body": "Hjemmekroppen", + "Layout_Login_Hide_Logo": "Skjul logo", + "Layout_Login_Hide_Logo_Description": "Skjul logoen på påloggingssiden.", + "Layout_Login_Hide_Title": "Skjul tittel", + "Layout_Login_Hide_Title_Description": "Skjul tittelen på påloggingssiden.", + "Layout_Login_Template": "Påloggingsmal", + "Layout_Login_Template_Description": "Tilpass utseendet til påloggingssiden.", + "Layout_Login_Template_Vertical": "Vertikal", + "Layout_Login_Template_Horizontal": "Horisontal", "Layout_Home_Title": "Hjemtittel", "Layout_Login_Terms": "Innloggingsvilkår", "Layout_Privacy_Policy": "Personvernerklæring", + "Layout_Home_Custom_Block_Visible": "Vis egendefinert innhold til hjemmesiden", + "Layout_Custom_Body_Only": "Kun vis tilpasset innhold", "Layout_Sidenav_Footer": "Sidebeskrivelse Footer", "Layout_Sidenav_Footer_description": "Footer størrelse er 260 x 70px", "Layout_Sidenav_Footer_Dark_description": "Footer størrelse er 260 x 70px", "Layout_Terms_of_Service": "Vilkår for bruk", "LDAP": "LDAP", + "LDAP_Documentation": "LDAP-dokumentasjon", + "LDAP_Connection": "Forbindelse", + "LDAP_Connection_Authentication": "Autentisering", + "LDAP_Connection_Encryption": "Kryptering", + "LDAP_Connection_successful": "LDAP-tilkoblingen var vellykket ", + "LDAP_Connection_Timeouts": "Tidsavbrudd", + "LDAP_UserSearch": "Brukersøk", + "LDAP_UserSearch_Filter": "Søkefilter", + "LDAP_UserSearch_GroupFilter": "Gruppefilter", + "LDAP_DataSync": "Datasynkronisering", + "LDAP_DataSync_DataMap": "Kartlegging", + "LDAP_DataSync_Avatar": "Avatar", + "LDAP_DataSync_Advanced": "Avansert synkronisering", + "LDAP_DataSync_Roles": "Synkroniser roller", + "LDAP_DataSync_Channels": "Synkroniser kanaler", "LDAP_Authentication": "Aktiver", "LDAP_Authentication_Password": "Passord", "LDAP_Authentication_UserDN": "Bruker DN", @@ -1658,14 +2535,17 @@ "LDAP_Background_Sync_Interval_Description": "Intervallet mellom synkroniseringer. Eksempel \"hver 24. time\" eller \"på den første dagen i uken\", flere eksempler på [Cron Text Parser] (http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Bakgrunnssynkronisering Oppdater eksisterende brukere", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Vil synkronisere avataren, feltene, brukernavnet, etc (basert på konfigurasjonen) av alle brukere som allerede er importert fra LDAP på hver ** Sync Interval **", + "LDAP_Background_Sync_Merge_Existent_Users_Description": "Vil slå sammen alle brukere (basert på dine filterkriterier) som finnes i LDAP og også finnes i Rocket.Chat. For å aktivere dette, aktiver \"Slå sammen eksisterende brukere\"-innstillingen i kategorien Datasynkronisering.", "LDAP_BaseDN": "Base DN", "LDAP_BaseDN_Description": "Det fullt kvalifiserte Distinguished Name (DN) av en LDAP-subtree du vil søke etter brukere og grupper. Du kan legge til så mange som du liker; Hver gruppe må imidlertid defineres i samme domenebase som brukerne som tilhører den. Eksempel: `ou = Brukere + ou = Prosjekter, dc = Eksempel, dc = com`. Hvis du angir begrensede brukergrupper, er det bare brukere som tilhører disse gruppene. Vi anbefaler at du angir toppnivået til LDAP-katalogtreet ditt som domenebase og bruk søkefilter for å kontrollere tilgangen.", "LDAP_CA_Cert": "CA Cert", "LDAP_Connect_Timeout": "Tilkoblingstidsavbrudd (ms)", + "LDAP_DataSync_AutoLogout": "Automatisk utlogging av deaktiverte brukere", "LDAP_Default_Domain": "Standard domenenavn", "LDAP_Default_Domain_Description": "Hvis det leveres, vil standarddomenet brukes til å lage en unik e-post for brukere der e-post ikke ble importert fra LDAP. E-posten vil bli montert som `brukernavn@default_domai` eller`unique_id@default_domain`. \n Eksempel: `rocket.chat`", "LDAP_Enable": "Aktiver", "LDAP_Enable_Description": "Forsøk å bruke LDAP for autentisering.", + "LDAP_Enable_LDAP_Groups_To_RC_Teams": "Aktiver teamkartlegging fra LDAP til Rocket.Chat", "LDAP_Encryption": "kryptering", "LDAP_Encryption_Description": "Krypteringsmetoden brukes til å sikre kommunikasjon til LDAP-serveren. Eksempler er \"plain\" (ingen kryptering), `SSL / LDAPS` (kryptert fra starten) og` StartTLS` (oppgradering til kryptert kommunikasjon når den er tilkoblet).", "LDAP_Find_User_After_Login": "Finn bruker etter innlogging", @@ -1682,6 +2562,7 @@ "LDAP_Group_Filter_Group_Name_Description": "Gruppens navn som det tilhører brukeren", "LDAP_Group_Filter_ObjectClass": "Gruppe ObjectClass", "LDAP_Group_Filter_ObjectClass_Description": "Den *objektklasse* som identifiserer gruppene. \n f.eks. **OpenLDAP:** `groupOfUniqueNames`", + "LDAP_Groups_To_Rocket_Chat_Teams": "Teamkartlegging fra LDAP til Rocket.Chat.", "LDAP_Host": "Vert", "LDAP_Host_Description": "LDAP-verten, f.eks. `ldap.example.com` eller` 10.0.0.30`.", "LDAP_Idle_Timeout": "Idle Timeout (ms)", @@ -1694,6 +2575,7 @@ "LDAP_Merge_Existing_Users_Description": "* Forsiktig! * Når du importerer en bruker fra LDAP, og en bruker med samme brukernavn allerede eksisterer, blir LDAP-info og passord satt inn i den eksisterende brukeren.", "LDAP_Port": "Havn", "LDAP_Port_Description": "Port for å få tilgang til LDAP. f.eks .: `389` eller` 636` for LDAPS", + "LDAP_Prevent_Username_Changes": "Hindre LDAP-brukere fra å endre Rocket.Chat-brukernavnet sitt", "LDAP_Query_To_Get_User_Teams": "LDAP-spørring for å få brukergrupper", "LDAP_Reconnect": "koble", "LDAP_Reconnect_Description": "Prøv å koble til igjen automatisk når tilkoblingen avbrytes av en eller annen grunn mens du utfører operasjoner", @@ -1703,13 +2585,25 @@ "LDAP_Search_Page_Size_Description": "Maksimalt antall innføringer hver resultatside vil returnere for å bli behandlet", "LDAP_Search_Size_Limit": "Søk størrelsesgrense", "LDAP_Search_Size_Limit_Description": "Maksimalt antall oppføringer som skal returneres. \n **Oppmerksomhet** Dette nummeret skal være større enn **Søk på sidestørrelse**", + "LDAP_Sync_Custom_Fields": "Synkroniser egendefinerte felter", + "LDAP_Sync_AutoLogout_Enabled": "Aktiver automatisk utlogging", + "LDAP_Sync_AutoLogout_Interval": "Intervall for automatisk utlogging", "LDAP_Sync_Now": "Bakgrunnssynkronisering nå", "LDAP_Sync_Now_Description": "Vil utføre **Background Sync** nå i stedet for å vente **Sync Interval** selv om **Bakgrunnssynkronisering** er False. \n Denne handlingen er asynkron, se loggene for mer informasjon om prosess", + "LDAP_Sync_User_Active_State_Both": "Aktiver og deaktiver brukere", + "LDAP_Sync_User_Active_State_Disable": "Deaktiver brukere", + "LDAP_Sync_User_Active_State_Nothing": "Ikke gjør noe", "LDAP_Sync_User_Avatar": "Synkroniser User Avatar", + "LDAP_Sync_User_Data_Roles": "Synkroniser LDAP-grupper", + "LDAP_Sync_User_Data_Channels": "Automatisk synkroniser LDAP-grupper til kanaler", + "LDAP_Sync_User_Data_Channels_Admin": "Kanaladministrator", + "LDAP_Sync_User_Data_Roles_AutoRemove": "Fjern brukerroller automatisk", + "LDAP_Sync_User_Data_Roles_Filter": "Brukergruppefilter", "LDAP_Timeout": "Timeout (ms)", "LDAP_Timeout_Description": "Hvor mange milesekunder venter på et søkeresultat før du returnerer en feil", "LDAP_Unique_Identifier_Field": "Unikt identifikasjonsfelt", "LDAP_Unique_Identifier_Field_Description": "Hvilket felt vil bli brukt til å koble LDAP-brukeren og Rocket.Chat-brukeren. Du kan informere flere verdier adskilt av komma for å prøve å få verdien fra LDAP-posten. \n Standardverdien er `objectGUID,ibm-entryUUID,GUID,dominoUNID,nsuniqueId,uidNumber`", + "LDAP_User_Found": "LDAP-bruker funnet", "LDAP_User_Search_Field": "Søkefelt", "LDAP_User_Search_Field_Description": "LDAP-attributtet som identifiserer LDAP-brukeren som forsøker godkjenning. Dette feltet skal være `sAMAccountName` for de fleste Active Directory-installasjoner, men det kan være` uid` for andre LDAP-løsninger, for eksempel OpenLDAP. Du kan bruke `e-post 'til å identifisere brukere via e-post eller hva som helst attributt du vil. \n Du kan bruke flere verdier adskilt av komma for å tillate brukere å logge inn ved hjelp av flere identifikatorer som brukernavn eller e-post.", "LDAP_User_Search_Filter": "Filter", @@ -1717,9 +2611,19 @@ "LDAP_User_Search_Scope": "omfang", "LDAP_Username_Field": "Brukernavn felt", "LDAP_Username_Field_Description": "Hvilket felt vil bli brukt som *brukernavn* for nye brukere. Legg igjen tomt for å bruke brukernavnet informert på innloggingssiden. \n Du kan også bruke maltekoder, som `#{givenName}.#{Sn}`. \n Standardverdien er `sAMAccountName`.", + "LDAP_Username_To_Search": "Brukernavn å søke etter", "Lead_capture_email_regex": "Lead capture email regex", "Lead_capture_phone_regex": "Lead capture phone regex", + "Learn_more": "Lære mer", + "Learn_more_about_agents": "Finn ut mer om agenter", + "Learn_more_about_business_hours": "Finn ut mer om åpningstider", + "Learn_more_about_contacts": "Finn ut mer om kontakter", + "Learn_more_about_custom_fields": "Finn ut mer om egendefinerte felter", + "Learn_more_about_conversations": "Finn ut mer om samtaler", + "Learn_more_about_departments": "Lær mer om avdelinger", + "Learn_more_about_SLA_policies": "Lær mer om SLA-retningslinjer", "Leave": "Forlat rom", + "Leave_a_comment": "Legg igjen en kommentar", "Leave_Group_Warning": "Er du sikker på at du vil forlate gruppen \"%s\"?", "Leave_Livechat_Warning": "Er du sikker på at du vil forlate livechat med \"%s\"?", "Leave_Private_Warning": "Er du sikker på at du vil legge diskusjonen med \"%s\"?", @@ -1727,12 +2631,21 @@ "Leave_Room_Warning": "Er du sikker på at du vil forlate rommet \"%s\"?", "Leave_the_current_channel": "La den nåværende kanalen gå", "leave-c": "La kanaler", + "leave-c_description": "Tillatelse til å forlate kanaler", "leave-p": "Legg igjen private grupper", + "leave-p_description": "Tillatelse til å forlate private grupper", + "Let_them_know": "La dem vite", + "License": "Tillatelse", + "Line": "Linje", + "Link": "Lenke", "List_of_Channels": "Liste over kanaler", "List_of_Direct_Messages": "Liste over direkte meldinger", + "Livechat": "Livechat", "Livechat_agents": "Livechat agenter", "Livechat_Agents": "Agenter", + "Livechat_allow_manual_on_hold_Description": "Hvis aktivert, vil agenten få muligheten til å sette en chat på vent", "Livechat_AllowedDomainsList": "Livechat Tillatte Domener", + "Livechat_Appearance": "Livechat-utseende", "Livechat_Dashboard": "Livechat Dashboard", "Livechat_enabled": "Livechat aktivert", "Livechat_forward_open_chats": "Videresend åpne chatter", @@ -1740,8 +2653,12 @@ "Livechat_guest_count": "Gjesteteller", "Livechat_Inquiry_Already_Taken": "Livechat forespørsel allerede tatt", "Livechat_managers": "Livechat-ledere", + "Livechat_maximum_queue_wait_time": "Maksimal ventetid i kø", "Livechat_offline": "Livechat offline", + "Omnichannel_On_Hold_manually": "Chatten ble manuelt satt på vent av {{user}}", "Livechat_online": "Livechat online", + "Omnichannel_placed_chat_on_hold": "Chat på vent: {{comment}}", + "Omnichannel_hide_conversation_after_closing": "Skjul samtalen etter lukking", "Livechat_Queue": "Livechat Queue", "Livechat_registration_form": "Registreringsskjema", "Livechat_room_count": "Livechat Room Count", @@ -1749,6 +2666,12 @@ "Livechat_Take_Confirm": "Vil du ta denne klienten?", "Livechat_title": "Livechat-tittel", "Livechat_title_color": "Livechat-tittel Bakgrunnsfarge", + "Livechat_transfer_return_to_the_queue": "{{from}} satte chatten tilbake i køen", + "Livechat_transfer_to_agent": "{{from}} overførte chatten til {{to}}", + "Livechat_transfer_to_agent_with_a_comment": "{{from}} overførte chatten til {{to}} med en kommentar: {{comment}}", + "Livechat_transfer_to_agent_auto_transfer_unanswered_chat": "{{from}} overførte chatten til {{to}} siden den var ubesvart i {{duration}} sekunder", + "Livechat_transfer_to_department": "{{from}} overførte chatten til avdelingen {{to}}", + "Livechat_transfer_to_department_with_a_comment": "{{from}} overførte chatten til avdelingen {{to}} med en kommentar: {{comment}}", "Livechat_Users": "Livechat-brukere", "Livestream_close": "Lukk Livestream", "Livestream_enable_audio_only": "Aktiver kun lydmodus", @@ -1758,11 +2681,16 @@ "Livestream_switch_to_room": "Bytt til dagens romstrøm", "Livestream_url": "Livestream kilde URL", "Livestream_url_incorrect": "Livestream url er feil", + "Load_Balancing": "Lastbalansering", "Load_more": "Last mer", + "Loading": "Laster", "Loading_more_from_history": "Laster mer fra historien", "Loading_suggestion": "Laster inn forslag", "Loading...": "Laster inn ...", + "Local_Time": "Lokal tid", + "Local_Time_time": "Lokal tid: {{time}}", "Localization": "lokalisering", + "Location": "Lokasjon", "Log_Exceptions_to_Channel": "Log unntak fra kanal", "Log_Exceptions_to_Channel_Description": "En kanal som vil motta alle fanget unntak. La være tom for å ignorere unntak.", "Log_File": "Vis fil og linje", @@ -1775,12 +2703,16 @@ "Log_Trace_Subscriptions_Filter": "Spor abonnement filter", "Log_Trace_Subscriptions_Filter_Description": "Teksten her vil bli vurdert som RegExp (`ny RegExp ('text')`). Hold det tomt for å vise spor av hver samtale.", "Log_View_Limit": "Logggrense", - "Logged_out_of_other_clients_successfully": "Logget ut av andre klienter med hell", + "Logged_out_of_other_clients_successfully": "Logget ut av andre klienter", "Login": "Logg inn", "Login_with": "Logg inn med%s", "Logistics": "logistikk", "Logout": "Logg ut", "Logout_Others": "Logg ut fra andre logget på steder", + "Logout_Device": "Logg ut enhet", + "Logs": "Logger", + "Low": "Lav", + "Lowest": "Laveste", "Mail_Message_Invalid_emails": "Du har oppgitt en eller flere ugyldige e-poster:%s", "Mail_Message_Missing_to": "Du må velge en eller flere brukere eller gi en eller flere e-postadresser, skilt av kommaer.", "Mail_Message_No_messages_selected_select_all": "Du har ikke valgt noen meldinger", @@ -1819,26 +2751,39 @@ "MapView_Enabled_Description": "Aktivering av kartvisning vil vise en plasseringstasteknapp til venstre for chatinputfeltet.", "MapView_GMapsAPIKey": "Google Static Maps API-nøkkel", "MapView_GMapsAPIKey_Description": "Dette kan hentes fra Google Developers Console gratis.", + "Mark_all_as_read": "`%s` - Merk alle meldinger (i alle kanaler) som lest", "Mark_as_read": "Merk som lest", "Mark_as_unread": "Merk som ulest", "Mark_unread": "Merk som ulest", + "Marketplace_app_last_updated": "Sist oppdatert {{lastUpdated}}", "MAU_value": "MAU {{value}}", "Max_length_is": "Maks lengde er%s", "Max_number_of_chats_per_agent": "Maks antall samtidige chatter", "Max_number_of_chats_per_agent_description": "Maks antall samtidige chatter en agent kan delta i", + "Max_Retry": "Maksimalt antall forsøk på å koble til serveren på nytt", + "Maximum": "Maksimum", + "Maximum_number_of_guests_reached": "Maksimalt antall gjester er nådd", "Me": "Meg", "Media": "Media", "Medium": "Medium", + "Members": "Medlemmer", "Members_List": "Medlemsliste", "mention-all": "Nevne alt", "mention-all_description": "Tillatelse til å bruke @all nevne", + "Mentions_all_room_members": "Omtaler alle medlemmer av rommet", + "Mentions_online_room_members": "Omtaler alle påloggede medlemmer av rommet", + "Mentions_user": "Omtaler bruker", + "Mentions_channel": "Omtaler kanalen", + "Mentions_you": "Omtaler deg", "mention-here": "Nevn her", "mention-here_description": "Tillatelse til å bruke @here mention", "Mentions": "nevner", "Mentions_default": "Mentjoner (standard)", "Mentions_only": "Kun mentene", "Merge_Channels": "Flett kanaler", + "message": "melding", "Message": "Beskjed", + "Message_Description": "Konfigurer meldingsinnstillinger.", "Message_AllowBadWordsFilter": "Tillat melding om dårlig ord filtrering", "Message_AllowDeleting": "Tillat melding å slette", "Message_AllowDeleting_BlockDeleteInMinutes": "Blokker melding som slettes etter (n) minutter", @@ -1854,6 +2799,7 @@ "Message_AlwaysSearchRegExp": "Søk alltid med RegExp", "Message_AlwaysSearchRegExp_Description": "Vi anbefaler at du sier «True» hvis språket ikke støttes på [Søk etter MongoDB tekst](https://docs.mongodb.org/manual/reference/text-search-languages/#text-search-languages).", "Message_Attachments": "Melding Vedlegg", + "Message_with_attachment": "Melding med vedlegg", "Report_sent": "Rapport sendt", "Message_Audio": "Lydmelding", "Message_Audio_bitRate": "Lydmelding Bitrate", @@ -1873,11 +2819,18 @@ "Message_GlobalSearch": "Global søk", "Message_GroupingPeriod": "Grupperingstid (i sekunder)", "Message_GroupingPeriodDescription": "Meldinger vil bli gruppert med forrige melding hvis begge er fra samme bruker og den forløpte tiden var mindre enn informert tid i sekunder.", + "Message_has_been_edited": "Meldingen er redigert", + "Message_has_been_edited_at": "Meldingen ble redigert {{date}}", + "Message_has_been_edited_by": "Meldingen ble redigert av {{username}}", + "Message_has_been_edited_by_at": "Meldingen ble redigert av {{username}} den {{date}}", + "Message_has_been_forwarded": "Meldingen er videresendt", "Message_HideType_au": "Skjul \"User Added\" meldinger", "Message_HideType_mute_unmute": "Skjul \"User Muted / Unmuted\" meldinger", "Message_HideType_ru": "Skjul \"Bruker fjernet\" meldinger", "Message_HideType_uj": "Skjul \"User Join\" meldinger", "Message_HideType_ul": "Skjul \"User Leave\" meldinger", + "Message_HideType_wm": "Skjul velkomstmeldinger", + "Message_Id": "Meldings-ID", "Message_Ignored": "Denne meldingen ble ignorert", "Message_info": "Melding info", "Message_KeepHistory": "Behold beskjed om redigering av meldinger", @@ -1889,19 +2842,24 @@ "Message_Read_Receipt_Store_Users": "Detaljert Les kvitteringer", "Message_Read_Receipt_Store_Users_Description": "Viser hver brukers leserkvitteringer", "Message_removed": "Melding fjernet", + "Message_is_removed": "melding fjernet", "Message_sent_by_email": "Melding sendt via e-post", "Message_ShowDeletedStatus": "Vis slettet status", "Message_starring": "Melding med hovedrollen", + "Message_Time": "Meldingstid", "Message_TimeAndDateFormat": "Tid og datoformat", "Message_TimeAndDateFormat_Description": "Se også: [Moment.js](http://momentjs.com/docs/#/displaying/format/)", "Message_TimeFormat": "Tidsformat", "Message_TimeFormat_Description": "Se også: [Moment.js](http://momentjs.com/docs/#/displaying/format/)", "Message_too_long": "Melding for lenge", + "Message_UserId": "Bruker-ID", "Message_view_mode_info": "Dette endrer mengden plassmeldinger som tas opp på skjermen.", "Message_VideoRecorderEnabled": "Videoopptaker aktivert", "Message_VideoRecorderEnabledDescription": "Krever at video / webm-filer skal være en akseptert medietype i \"Filopplastings\" -innstillinger.", "messages": "meldinger", "Messages": "meldinger", + "Messages_selected": "Meldinger er valgt", + "Messages_sent": "Meldinger sendt", "Messages_that_are_sent_to_the_Incoming_WebHook_will_be_posted_here": "Meldinger som sendes til Incoming WebHook vil bli lagt ut her.", "Meta": "Meta", "Meta_custom": "Egendefinerte Meta Tags", @@ -1910,16 +2868,54 @@ "Meta_language": "Språk", "Meta_msvalidate01": "MSValidate.01", "Meta_robots": "roboter", + "meteor_status_connected": "Tilkoblet", + "meteor_status_connecting": "Kobler til...", + "meteor_status_failed": "Servertilkoblingen mislyktes", + "meteor_status_offline": "Frakoblet modus.", + "meteor_status_reconnect_in_other": "prøver igjen om {{count}} sekunder...", + "meteor_status_reconnect_in_one": "prøver igjen om {{count}} sekunder...", + "meteor_status_try_now_offline": "Koble til igjen", + "meteor_status_try_now_waiting": "Prøv nå", + "meteor_status_waiting": "Venter på serverforbindelse,", + "Method": "Metode", + "Mic_on": "Mikrofon på", + "Microphone": "Mikrofon", + "Microphone_access_not_allowed": "Mikrofontilgang var ikke tillatt, sjekk nettleserinnstillingene.", + "Mic_off": "Mikrofon av", "Min_length_is": "Min lengde er%s", + "Minimum": "Minimum", "Minimum_balance": "Minimumsbalanse", + "minute": "minutt", "minutes": "minutter", + "Missing_configuration": "Manglende konfigurasjon", "Mobex_sms_gateway_from_number": "Fra", "Mobex_sms_gateway_password": "Passord", "Mobex_sms_gateway_username": "Brukernavn", "Mobile": "Mobil", "Mobile_Push_Notifications_Default_Alert": "Standardvarsler for mobilvarsler", + "Moderation_Show_reports": "Vis rapporter", + "Moderation_Go_to_message": "Gå til melding", "Moderation_Delete_message": "Slett melding", + "Moderation_Dismiss_and_delete": "Avvis og slett", + "Moderation_Delete_this_message": "Slett meldingen", + "Moderation_Message_context_header": "Rapporterte melding(er)", + "Moderation_Action_View_reports": "Se rapporterte meldinger", + "Moderation_Hide_reports": "Skjul rapporter", + "Moderation_Deactivate_User": "Deaktiver bruker", + "Moderation_User_deactivated": "Bruker deaktivert", + "Moderation_Delete_all_messages": "Slett alle meldinger", + "Moderation_Duplicate_messages": "Dupliserte meldinger", + "Moderation_Report_reports": "Rapporter", + "Moderation_Reported_message": "Rapportert melding", + "Moderation_Message_already_deleted": "Meldingen er allerede slettet", + "Moderation_Reset_user_avatar": "Tilbakestill brukeravatar", + "Moderation_See_messages": "Se meldinger", + "Moderation_Avatar_reset_success": "Avatar tilbakestilt", + "Moderation_User_deleted_warning": "Brukeren som sendte meldingen(e) eksisterer ikke lenger eller er slettet.", "Monday": "mandag", + "Mongo_version": "Mongo versjon", + "MongoDB": "MongoDB", + "MongoDB_Deprecated": "MongoDB avviklet", "Monitor_history_for_changes_on": "Overvåk historikk for endringer på", "Monthly_Active_Users": "Månedlige aktive brukere", "More": "Mer", @@ -1927,18 +2923,25 @@ "More_direct_messages": "Flere direkte meldinger", "More_groups": "Flere private grupper", "More_unreads": "Flere diskusjoner", + "Most_recent_updated": "Sist oppdatert", "Move_beginning_message": "`%s` - Flytt til begynnelsen av meldingen", "Move_end_message": "`%s` - Flytt til slutten av meldingen", + "Move_queue": "Flytt til køen", "Msgs": "meld", "multi": "multi", + "Multi_line": "Flerlinje", + "Mute": "Demp", + "Mute_and_dismiss": "Demp og avvis", "Mute_all_notifications": "Slå av alle varsler", "Mute_Focused_Conversations": "Mute Fokuserte samtaler", "Mute_Group_Mentions": "Mute @all og @here nevner", "Mute_someone_in_room": "Stum på noen i rommet", "Mute_user": "Stopp brukeren", + "Mute_microphone": "Demp mikrofon", "mute-user": "Slå av brukeren", "mute-user_description": "Tillatelse til å dempe andre brukere i samme kanal", "Muted": "dempet", + "My Data": "Mine data", "My_Account": "Min konto", "My_location": "Min posisjon", "n_messages": "%s meldinger", @@ -1948,10 +2951,21 @@ "Name_of_agent": "Navn på agent", "Name_optional": "Navn (valgfritt)", "Name_Placeholder": "Vennligst skriv inn navnet ditt...", + "Navigation": "Navigasjon", + "Navigation_bar": "Navigasjonslinje", "Navigation_History": "Navigasjonshistorikk", + "Next": "Neste", + "Never": "Aldri", + "New": "Ny", "New_Application": "Ny applikasjon", + "New_Call": "Ny samtale", + "New_chat_in_queue": "Ny chat i kø", + "New_contact": "Ny kontakt", "New_Custom_Field": "Nytt tilpasset felt", "New_Department": "Ny avdeling", + "New_discussion": "Ny diskusjon", + "New_discussion_name": "Et meningsfylt navn for diskusjonsrommet", + "New_Email_Inbox": "Ny e-postinnboks", "New_integration": "Ny integrering", "New_line_message_compose_input": "`%s` - Ny linje i meldingen komponerer inngang", "New_logs": "Nye logger", @@ -1959,31 +2973,55 @@ "New_messages": "Nye meldinger", "New_password": "Nytt passord", "New_Password_Placeholder": "Vennligst oppgi nytt passord ...", + "New_SLA_Policy": "Ny SLA-retningslinje ", "New_role": "Ny rolle", "New_Room_Notification": "Nytt romvarsling", "New_Tag": "Ny tagg", "New_Trigger": "Ny utløser", "New_Unit": "Ny enhet", + "New_users": "Nye brukere", + "New_user": "Ny bruker", "New_version_available_(s)": "Ny versjon tilgjengelig (%s)", "New_videocall_request": "Ny videosamtaleforespørsel", "New_visitor_navigation": "Ny navigasjon: {{history}}", + "New_workspace_confirmed": "Nytt arbeidsområde bekreftet", + "New_workspace": "Nytt arbeidsområdet", "Newer_than": "Nyere enn", "Newer_than_may_not_exceed_Older_than": "\"Nyere enn\" kan ikke overstige \"Eldre enn\"", + "Nickname": "Kallenavn", + "Nickname_Placeholder": "Skriv inn kallenavnet ditt...", "No": "Nei", "No_available_agents_to_transfer": "Ingen tilgjengelige agenter for å overføre", "No_channels_yet": "Du er ikke en del av en kanal ennå", + "No_chats_yet": "Ingen chatter ennå", + "No_chats_yet_description": "Alle chattene dine vises her.", + "No_calls_yet": "Ingen anrop enda", + "No_calls_yet_description": "Alle dine anrop vil vises her.", + "No_contacts_yet": "Ingen kontakter enda", + "No_contacts_yet_description": "Alle kontakter vil vises her.", + "No_custom_fields_yet": "Foreløpig ingen egendefinerte felter", + "No_departments_yet": "Enda ingen avdelinger", "No_direct_messages_yet": "Ingen direkte meldinger.", + "No_Discussions_found": "Ingen diskusjoner funnet", "No_discussions_yet": "Ingen diskusjoner enda", + "No_emojis_found": "Ingen emojier funnet", "No_Encryption": "Ingen kryptering", + "No_files_found": "Ingen filer funnet", + "No_files_left_to_download": "Ingen filer igjen å laste ned", "No_groups_yet": "Du har ingen private grupper enda.", + "No_history": "Ingen historikk", "No_integration_found": "Ingen integrasjon funnet av den oppgitte id.", + "No_Limit": "Ingen grense", "No_livechats": "Du har ingen livechats", + "No_members_found": "Ingen medlemmer funnet", "No_mentions_found": "Ingen meldinger funnet", "No_messages_yet": "Ingen meldinger ennå", "No_pages_yet_Try_hitting_Reload_Pages_button": "Ingen sider ennå. Prøv å trykke på \"Last inn sider\" -knappen.", "No_pinned_messages": "Ingen pinnede meldinger", + "No_previous_chat_found": "Ingen tidligere chat funnet", "No_results_found": "Ingen resultater", "No_results_found_for": "Ingen resultater funnet for:", + "No_SLA_policies_yet": "Ingen SLA-retningslinjer enda", "No_snippet_messages": "Ingen utdrag", "No_starred_messages": "Ingen stjernemerkede meldinger", "No_such_command": "Ingen slik kommando: `/ {{command}}`", @@ -1995,7 +3033,11 @@ "Not_authorized": "Ikke autorisert", "Normal": "Normal", "Not_Available": "Ikke tilgjengelig", + "Not_assigned": "Ikke tildelt", "Not_found_or_not_allowed": "Ikke funnet eller ikke tillatt", + "Not_in_channel": "Ikke i kanalen", + "Not_started": "Ikke påbegynt", + "Not_Visible_To_Workspace": "Ikke synlig for arbeidsområdet", "Nothing": "Ingenting", "Nothing_found": "Ingenting funnet", "Notification_Desktop_Default_For": "Vis skrivebordsvarsler for", @@ -2008,10 +3050,17 @@ "Notifications_Sound_Volume": "Meldinger lydvolum", "Notify_active_in_this_room": "Gi beskjed til aktive brukere i dette rommet", "Notify_all_in_this_room": "Gi beskjed om alt i dette rommet", + "Now_Its_Visible_For_Everyone": "Nå er det synlig for alle", + "Default_Server_Timezone": "Server-tidssone", + "Default_Custom_Timezone": "Egendefinert tidssone", + "Default_User_Timezone": "Brukerens nåværende tidssone", "Num_Agents": "# Agenter", + "Number_in_seconds": "Antall i sekunder", + "Number_of_events": "Antall hendelser", "Number_of_messages": "Antall meldinger", "Number_of_most_recent_chats_estimate_wait_time": "Antall nylige chatter for å beregne estimert ventetid", "Number_of_most_recent_chats_estimate_wait_time_description": "Dette tallet definerer antall sist betjente rom som skal brukes til å beregne ventetid for kø.", + "OAuth": "OAuth", "OAuth_Application": "OAuth Application", "Objects": "objekter", "Off": "Av", @@ -2020,6 +3069,7 @@ "Office_Hours": "Kontortid", "Office_hours_enabled": "Kontortimer aktivert", "Office_hours_updated": "Kontortid oppdatert", + "offline": "frakoblet", "Offline": "offline", "Offline_DM_Email": "Direkte e-post-emne", "Offline_Email_Subject_Description": "Du kan bruke følgende plassholdere: \n - `[Site_Name]`, `[Site_URL]`, [User] og [Room] for henholdsvis søknadens navn, URL, brukernavn og romnavn. ", @@ -2031,26 +3081,53 @@ "Offline_message": "Frakoblet melding", "Offline_success_message": "Frakoblet suksessmelding", "Offline_unavailable": "Frakoblet utilgjengelig", + "Ok": "Ok", + "Old Colors": "Gamle farger", "Older_than": "Eldre enn", + "Omnichannel_External_Frame_Encryption_JWK": "Krypteringsnøkkel (JWK)", + "omnichannel_sla_change_history": "SLA-retningslinjene er endret: {{user}} endret SLA-retningslinjene til {{sla}}", + "Omnichannel_enable_department_removal": "Aktiver fjerning av avdeling", + "Omnichannel_enable_department_removal_alert": "Fjernede avdelinger kan ikke gjenopprettes, vi anbefaler å arkivere avdelingen i stedet.", "Omnichannel_Reports_Status_Open": "Åpne", "Omnichannel_Reports_Status_Closed": "Lukket", + "Omnichannel_Reports_Channels_Empty_Subtitle": "Dette diagrammet viser de mest brukte kanalene.", + "Omnichannel_Reports_Departments_Empty_Subtitle": "Dette diagrammet viser avdelingene som mottar flest samtaler.", + "Omnichannel_Reports_Status_Empty_Subtitle": "Dette diagrammet vil oppdateres så snart samtalene starter.", + "Omnichannel_Reports_Tags_Empty_Subtitle": "Dette diagrammet viser de mest brukte taggene.", + "Omnichannel_Reports_Agents_Empty_Subtitle": "Dette diagrammet viser hvilke agenter som mottar det høyeste volumet av samtaler.", "On": "På", + "On_Hold": "På vent", + "On_Hold_Chats": "På vent", + "On_Hold_conversations": "Samtaler på vent", "online": "på nett", "Online": "på nett", "Only_authorized_users_can_write_new_messages": "Kun autoriserte brukere kan skrive nye meldinger", + "Only_authorized_users_can_react_to_messages": "Kun autoriserte brukere kan reagere på meldinger", "Only_from_users": "Bare beskjære innhold fra disse brukerne (la tomt for å beskjære alles innhold)", "Only_On_Desktop": "Skrivebordsmodus (sendes bare med enter på skrivebordet)", "Only_you_can_see_this_message": "Bare du kan se denne meldingen", + "Only_invited_users_can_acess_this_channel": "Bare inviterte brukere har tilgang til denne kanalen", "Oops_page_not_found": "Ups, siden ble ikke funnet", "Oops!": "Oops", + "Person_Or_Channel": "Person eller Channel", "Open": "Åpne", + "Open_call": "Åpen samtale", + "Open_call_in_new_tab": "Åpne samtale i ny fane", "Open_channel_user_search": "`%s` - Åpne kanal / brukeresøk", + "Open_conversations": "Åpne samtaler", + "Open_Days": "Åpne dager", "Open_days_of_the_week": "Åpen dager i uken", + "Open_Dialpad": "Åpne tastaturet", + "Open_directory": "Åpne katalogen", "Open_Livechats": "Åpne Livechats", + "Open_Outlook": "Åpne Outlook", + "Open_settings": "Åpne innstillinger", + "Open_thread": "Åpne tråd", "Open_your_authentication_app_and_enter_the_code": "Åpne autentiseringsprogrammet ditt og skriv inn koden. Du kan også bruke en av sikkerhetskodene dine.", "Opened": "åpnet", "Opened_in_a_new_window": "Åpnet i nytt vindu.", "Opens_a_channel_group_or_direct_message": "Åpner en kanal, gruppe eller direkte melding", + "Optional": "Valgfri", "optional": "valgfri", "Options": "Egenskaper", "or": "eller", @@ -2061,6 +3138,7 @@ "Organization_Name": "Organisasjonsnavn", "Organization_Type": "Organisasjonstype", "Original": "Opprinnelig", + "OS": "OS", "OS_Arch": "OS Arch", "OS_Cpus": "OS CPU Count", "OS_Freemem": "OS Free Memory", @@ -2075,22 +3153,65 @@ "Others": "Andre", "OTR": "OTR", "OTR_is_only_available_when_both_users_are_online": "OTR er bare tilgjengelig når begge brukerne er online", + "outbound-voip-calls": "Utgående VoIP-anrop", + "Outgoing": "Utgående", "Outgoing_WebHook": "Utgående WebHook", "Outgoing_WebHook_Description": "Få data ut av Rocket.Chat i sanntid.", + "Outlook_authentication": "Outlook-autentisering", + "Outlook_authentication_disabled": "Outlook-autentisering er deaktivert", + "Outlook_calendar": "Outlook-kalender", + "Outlook_calendar_settings": "Outlook-kalenderinnstillinger", + "Outlook_Calendar": "Outlook-kalender", "Outlook_Calendar_Enabled": "aktivert", + "Outlook_Calendar_Outlook_Url": "Outlook URL", + "Outlook_Calendar_Outlook_Url_Description": "URL som brukes til å starte Outlook-nettappen.", + "Output_format": "Utgående format", + "Outlook_Sync_Failed": "Kunne ikke laste inn Outlook-hendelser.", + "Outlook_Sync_Success": "Outlook-hendelser synkronisert.", "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", + "Owner": "Eier", + "Page_not_exist_or_not_permission": "Siden eksisterer ikke, eller du har kanskje ikke tilgangstillatelse", + "Page_not_found": "Fant ikke siden", "Page_title": "Side tittel", "Page_URL": "Side URL", + "Pages": "Sider", + "Parent_channel_doesnt_exist": "Channel finnes ikke.", + "Participants": "Deltakere", "Password": "Passord", "Password_Change_Disabled": "Din Rocket.Chat-administrator har deaktivert endring av passord", + "Password_Changed_Description": "Du kan bruke følgende plassholdere:\n - `[passord]` for det midlertidige passordet.\n - `[navn]`, `[fname]`, `[lname]` for henholdsvis brukerens fulle navn, fornavn eller etternavn.\n - `[email]` for brukerens e-post.\n - `[Site_Name]` og `[Site_URL]` for henholdsvis applikasjonsnavn og URL.", + "Password_Changed_Email_Subject": "[Site_Name] - Passord endret", + "Password_changed_section": "Passord endret", "Password_changed_successfully": "Passordet ble endret", + "Password_History": "Passordhistorikk", + "Password_History_Amount": "Lengde på passordhistorikk", + "Password_History_Amount_Description": "Antall sist brukte passord for å hindre brukere i å gjenbruke.", + "Password_must_have": "Passordet må ha:", "Password_Policy": "Passordpolicy", + "Password_Policy_Aria_Description": "Nedenfor er det oppført verifikasjoner av passordkrav", + "Password_must_meet_the_complexity_requirements": "Passordet må oppfylle kompleksitetskravene.", + "Password_to_access": "Passord for tilgang", + "Passwords_do_not_match": "passordene er ikke like", "Past_Chats": "Tidligere Chats", + "Paste_here": "Lim inn her...", + "Paste": "Lim inn", + "Pause": "Pause", + "Paste_error": "Kunne ikke lese fra utklippstavlen", + "Paid_Apps": "Betalte apper", "Payload": "nyttelast", + "PDF": "PDF", "People": "Mennesker", "Permalink": "permalink", "Permissions": "tillatelser", + "Personal_Access_Tokens": "Personlige tilgangstokener", + "Phone": "Telefon", + "Phone_call": "Telefonsamtale", + "Phone_Number": "Telefonnummer", "Thank_you_exclamation_mark": "Takk skal du ha!", + "Thank_You_For_Choosing_RocketChat": "Takk for at du valgte Rocket.Chat!", + "Phone_already_exists": "Telefonen finnes allerede", + "Phone_number": "Telefonnummer", + "PID": "PID", "Pin_Message": "Pin melding", "pin-message": "Pin melding", "pin-message_description": "Tillatelse til å knytte en melding i en kanal", @@ -2108,6 +3229,9 @@ "PiwikAnalytics_url_Description": "Den url hvor Piwik er bosatt, sørg for å inkludere den bakre skråstreken. Eksempel: `https://piwik.rocket.chat/`", "Placeholder_for_email_or_username_login_field": "Plassholder for e-post eller brukernavn påloggingsfelt", "Placeholder_for_password_login_field": "Plassholder for passordloggfelt", + "Platform_Windows": "Windows", + "Platform_Linux": "Linux", + "Platform_Mac": "Mac", "Please_add_a_comment": "Vennligst legg til en kommentar", "Please_add_a_comment_to_close_the_room": "Vennligst legg til en kommentar for å lukke rommet", "Please_answer_survey": "Ta et øyeblikk for å svare på en rask undersøkelse om denne chatten", @@ -2129,6 +3253,7 @@ "Please_wait_while_OTR_is_being_established": "Vent mens OTR etableres", "Please_wait_while_your_account_is_being_deleted": "Vennligst vent mens kontoen din blir slettet ...", "Please_wait_while_your_profile_is_being_saved": "Vennligst vent mens profilen din blir lagret ...", + "Policies": "Retningslinjer", "Port": "Havn", "Post_as": "Legg inn som", "Post_to_Channel": "Legg til i kanal", @@ -2137,20 +3262,42 @@ "post-readonly_description": "Tillatelse til å legge inn en melding i en skrivebeskyttet kanal", "Preferences": "Preferanser", "Preferences_saved": "Innstillinger lagret", + "Preparing_list_of_channels": "Forbereder kanalliste", + "Preparing_list_of_messages": "Forbereder meldingsliste", + "Preparing_list_of_users": "Forbereder brukerliste", + "Presence": "Tilstedeværelse", + "Preview": "Forhåndsvisning", "preview-c-room": "Forhåndsvis offentlig kanal", "preview-c-room_description": "Tillatelse til å vise innholdet i en offentlig kanal før de ble med", + "Previous_month": "Forrige måned", + "Previous_week": "Forrige uke", + "Price": "Pris", + "Priorities": "Prioriteringer", + "Priority": "Prioritering", + "Priority_saved": "Prioritering lagret", + "Priority_removed": "Prioritering fjernet", + "Priorities_restored": "Prioriteringer gjenopprettet", "Privacy": "Privatliv", "Privacy_Policy": "Personvernerklæring", + "Privacy_policy": "Personvernerklæring", + "Privacy_summary": "Personvernsammendrag", "Private": "Privat", + "private": "privat", + "Private_channels": "Private kanaler", + "Private_Apps": "Private apper", "Private_Channel": "Privat kanal", + "Private_Channels": "Private kanaler", + "Private_Chats": "Private chatter", "Private_Group": "Privat gruppe", "Private_Groups": "Private grupper", "Private_Groups_list": "Liste over private grupper", "Private_Team": "Privat team", + "Productivity": "Produktivitet", "Profile": "Profil", "Profile_details": "Profildetaljer", "Profile_picture": "Profilbilde", "Profile_saved_successfully": "Profilen er lagret vellykket", + "Prometheus": "Prometheus", "Prune": "Sviske", "Prune_finished": "Prune ferdig", "Prune_Messages": "Beskjære meldinger", @@ -2162,8 +3309,12 @@ "Pruning_files": "Beskjæring av filer ...", "Pruning_messages": "Beskjæring av meldinger ...", "Public": "Offentlig", + "public": "offentlig", "Public_Channel": "Offentlig kanal", + "Public_Channels": "Offentlige kanaler", "Public_Community": "Offentlig fellesskap", + "Public_URL": "Offentlig URL", + "Purchased": "Anskaffet", "Push": "Trykk", "Push_apn_cert": "APN-sertifisering", "Push_apn_dev_cert": "APN Dev Cert", @@ -2182,16 +3333,24 @@ "Push_test_push": "Test", "Query": "Spørsmål", "Query_description": "Tilleggsbetingelser for å bestemme hvilke brukere som skal sende e-posten til. Uberegnede brukere blir automatisk fjernet fra spørringen. Det må være et gyldig JSON. Eksempel: \"{\" createdAt \": {\" $ gt \": {\" $ date \":\" 2015-01-01T00: 00: 00.000Z \"}}}\"", + "Query_is_not_valid_JSON": "Spørringen er ikke gyldig JSON", "Queue": "Kø", + "Queued": "Satt i kø", + "Queues": "Køer", + "Queue_Time": "Køtid", + "Queue_management": "Køstyring", "quote": "sitat", "Quote": "Sitat", "Random": "Tilfeldig", + "Rate Limiter": "Frekvensbegrensning ", + "Rate Limiter_Description": "Kontroller frekvensen av forespørsler som sendes eller mottas av serveren din for å forhindre cyberangrep og skraping.", "React_when_read_only": "Tillat reaksjon", "React_when_read_only_changed_successfully": "Tillat å reagere når bare lest ble endret", "Reacted_with": "Reagert med", "Reactions": "reaksjoner", "Read_by": "Les av", "Read_only": "Les bare", + "Readability": "Lesbarhet", "Read_only_changed_successfully": "Bare lest endret", "Read_only_channel": "Les kun kanal", "Read_only_group": "Read Only Group", @@ -2200,7 +3359,11 @@ "Reason_To_Join": "Årsak til å bli med", "Receive_alerts": "Motta varsler", "Receive_Group_Mentions": "Motta @all og @here nevner", + "Receive_login_notifications": "Motta påloggingsvarsler", + "Receive_Login_Detection_Emails": "Motta påloggingsdeteksjons-e-poster", + "Receive_Login_Detection_Emails_Description": "Motta en e-post hver gang en ny pålogging oppdages på kontoen din.", "Record": "Ta opp", + "recording": "opptak", "Redirect_URI": "Omdirigere URI", "Refresh_keys": "Oppdater nøkler", "Refresh_oauth_services": "Oppdater OAuth-tjenester", @@ -2223,39 +3386,70 @@ "Registration_Succeeded": "Registrering lyktes", "Registration_via_Admin": "Registrering via Admin", "Regular_Expressions": "Vanlig uttrykk", + "Reject_call": "Avvis anrop", "Release": "Utgivelse", + "Releases": "Utgivelser", "Religious": "Religiøs", "Reload": "Last", + "Reload_page": "Last inn siden på nytt", "Reload_Pages": "Oppdater sidene", "Remove": "Fjerne", "Remove_Admin": "Fjern Admin", "Remove_as_leader": "Fjern som leder", "Remove_as_moderator": "Fjern som moderator", "Remove_as_owner": "Fjern som eier", + "Remove_Channel_Links": "Fjern kanallenker", "Remove_custom_oauth": "Fjern tilpasset oauth", "Remove_from_room": "Fjern fra rommet", + "Remove_from_team": "Fjern fra teamet", "Remove_last_admin": "Fjerner siste admin", "Remove_someone_from_room": "Fjern noen fra rommet", + "remove-team-channel": "Fjern Teamkanal", "remove-user": "Fjern bruker", "remove-user_description": "Tillatelse til å fjerne en bruker fra et rom", "Removed": "fjernet", "Removed_User": "Fjernet bruker", + "Removed__roomName__from_this_team": "fjernet #{{roomName}} fra dette teamet", + "Removed__username__from_team": "fjernet @{{user_removed}} fra dette teamet", + "Removed__roomName__from_the_team": "fjernet #{{roomName}} fra dette teamet", + "Removed__username__from_the_team": "fjernet @{{user_removed}} fra dette teamet", "Reply": "Svare", + "Reply_in_direct_message": "Svar i direktemelding", + "Reply_in_thread": "Svar i tråden", + "Reply_via_Email": "Svar via e-post", "ReplyTo": "Svare på", + "Reports": "Rapporter", "Report_Abuse": "Rapporter misbruk", "Report_exclamation_mark": "Rapportere!", "Report_this_message_question_mark": "Rapporter denne meldingen?", + "Report_User": "Rapporter bruker", "Reporting": "rapportering", + "Request_comment_when_closing_conversation_description": "Hvis aktivert, må agenten angi en kommentar før samtalen avsluttes.", + "requests": "forespørsler", + "Requests": "Forespørsler", + "Requested": "Forespurt", + "Requested_At": "Forespurt klokken", + "Requested_By": "Forespurt av", + "Required": "Påkrevd", + "required": "påkrevd", "Require_all_tokens": "Krev alle tokens", "Require_any_token": "Krev noen token", "Require_password_change": "Krev passordendring", "Resend_verification_email": "Send bekreftelsesmeldingen på nytt", "Reset": "Tilbakestill", + "Reset_priorities": "Tilbakestill prioriteter", "Reset_Connection": "Tilbakestill tilkobling", + "Reset_E2E_Key": "Tilbakestill E2E-nøkkel", "Reset_password": "Tilbakestilling av passord", - "Reset_section_settings": "Tilbakestill seksjonsinnstillinger", + "Reset_section_settings": "Tilbakestill til standardinnstillinger", + "Reset_TOTP": "Tilbakestill TOTP", + "Responding": "Svarer", "Restart": "Omstart", "Restart_the_server": "Start serveren på nytt", + "restart-server": "Start serveren på nytt", + "restart-server_description": "Tillatelse til å starte serveren på nytt", + "Results": "Resultater", + "Resume": "Gjenoppta", "Retail": "Detaljhandel", "Retention_setting_changed_successfully": "Retenspolicyinnstillingen ble endret", "RetentionPolicy": "Retensjonspolitikk", @@ -2284,6 +3478,10 @@ "RetentionPolicyRoom_MaxAge": "Maksimal meldingsalder i dager (standard: {{max}})", "RetentionPolicyRoom_OverrideGlobal": "Overstyr global retensjonspolicy", "RetentionPolicyRoom_ReadTheDocs": "Pass på! Å endre disse innstillingene uten ytterst forsiktighet kan ødelegge all meldingshistorikk. Les dokumentasjonen før du slår på funksjonen herher.", + "Retry": "Prøv på nytt", + "Required_action": "Påkrevd handling", + "Notes": "Notater", + "Unsafe_Url": "Usikker URL", "Role": "rolle", "Role_Editing": "Rollredigering", "Role_Mapping": "Rollekobling", @@ -2295,29 +3493,43 @@ "Room_archivation_state_true": "arkivert", "Room_archived": "Rom arkivert", "room_changed_announcement": "Rommeldingen endret til: {{room_announcement}}av {{user_by}}", + "room_changed_avatar": "Romavatar endret av {{user_by}}", "room_changed_description": "Rombeskrivelsen endret til: {{room_description}}av {{user_by}}", "room_changed_privacy": "Romtype er endret til: {{room_type}}av {{user_by}}", "room_changed_topic": "Romemne endret til: {{room_topic}}av {{user_by}}", + "room_changed_type": "endret rommet til {{room_type}}", + "room_changed_topic_to": "endret rommets emne til {{room_topic}}", "Room_default_change_to_private_will_be_default_no_more": "Dette er en standardkanal, og endring av den til en privat gruppe vil føre til at den ikke lenger er en standardkanal. Vil du fortsette?", "Room_description_changed_successfully": "Rombeskrivelsen ble endret", + "room_disallowed_reactions": "ikke tillatte reaksjoner", "Room_has_been_archived": "Rom har blitt arkivert", + "Room_has_been_converted": "Room er konvertert", + "Room_has_been_created": "Room er opprettet", "Room_has_been_deleted": "Rommet har blitt slettet", + "Room_has_been_removed": "Room er fjernet", "Room_has_been_unarchived": "Rom har blitt arkivert", "Room_Info": "Rominformasjon", "room_is_blocked": "Dette rommet er blokkert", + "room_account_deactivated": "Denne kontoen er deaktivert", "room_is_read_only": "Dette rommet er kun skrivebeskyttet", "room_name": "Romnavn", "Room_name_changed": "Romnavnet endret til: {{room_name}}av {{user_by}}", + "Room_name_changed_to": "endret romnavn til {{room_name}}", "Room_name_changed_successfully": "Romnavnet ble endret", + "Room_not_exist_or_not_permission": "Rommet eksisterer ikke eller du har ikke tilgang", "Room_not_found": "Rom ikke funnet", "Room_password_changed_successfully": "Rompassordet ble endret", + "Room_Status_Open": "Åpen", "Room_topic_changed_successfully": "Romemne endret seg vellykket", "Room_type_changed_successfully": "Romtype er endret", "Room_type_of_default_rooms_cant_be_changed": "Dette er et standardrom, og typen kan ikke endres, vennligst kontakt med administratoren din.", "Room_unarchived": "Rom unarchived", + "Room_updated_successfully": "Rommet ble oppdatert!", "Room_uploaded_file_list": "Filer Liste", "Room_uploaded_file_list_empty": "Ingen filer tilgjengelig.", "Rooms": "Rom", + "Rooms_added_successfully": "Romet ble lagt til", + "Run_only_once_for_each_visitor": "Kjør bare én gang for hver besøkende", "run-import": "Kjør import", "run-import_description": "Tillatelse til å kjøre importørene", "run-migration": "Kjør migrering", @@ -2325,23 +3537,37 @@ "Running_Instances": "Kjører forekomster", "Runtime_Environment": "Runtime Environment", "S_new_messages_since_s": "%s nye meldinger siden%s", + "S_new_messages": "%s nye meldinger", "Same_As_Token_Sent_Via": "Samme som \"Token Sent Via\"", "Same_Style_For_Mentions": "Samme stil for nevner", "SAML": "SAML", + "SAML_Connection": "Tilkobling", + "SAML_General": "Generell", "SAML_Custom_Cert": "Tilpasset sertifikat", + "SAML_Custom_Debug": "Aktiver feilsøking", "SAML_Custom_Entry_point": "Tilpasset oppføringspunkt", "SAML_Custom_Generate_Username": "Generer brukernavn", "SAML_Custom_IDP_SLO_Redirect_URL": "IDP SLO Omadresser URL", + "SAML_Custom_Immutable_Property_EMail": "E-post", "SAML_Custom_Immutable_Property_Username": "Brukernavn", "SAML_Custom_Issuer": "Tilpasset utsteder", "SAML_Custom_Logout_Behaviour": "Logout Behavior", - "SAML_Custom_Logout_Behaviour_End_Only_RocketChat": "Bare logg ut fra Rocket.Chat", + "SAML_Custom_Logout_Behaviour_End_Only_RocketChat": "Kun logg ut fra Rocket.Chat", "SAML_Custom_Logout_Behaviour_Terminate_SAML_Session": "Avslutt SAML-økten", "SAML_Custom_Private_Key": "Privat nøkkelinnhold", "SAML_Custom_Provider": "Tilpasset leverandør", "SAML_Custom_Public_Cert": "Offentlig sertifisering", "SAML_Custom_user_data_fieldmap": "Brukerdatafeltkart", + "SAML_Custom_Username_Field": "Feltnavn for brukernavn", + "SAML_Custom_Username_Normalize": "Normaliser brukernavn", + "SAML_Custom_Username_Normalize_Lowercase": "Til små bokstaver", + "SAML_Custom_Username_Normalize_None": "Ingen normalisering", + "SAML_Role_Attribute_Sync": "Synkroniser brukerroller", "SAML_Section_1_User_Interface": "Brukergrensesnitt", + "SAML_Section_2_Certificate": "Sertifikat", + "SAML_Section_3_Behavior": "Oppførsel", + "SAML_Section_4_Roles": "Roller", + "SAML_Section_6_Advanced": "Avansert", "Saturday": "lørdag", "Save": "Lagre", "Save_changes": "Lagre endringer", @@ -2355,33 +3581,59 @@ "Scan_QR_code_alternative_s": "Hvis du ikke kan skanne QR-koden, kan du skrive inn kode manuelt i stedet:", "Scope": "omfang", "Screen_Share": "Skjermdel", + "Script": "Script", "Script_Enabled": "Skript aktivert", "Search": "Søk", + "Searchable": "Søkbar", + "Search_Apps": "Søk i apper", + "Search_Installed_Apps": "Søk i installerte apper", + "Search_Private_apps": "Søk i private apper", + "Search_Premium_Apps": "Søk i Premium-apper", "Search_by_file_name": "Søk etter filnavn", "Search_by_username": "Søk etter brukernavn", + "Search_by_category": "Søk på kategori", "Search_Channels": "Søk kanaler", + "Search_Chat_History": "Søk chathistorikk", "Search_current_provider_not_active": "Gjeldende søkeleverandør er ikke aktiv", + "Search_Devices_Users": "Søk etter enheter eller brukere", + "Search_Files": "Søk etter filer", "Search_message_search_failed": "Søkeforespørsel mislyktes", "Search_Messages": "Søk meldinger", "Search_Page_Size": "Sidestørrelse", "Search_Private_Groups": "Søk i private grupper", "Search_Provider": "Søk leverandør", + "Search_rooms": "Søk etter rom", + "Search_Rooms": "Søk etter rom", "Search_Users": "Søk brukere", + "used_limit_infinite": "{{brukt, tall}} / ∞", "seconds": "sekunder", "Secret_token": "Hemmelig Token", "Security": "Sikkerhet", + "See_all_themes": "Se alle temaene", + "See_documentation": "Se dokumentasjon", + "See_Pricing": "Se Priser", + "See_full_profile": "Se hele profilen", + "See_history": "Se historikk", + "Select": "Velg", "Select_a_department": "Velg en avdeling", + "Select_a_room": "Velg et rom", "Select_a_user": "Velg en bruker", "Select_an_avatar": "Velg en avatar", "Select_an_option": "Velg et alternativ", + "Select_at_least_one_user": "Velg minst én bruker", + "Select_at_least_two_users": "Velg minst to brukere", "Select_department": "Velg en avdeling", "Select_file": "Velg Fil", "Select_role": "Velg en rolle", "Select_service_to_login": "Velg en tjeneste for å logge inn for å laste inn bildet eller laste det opp direkte fra datamaskinen", + "Select_the_channels_you_want_the_user_to_be_removed_from": "Velg kanalene du vil at brukeren skal fjernes fra", + "Select_atleast_one_channel_to_forward_the_messsage_to": "Velg minst én kanal å videresende meldingen til", "Select_user": "Velg bruker", "Select_users": "Velg brukere", + "Select_period": "Velg periode", "Selected_agents": "Utvalgte agenter", "Selected_departments": "Valgte avdelinger", + "Selecting_users": "Velger brukere", "Send": "Sende", "Send_a_message": "Send en melding", "Send_a_test_mail_to_my_user": "Send en testmelding til brukeren min", @@ -2389,30 +3641,49 @@ "Send_confirmation_email": "Send bekreftelses-e-post", "Send_data_into_RocketChat_in_realtime": "Send data til Rocket.Chat i sanntid.", "Send_email": "Send e-post", + "Send_Email_SMTP_Warning": "For å sende denne e-posten må du konfigurere SMTP-e-postserveren", "Send_invitation_email": "Send invitasjons-e-post", "Send_invitation_email_error": "Du har ikke oppgitt noen gyldig e-postadresse.", "Send_invitation_email_info": "Du kan sende flere e-post invitasjoner samtidig.", "Send_invitation_email_success": "Du har sendt en invitasjons-e-post til følgende adresser:", + "Send_it_as_attachment_instead_question": "Sende det som vedlegg i stedet?", "Send_request_on_agent_message": "Send forespørsel om agentmeldinger", "Send_request_on_chat_close": "Send forespørsel om chat Lukk", "Send_request_on_lead_capture": "Send forespørsel om blyopptak", "Send_request_on_offline_messages": "Send forespørsel om offline meldinger", "Send_request_on_visitor_message": "Send forespørsel om besøksmeldinger", "Send_Test": "Send test", + "Export_as_PDF": "Eksporter som PDF", "Send_Visitor_navigation_history_as_a_message": "Send besøksnavigasjonsloggen som en melding", "Send_visitor_navigation_history_on_request": "Send besøksnavigasjonshistorikk på forespørsel", "Send_welcome_email": "Send velkomstmelding", "Send_your_JSON_payloads_to_this_URL": "Send JSON nyttelastene til denne nettadressen.", + "send-mail_description": "Tillatelse til å sende e-poster", + "send-many-messages": "Send mange meldinger", + "Sender_Info": "Avsender info", "Sending": "Sender ...", + "Sending_Invitations": "Sender invitasjoner", + "Sending_your_mail_to_s": "Sender e-posten din til %s", "Sent_an_attachment": "Sender et vedlegg", + "Sent_from": "Sendt fra", + "Separate_multiple_words_with_commas": "Skill flere ord med komma", "Served_By": "Servert av", + "Server": "Server", + "Server_already_added": "Server allerede lagt til", + "Server_doesnt_exist": "Serveren eksisterer ikke", + "Servers": "Servere", + "Server_Configuration": "Serverkonfigurasjon", "Server_Info": "Serverinfo", + "Server_name": "Server navn", "Server_Type": "Server Type", "Service": "Tjeneste", "Service_account_key": "Tjenesten konto nøkkel", + "Set_as_favorite": "Sett som favoritt", "Set_as_leader": "Sett som leder", "Set_as_moderator": "Sett som moderator", "Set_as_owner": "Sett som eier", + "Upload_app": "Last opp app", + "Set_random_password_and_send_by_email": "Angi tilfeldig passord og send via e-post", "set-moderator": "Sett moderator", "set-moderator_description": "Tillatelse til å sette andre brukere som moderator på en kanal", "set-owner": "Sett eier", @@ -2423,17 +3694,22 @@ "set-readonly_description": "Tillatelse til å angi en kanal for å lese kun kanal", "Settings": "Innstillinger", "Settings_updated": "innstillingene er oppdatert", + "Setup_SMTP": "Sett opp SMTP", "Setup_Wizard": "Setup Wizard", + "Setup_Wizard_Description": "Grunnleggende informasjon om arbeidsområdet ditt, for eksempel organisasjonsnavn og land.", "Setup_Wizard_Info": "Vi veileder deg gjennom å sette opp din første admin bruker, konfigurere organisasjonen din og registrere serveren din for å motta gratis push notifications og mer.", + "Share": "Dele", "Share_Location_Title": "Del lokasjon?", "Shared_Location": "Felles plassering", "Shortcut": "Snarvei", + "shortcut_name": "snarveisnavn", "Should_be_a_URL_of_an_image": "Skal være en URL til et bilde.", "Should_exists_a_user_with_this_username": "Brukeren må allerede eksistere.", "Show_agent_email": "Vis agent-e-post", "Show_all": "Vis alt", "Show_Avatars": "Vis avatars", "Show_counter": "Vis teller", + "Show_default_content": "Vis standardinnhold", "Show_email_field": "Vis e-postfelt", "Show_more": "Vis mer", "Show_name_field": "Vis navnefelt", @@ -2445,19 +3721,42 @@ "Show_room_counter_on_sidebar": "Vis romteller på sidebar", "Show_Setup_Wizard": "Vis oppsettveiviseren", "Show_the_keyboard_shortcut_list": "Vis hurtigtastlisten for tastaturet", + "Show_To_Workspace": "Vis til arbeidsområdet", + "Show_video": "Vis video", "Showing_archived_results": "

Viser %s arkiverte resultater

", + "Showing_current_of_total": "Viser {{current}} av {{total}}", "Showing_online_users": "Viser: {{total_showing}}, Online: {{online}}, Totalt: {{total}} brukere", "Showing_results": "

Viser %s resultater

", + "Showing_results_of": "Viser resultater %s - %s av %s", + "Show_usernames": "Vis brukernavn", + "Show_roles": "Vis roller", + "Show_or_hide_the_user_roles_of_message_authors": "Vis eller skjul brukerrollene til meldingsforfattere.", + "Show_or_hide_the_username_of_message_authors": "Vis eller skjul brukernavnet til meldingsforfatterne.", "Sidebar": "sidebar", "Sidebar_list_mode": "Sidebar Kanallistemodus", "Sign_in_to_start_talking": "Logg inn for å begynne å snakke", + "Sign_in_with__provider__": "Logg på med {{provider}}", "since_creation": "siden%s", "Site_Name": "Side navn", "Site_Url": "Nettstedets nettadresse", "Site_Url_Description": "Eksempel: `https://chat.domain.com/`", "Size": "Størrelse", + "Skin_tone": "Hudfarge", "Skip": "Hopp", + "Skip_to_main_content": "Gå til hovedinnhold", + "SLA_Policy": "SLA-retningslinje", + "SLA_Policies": "SLA-retningslinjer", + "SLA_removed": "SLA fjernet", + "Slack": "Slack", "Slack_Users": "Slack's Brukere CSV", + "SlackBridge_APIToken": "API-tokens (Legacy)", + "SlackBridge_UseLegacy": "Bruk Legacy API-tokens", + "SlackBridge_APIToken_Description": "Du kan konfigurere flere Slack-servere ved å legge til én API-token per linje.", + "SlackBridge_BotToken": "Bot-tokens", + "SlackBridge_BotToken_Description": "Du kan konfigurere flere Slack servere ved å legge til en Bot Token per linje.", + "SlackBridge_AppToken": "App-tokens", + "SlackBridge_AppToken_Description": "Du kan konfigurere flere Slack servere ved å legge til en apptoken per linje.", + "SlackBridge_SigningSecret_Description": "Du kan konfigurere flere Slack-servere ved å legge til én innloggingshemmelighet per linje.", "SlackBridge_error": "SlackBridge fikk en feil mens du importerte meldingene dine på%s:%s", "SlackBridge_finish": "SlackBridge er ferdig med å importere meldingene på%s. Vennligst last inn for å se alle meldinger.", "SlackBridge_Out_All": "SlackBridge Out All", @@ -2466,10 +3765,13 @@ "SlackBridge_Out_Channels_Description": "Velg hvilke kanaler som vil sende meldinger tilbake til Slack", "SlackBridge_Out_Enabled": "SlackBridge ut aktivert", "SlackBridge_Out_Enabled_Description": "Velg om SlackBridge også skal sende meldingene dine tilbake til Slack", + "SlackBridge_Remove_Channel_Links_Description": "Fjern den interne koblingen mellom Rocket.Chat-kanaler og Slack-kanaler. Koblingene vil bli gjenskapt basert på kanalnavnene.", "SlackBridge_start": "@%s har startet en SlackBridge-import på `#%s`. Vi forteller deg når den er ferdig.", "Slash_Gimme_Description": "Viser (つ ◕ ◕) つ før meldingen din", "Slash_LennyFace_Description": "Viser (͡ ° ͜ʖ ͡ °) etter meldingen", "Slash_Shrug_Description": "Viser ¯ \\ _ (ツ) _ / ¯ etter meldingen", + "Slash_Status_Description": "Angi statusmeldingen din", + "Slash_Status_Params": "Statusmelding", "Slash_Tableflip_Description": "Viser (╯ ° □ °) ╯ (┻━┻", "Slash_TableUnflip_Description": "Viser ── ノ (゜ - ゜ ノ)", "Slash_Topic_Description": "Angi emne", @@ -2483,33 +3785,49 @@ "Smarsh_MissingEmail_Email": "Manglende e-post", "Smarsh_MissingEmail_Email_Description": "E-posten som skal vises for en brukerkonto når e-postadressen mangler, skjer vanligvis med botkontoer.", "Smileys_and_People": "Smileys & People", + "SMS": "SMS", "SMS_Enabled": "SMS aktivert", "SMTP": "SMTP", "SMTP_Host": "SMTP-verten", "SMTP_Password": "SMTP-passord", "SMTP_Port": "SMTP-port", + "SMTP_Server_Not_Setup_Title": "SMTP-serveren er ikke konfigurert enda", "SMTP_Test_Button": "Test SMTP-innstillinger", "SMTP_Username": "SMTP Brukernavn", "Snippet_Added": "Opprettet på%s", "Snippet_name": "Kuttnavn", "Snippeted_a_message": "Lagde et utdrag {{snippetLink}}", "Social_Network": "Sosialt nettverk", + "Something_went_wrong": "Noe gikk galt", + "Something_went_wrong_try_again_later": "Noe gikk galt. Prøv igjen senere.", "Sorry_page_you_requested_does_not_exist_or_was_deleted": "Beklager, siden du ba om, finnes ikke eller ble slettet!", "Sort": "Sortere", + "Sort_By": "Sorter etter", + "Sorting_mechanism": "Sorteringsmekanisme", "Sort_by_activity": "Sorter etter aktivitet", "Sound": "Lyd", + "Sounds": "Lyder", "Sound_File_mp3": "Lydfil (mp3)", + "Sound File": "Lydfil", + "Source": "Kilde", + "Speakers": "Høyttalere", "SSL": "SSL", "Star_Message": "Stjernemelding", "Starred_Messages": "Stjernemerkede meldinger", "Start": "Start", + "Start_a_call": "Start en samtale", + "Start_a_call_with": "Start en samtale med", + "Start_a_free_trial": "Start en gratis prøveperiode", "Start_audio_call": "Start lydanrop", + "Start_call": "Start samtale", "Start_Chat": "Start Chat", + "Start_free_trial": "Start gratis prøveperiode", "Start_of_conversation": "Start samtalen", "Start_OTR": "Start OTR", "Start_video_call": "Start videosamtale", "Start_video_conference": "Start videokonferanse?", "Start_with_s_for_user_or_s_for_channel_Eg_s_or_s": "Start med %sfor bruker eller %sfor kanal. Eksempel: %seller %s", + "Started": "Startet", "Started_a_video_call": "Startet et videoanrop", "Started_At": "Startet på", "Statistics": "Statistikk", @@ -2523,19 +3841,29 @@ "Stats_Non_Active_Users": "Inaktive brukere", "Stats_Offline_Users": "Offline brukere", "Stats_Online_Users": "Online brukere", + "Stats_Total_Active_Apps": "Totalt aktive apper", "Stats_Total_Channels": "Totalt antall kanaler", - "Stats_Total_Direct_Messages": "Totalt direkte meldingsrom", + "Stats_Total_Connected_Users": "Totalt antall tilkoblede brukere", + "Stats_Total_Direct_Messages": "Direktemeldinger", + "Stats_Total_Installed_Apps": "Totalt antall installerte apper", "Stats_Total_Livechat_Rooms": "Totalt Livechat-rom", "Stats_Total_Messages": "Totalt antall meldinger", "Stats_Total_Messages_Channel": "Totalt antall meldinger i kanaler", "Stats_Total_Messages_Direct": "Totalt antall meldinger i direkte meldinger", "Stats_Total_Messages_Livechat": "Totalt antall meldinger i Livechats", "Stats_Total_Messages_PrivateGroup": "Totalt antall meldinger i private grupper", + "Stats_Total_Messages_Discussions": "I diskusjoner", "Stats_Total_Private_Groups": "Totalt Private Grupper", "Stats_Total_Rooms": "Totalt rom", + "Stats_Total_Uploads": "Totalt antall opplastinger", "Stats_Total_Users": "Totalt antall brukere", "Status": "Status", + "StatusMessage": "Statusmelding", + "StatusMessage_Changed_Successfully": "Statusmeldingen ble endret.", + "StatusMessage_Placeholder": "Hva gjør du akkurat nå?", + "StatusMessage_Too_Long": "Statusmeldingen må være kortere enn 120 tegn.", "Step": "Trinn", + "Stop_call": "Stopp samtale", "Stop_Recording": "Stopp innspilling", "Store_Last_Message": "Lagre siste melding", "Store_Last_Message_Sent_per_Room": "Lagre siste melding sendt på hvert rom.", @@ -2544,44 +3872,110 @@ "Stream_Cast_Address_Description": "IP eller vert av Rocket.Chat sentral Stream Cast. F.eks `192.168.1.1: 3000` eller` localhost: 4000`", "Subject": "Emne", "Submit": "Sende inn", + "Subscribe": "Abonner", "Success": "Suksess", "Success_message": "Suksessmelding", + "Suggestion_from_recent_messages": "Forslag fra siste meldinger", "Sunday": "søndag", "Support": "Support", "Survey": "undersøkelse", "Survey_instructions": "Vurder hvert spørsmål i henhold til din tilfredshet, 1 som betyr at du er helt utilfreds og 5 betyr at du er helt fornøyd.", "Symbols": "Symboler", + "Sync": "Synkroniser", + "Sync / Import": "Synkroniser / importer", "Sync_in_progress": "Synkronisering pågår", + "Sync_Interval": "Synkroniseringsintervall", "Sync_success": "Synk suksess", "Sync_Users": "Synkronisere brukere", "System_messages": "Systemmeldinger", "Tag": "stikkord", "Tag_removed": "Tagg fjernet", "Take_it": "Ta det!", + "Talk_Time": "Samtaletid ", + "Talk_to_an_expert": "Snakk med en ekspert", + "Talk_to_sales": "Snakk med salg", + "Talk_to_your_workspace_administrator_about_enabling_video_conferencing": "Snakk med arbeidsområdeadministratoren din om å aktivere videokonferanser", + "Talk_to_your_workspace_admin_to_address_this_issue": "Snakk med arbeidsområdeadministratoren din for å løse dette problemet.", + "Target user not allowed to receive messages": "Valgt bruker har ikke tillatelse til å motta meldinger", "TargetRoom": "Målrommet", "TargetRoom_Description": "Rommet der meldinger vil bli sendt som er et resultat av at denne hendelsen blir sparket. Bare ett målrom er tillatt og det må eksistere.", "Team": "Team", + "Team_Add_existing_channels": "Legg til eksisterende kanaler", + "Team_Add_existing": "Legg til eksisterende", + "Team_Channels": "Team-Channel", + "Team_Delete_Channel_modal_content_danger": "Dette kan ikke angres.", + "Team_Delete_Channel_modal_content": "Vil du slette denne Channel?", + "Team_has_been_created": "Teamet er opprettet", + "Team_has_been_deleted": "Teamet er slettet", + "Team_Info": "Teaminformasjon", + "Team_Mapping": "Teamkartlegging", + "Team_Name": "Teamnavn", + "Team_Remove_from_team_modal_content": "Vil du fjerne denne kanalen fra {{teamName}}? Kanalen flyttes tilbake til arbeidsområdet.", + "Team_Remove_from_team": "Fjern fra team", + "Teams": "Team", + "Teams_channels_didnt_leave": "Du valgte ikke følgende kanaler, så du forlater dem ikke:", + "Teams_channels_last_owner_delete_channel_warning": "Du er den siste eieren av denne kanalen. Når du konverterer teamet til en kanal, vil kanalen bli flyttet til arbeidsområdet.", + "Teams_channels_last_owner_leave_channel_warning": "Du er den siste eieren av denne kanalen. Når du forlater teamet, vil kanalen bli holdt inne i teamet, men du vil administrere den utenfra.", + "Teams_leaving_team": "Du forlater dette teamet.", + "Teams_channels": "Teamets kanaler", + "Teams_convert_channel_to_team": "Konverter til Team", + "Teams_delete_team_choose_channels": "Velg kanalene du vil slette. De du bestemmer deg for å beholde, vil være tilgjengelige på arbeidsområdet ditt.", + "Teams_delete_team_public_notice": "Vær oppmerksom på at offentlige Channel fortsatt vil være offentlige og synlige for alle.", + "Teams_delete_team_Warning": "Når du sletter et team, vil alt chatinnhold og konfigurasjon bli slettet.", + "Teams_delete_team": "Du er i ferd med å slette dette teamet.", + "Teams_deleted_channels": "Følgende Channel vil bli slettet:", + "Teams_Errors_Already_exists": "Teamet `{{name}}` eksisterer allerede.", + "Teams_Errors_team_name": "Du kan ikke bruke \"{{name}}\" som et teamnavn.", + "Teams_move_channel_to_team": "Flytt til Team", + "Teams_New_Title": "Opprett team", "Teams_New_Name_Label": "Navn", + "Teams_Info": "Teaminformasjon", + "Teams_leave": "Forlat teamet", + "Teams_left_team_successfully": "Du forlot teamet", + "Teams_members": "Teamets medlemmer", + "Teams_New_Add_members_Label": "Legg til medlemmer", "Teams_New_Broadcast_Description": "Kun autoriserte brukere kan skrive nye meldinger, men de andre brukerne vil kunne svare", "Teams_New_Description_Label": "Emne", + "Teams_New_Encrypted_Label": "Kryptert", "Teams_New_Private_Label": "Privat", + "Teams_New_Read_only_Description": "Alle brukere i dette teamet kan skrive meldinger", + "Teams_Public_Team": "Offentlig team", "Teams_Private_Team": "Privat team", + "Teams_removing_member": "Fjerner medlem", + "Teams_removing__username__from_team": "Du fjerner {{username}} fra dette teamet", + "Teams_removing__username__from_team_and_channels": "Du fjerner {{username}} fra dette teamet og alle dets Channel.", + "Teams_Select_a_team": "Velg et team", + "Teams_Search_teams": "Søk etter team", "Teams_New_Read_only_Label": "Les bare", "Technology_Services": "Teknologi Tjenester", + "Terms": "Vilkår", + "Terms_of_use": "Bruksvilkår", "Test_Connection": "Testforbindelse", + "Upgrade_tab_trial_guide": "Prøveveiledning", "Test_Desktop_Notifications": "Test skrivebordsbeskjeder", + "test-push-notifications": "Test push-varsler", + "test-push-notifications_description": "Tillatelse til å teste push-varsler", + "Texts": "Tekster", "Thank_you_for_your_feedback": "Takk for din tilbakemelding", "The_application_name_is_required": "Programnavnet kreves", + "The_application_will_be_able_to": "<1>{{appName}} vil kunne:", "The_channel_name_is_required": "Kanalnavnet er påkrevd", "The_emails_are_being_sent": "E-postene blir sendt.", "The_field_is_required": "Feltet%s er påkrevd.", "The_image_resize_will_not_work_because_we_can_not_detect_ImageMagick_or_GraphicsMagick_installed_in_your_server": "Bildestørrelsen fungerer ikke fordi vi ikke kan oppdage ImageMagick eller GraphicsMagick installert på serveren din.", + "The_message_is_a_discussion_you_will_not_be_able_to_recover": "Meldingen er en diskusjon, du vil ikke kunne gjenopprette meldingene!", + "The_necessary_browser_permissions_for_location_sharing_are_not_granted": "De nødvendige nettlesertillatelsene for posisjonsdeling ble ikke gitt", "The_redirectUri_is_required": "RedirectUri er påkrevd", + "The_selected_user_is_not_an_agent": "Den valgte brukeren er ikke en agent", "The_server_will_restart_in_s_seconds": "Serveren starter på nytt i%s sekunder", "The_setting_s_is_configured_to_s_and_you_are_accessing_from_s": "Innstillingen %s er konfigurert til %s og du får tilgang fra %s!", + "The_user_s_will_be_removed_from_role_s": "Brukeren %s vil bli fjernet fra rollen %s", "The_user_will_be_removed_from_s": "Brukeren blir fjernet fra%s", "The_user_wont_be_able_to_type_in_s": "Brukeren kan ikke skrive inn%s", + "The_workspace_has_exceeded_the_monthly_limit_of_active_contacts": "Arbeidsområdet har overskredet den månedlige grensen for aktive kontakter.", "Theme": "Tema", + "Themes": "Temaer", + "theme-color-attention-color": "Oppmerksomhetsfarge", "theme-color-component-color": "Komponentfarge", "theme-color-content-background-color": "Innhold Bakgrunnsfarge", "theme-color-custom-scrollbar-color": "Tilpasset rullegardinfarge", @@ -2594,6 +3988,8 @@ "theme-color-primary-font-color": "Primær skrifttype farge", "theme-color-rc-color-alert": "Varsling", "theme-color-rc-color-alert-light": "Alert Light", + "theme-color-rc-color-alert-message-primary": "Varslingsmelding Primær", + "theme-color-rc-color-alert-message-primary-background": "Varslingsmelding Primær bakgrunn", "theme-color-rc-color-button-primary": "Knapp Primær", "theme-color-rc-color-button-primary-light": "Knapp Primærlys", "theme-color-rc-color-content": "Innhold", @@ -2629,27 +4025,40 @@ "There_are_no_departments_added_to_this_unit_yet": "Ingen avdelinger er lagt til denne enheten enda", "There_are_no_departments_available": "Det er ingen tilgjengelige avdelinger", "There_are_no_integrations": "Det er ingen integrasjoner", + "There_are_no_rooms_for_the_given_search_criteria": "Det er ingen rom for de oppgitte søkekriteriene", "There_are_no_users_in_this_role": "Det er ingen brukere i denne rollen.", + "These_notes_will_be_available_in_the_call_summary": "Disse notatene vil være tilgjengelige i samtalesammendraget", + "This_agent_was_already_selected": "Denne agenten er allerede valgt", + "This_cant_be_undone": "Dette kan ikke angres.", "This_conversation_is_already_closed": "Denne samtalen er allerede stengt.", "This_email_has_already_been_used_and_has_not_been_verified__Please_change_your_password": "Denne e-posten er allerede brukt og har ikke blitt verifisert. Vennligst endre passordet ditt.", + "This_feature_is_currently_in_alpha": "Denne funksjonen er for øyeblikket i alpha!", "This_is_a_desktop_notification": "Dette er et stasjonært varsel", + "Input": "Input ", "This_is_a_push_test_messsage": "Dette er en push-testmelding", "This_room_has_been_archived_by__username_": "Dette rommet er arkivert av {{username}}", "This_room_has_been_unarchived_by__username_": "Dette rommet er blitt arkivert av {{username}}", "Threads": "Tråder", "Thursday": "Torsdag", "Time_in_seconds": "Tid i sekunder", + "Timezone": "Tidssone", "Title": "Tittel", "Title_bar_color": "Tittel bar farge", "Title_bar_color_offline": "Tittellinjefarge offline", "Title_offline": "Tittel offline", + "To": "Til", "To_additional_emails": "Til flere e-poster", "To_install_RocketChat_Livechat_in_your_website_copy_paste_this_code_above_the_last_body_tag_on_your_site": "For å installere Rocket.Chat Livechat på nettstedet ditt, kopier & lim inn denne koden over det siste < / body > ta på nettstedet ditt.", + "To_prevent_seeing_this_message_again_allow_popups_from_workspace_URL": "For å unngå å se denne meldingen igjen, sørg for at nettleserinnstillingene tillater åpning av popup-vinduer fra arbeidsområdets URL:", "to_see_more_details_on_how_to_integrate": "for å se flere detaljer om hvordan å integrere.", "To_users": "Til brukere", + "Today": "I dag", "Toggle_original_translated": "Bytt original / oversatt", + "toggle-room-e2e-encryption_description": "Tillatelse til å veksle e2e-krypteringsrom", + "Token": "Token", "Token_Access": "Token Access", "Token_Controlled_Access": "Token kontrollert tilgang", + "Token_has_been_removed": "Token er fjernet", "Token_required": "Token kreves", "Tokens_Minimum_Needed_Balance": "Minst nødvendig tokenbalanse", "Tokens_Minimum_Needed_Balance_Description": "Angi laveste nødvendige balanse for hvert token. Blank eller \"0\" for ikke grense.", @@ -2659,29 +4068,46 @@ "Tokens_Required_Input_Error": "Ugyldige skrevet tokens.", "Tokens_Required_Input_Placeholder": "Tokens aktiva navn", "Topic": "Emne", + "Total_abandoned_chats": "Totalt antall forlatte chatter", + "Total_conversations": "Totalt antall samtaler", "Total_Discussions": "Totalt antall diskusjoner", "Total_messages": "Totalt antall meldinger", + "Total_rooms": "Totalt antall rom", "Total_Threads": "Totalt antall tråder", + "Total_visitors": "Totalt antall besøkende", + "TOTP Invalid [totp-invalid]": "Kode eller passord er ugyldig", "Transcript_Enabled": "Spør besøkende hvis de vil ha en transkripsjon etter at Chat er lukket", "Transcript_message": "Melding til å vise når du spør om transkripsjon", "Transcript_of_your_livechat_conversation": "Transkripsjon av livechat-samtalen.", + "Translate": "Oversett", "Translated": "oversatt", + "Translate_to": "Oversett til", "Translations": "Oversettelser", "Travel_and_Places": "Reise og steder", "Trigger_removed": "Trigger fjernet", "Trigger_Words": "Trigger Ord", "Triggers": "Triggers", + "Troubleshoot": "Feilsøk", "Troubleshoot_Disable_Notifications": "Deaktiver varslinger", "True": "Ja", + "Try_now": "Prøv nå", "Tuesday": "tirsdag", "Turn_OFF": "Skru av", "Turn_ON": "Slå på", + "Turn_on_video": "Slå på video", + "Turn_on_microphone": "Slå på mikrofon", + "Turn_off_microphone": "Slå av mikrofon", + "Turn_off_video": "Slå av video", + "Two Factor Authentication": "Tofaktorautentisering", "Two-factor_authentication": "Tofaktorautentisering", "Two-factor_authentication_disabled": "Tofaktorautentisering deaktivert", + "Two-factor_authentication_email": "Tofaktorautentisering via e-post", + "Two-factor_authentication_email_is_currently_disabled": "Tofaktorautentisering via e-post er deaktivert for øyeblikket ", "Two-factor_authentication_enabled": "Tofaktorautentisering aktivert", "Two-factor_authentication_is_currently_disabled": "Tofaktorautentisering er for øyeblikket deaktivert", "Two-factor_authentication_native_mobile_app_warning": "ADVARSEL: Når du har aktivert dette, vil du ikke kunne logge på de innkommende mobilappene (Rocket.Chat +) ved hjelp av passordet ditt før de implementerer 2FA.", "Type": "Type", + "Types": "Typer", "Type_your_email": "Skriv inn din e-postadresse", "Type_your_job_title": "Skriv inn jobbtittel", "Type_your_message": "Skriv inn meldingen din", @@ -2697,18 +4123,31 @@ "UI_Unread_Counter_Style": "Ulest counter stil", "UI_Use_Name_Avatar": "Bruk Full Name Initials til å generere Standard Avatar", "UI_Use_Real_Name": "Bruk Real Name", + "unable-to-get-file": "Kan ikke hente filen", "Unarchive": "Opphev arkivering", "unarchive-room": "Unarchive Room", "unarchive-room_description": "Tillatelse til å unarchive kanaler", + "Unassigned": "Ikke tildelt", + "unauthorized": "Ikke autorisert", + "Unavailable": "Utilgjengelig", + "Unblock": "Fjern blokkering", "Unblock_User": "Fjern blokkering av bruker", + "Uncheck_All": "Fjern avmerking for alle", + "Undefined": "Ikke definert", "Unfavorite": "Fjern fra favoritt", + "Unfollow_message": "Slutt å følge melding", "Unignore": "Ikke ignorer", "Uninstall": "Avinstaller", "Unit_removed": "Enhet fjernet", + "Unique_ID_change_detected_learn_more_link": "Les mer", + "Unique_ID_change_detected": "Oppdaget endring av unik ID", + "Unknown_User": "Ukjent bruker", + "Unlimited": "Ubegrenset", "Unmute_someone_in_room": "Slå på noen på rommet", "Unmute_user": "Slå av brukeren", "Unnamed": "unnamed", "Unpin_Message": "Unpin Message", + "Unprioritized": "Uprioritert", "Unread": "ulest", "Unread_Count": "Ulest antall", "Unread_Count_DM": "Ulest antall for direkte meldinger", @@ -2717,17 +4156,24 @@ "Unread_Rooms": "Uleste rom", "Unread_Rooms_Mode": "Uleste rommodus", "Unread_Tray_Icon_Alert": "Uread Tray Icon Alert", - "Unstar_Message": "Fjern Star", + "Unstar_Message": "Fjern stjerne", + "Update": "Oppdater", + "Update_to_version": "Oppdater til {{version}}", "Update_your_RocketChat": "Oppdater Rocket.Chat", "Updated_at": "Oppdatert på", + "Upload": "Last opp", + "Upload_private_app": "Last opp privat app", "Upload_file_description": "Filbeskrivelse", "Upload_file_name": "Filnavn", "Upload_file_question": "Last opp fil?", "Upload_Folder_Path": "Last opp mappebane", + "Upload_From": "Last opp fra {{name}}", "Upload_user_avatar": "Last opp avatar", "Uploading_file": "Laster opp fil ...", "Uptime": "oppetid", "URL": "URL", + "URLs": "URL-er", + "Use": "Bruk", "Use_account_preference": "Bruk kontoinnstillinger", "Use_Emojis": "Bruk Emojis", "Use_Global_Settings": "Bruk Globale innstillinger", @@ -2740,14 +4186,18 @@ "Use_url_for_avatar": "Bruk URL for avatar", "Use_User_Preferences_or_Global_Settings": "Bruk brukerinnstillinger eller globale innstillinger", "User": "Bruker", + "User_menu": "Brukermeny", "User__username__is_now_a_leader_of__room_name_": "Bruker {{username}} er nå leder av {{room_name}}", "User__username__is_now_a_moderator_of__room_name_": "Bruker {{username}} er nå en moderator av {{room_name}}", "User__username__is_now_an_owner_of__room_name_": "Bruker {{username}} er nå eier av {{room_name}}", + "User__username__muted_in_room__roomName__": "Bruker {{username}} er dempet i rom {{roomName}}", "User__username__removed_from__room_name__leaders": "Bruker {{username}} fjernet fra {{room_name}} ledere", "User__username__removed_from__room_name__moderators": "Bruker {{username}} fjernet fra {{room_name}} moderatorer", "User__username__removed_from__room_name__owners": "Bruker {{username}} fjernet fra {{room_name}} eiere", + "User__username__unmuted_in_room__roomName__": "Bruker {{username}}, er ikke lengre dempet i rommet {{roomName}}", "User_added": "Bruker lagt til", "User_added_by": "Bruker {{user_added}}lagt til av {{user_by}}.", + "User_added_to": "la til {{user_added}}", "User_added_successfully": "Bruker lagt til", "User_and_group_mentions_only": "Bruker og gruppe nevner bare", "User_default": "Brukerstandard", @@ -2758,6 +4208,7 @@ "User_has_been_ignored": "Brukeren er ignorert", "User_has_been_muted_in_s": "Brukeren har blitt dempet i%s", "User_has_been_removed_from_s": "Brukeren er fjernet fra%s", + "User_has_been_removed_from_team": "Brukeren er fjernet fra teamet", "User_has_been_unignored": "Brukeren ignoreres ikke lenger", "User_Info": "brukerinformasjon", "User_Interface": "Brukergrensesnitt", @@ -2766,23 +4217,31 @@ "User_is_now_an_admin": "Brukeren er nå en administrator", "User_is_unblocked": "Brukeren er ulåst", "User_joined_channel": "Har sluttet seg til kanalen.", + "User_joined_the_channel": "ble med i kanalen", + "User_joined_the_conversation": "ble med i samtalen", "User_left": "Har forlatt kanalen.", + "User_left_this_channel": "forlot kanalen", + "User_left_this_team": "forlot laget", "User_logged_out": "Brukeren er logget ut", "User_management": "brukeradministrasjon", "User_mentions_only": "Bruker nevner bare", "User_muted": "Bruker Muted", "User_muted_by": "Bruker {{user_muted}}dempet av {{user_by}}.", + "User_has_been_muted": "dempet {{user_muted}}", "User_not_found": "Bruker ikke funnet", "User_not_found_or_incorrect_password": "Bruker ikke funnet eller feil passord", "User_or_channel_name": "Bruker- eller kanalnavn", "User_Presence": "Brukerens tilstedeværelse", "User_removed": "Brukeren er fjernet", "User_removed_by": "Bruker {{user_removed}}fjernet av {{user_by}}.", + "User_has_been_removed": "fjernet {{user_removed}}", "User_sent_a_message_on_channel": "{{username}} sendte en melding på {{channel}}", "User_sent_a_message_to_you": "{{username}} sendte deg en melding", "user_sent_an_attachment": "{{user}} sendte et vedlegg", "User_Settings": "Brukerinstillinger", + "User_started_a_new_conversation": "{{username}} startet en ny samtale", "User_unmuted_by": "Bruker {{user_unmuted}}unmuted av {{user_by}}.", + "User_has_been_unmuted": "dempet {{user_unmuted}}", "User_unmuted_in_room": "Bruker uutløst i rommet", "User_updated_successfully": "Brukeren er oppdatert vellykket", "User_uploaded_a_file_on_channel": "{{username}} lastet opp en fil på {{channel}}", @@ -2814,43 +4273,68 @@ "Username_is_already_in_here": "`@%s` er allerede her inne.", "Username_Placeholder": "Vennligst skriv inn brukernavn ...", "Username_title": "Registrer brukernavn", + "Username_has_been_updated": "Brukernavnet ble oppdatert", "Username_wants_to_start_otr_Do_you_want_to_accept": "{{username}} vil starte OTR. Ønsker du å godta?", + "Username_name_email": "Brukernavn, navn eller e-post", "Users": "brukere", + "Users must use Two Factor Authentication": "Brukere må bruke tofaktorautentisering", "Users_added": "Brukerne har blitt lagt til", "Users_in_role": "Brukere i rollen", + "UTC_Timezone": "UTC-tidssone", "UTF8_Names_Slugify": "UTF8 Navn Slugify", "Videocall_enabled": "Videoanrop aktivert", "Validate_email_address": "Bekreft e-postadresse", + "Validation": "Validering", + "Value_messages": "{{value}} meldinger", + "Value_users": "{{value}} brukere", "Verification": "Bekreftelse", "Verification_Description": "Du kan bruke følgende plassholdere: \n - `[Verification_Url]` for verifikasjonsadressen. \n - [navn], [fname], [lname] for brukerens fulle navn, fornavn eller etternavn. \n - `[email]` for brukerens e-postadresse. \n - `[Site_Name]` og `[Site_URL]` for henholdsvis programnavnet og nettadressen. ", "Verification_Email": "Klikk herfor å bekrefte kontoen din.", + "Verification_email_body": "Vennligst klikk på knappen nedenfor for å bekrefte e-postadressen din.", "Verification_email_sent": "Verifikasjons e-post sendt", "Verification_Email_Subject": "[Site_Name] - Bekreft kontoen din", "Verified": "Verified", "Verify": "Bekreft", + "Verify_your_email": "Bekreft e-posten din", + "Verify_your_email_with_the_code_we_sent": "Bekreft e-posten din med koden vi har sendt", "Version": "Versjon", + "Version_version": "Versjon {{version}}", "Video_Chat_Window": "Video Chat", "Video_Conference": "Video konferanse", + "Video_Conference_Info": "Møteinformasjon", + "Video_Conference_Url": "Møte-URL", "Video_message": "Videomelding", "Videocall_declined": "Videoanrop avslått.", + "video_conference_started": "_Startet en samtale._", + "video_conference_ended": "_Samtalen har sluttet._", + "video_livechat_started": "_Startet en videosamtale._", + "video_direct_calling": "_Ringer._", "View_mode": "Visningsmodus", "View_All": "Se alle medlemmer", + "View_channels": "Se kanaler", "View_Logs": "Se logger", + "View_the_Logs_for": "Se loggene for: \"{{name}}\"", + "view-all-teams": "Se alle team", + "view-all-teams_description": "Tillatelse til å se alle team", + "view-all-team-channels": "Se alle teamkanaler", "view-broadcast-member-list": "Se Medlemsliste i Broadcast Room", "view-c-room": "Se offentlig kanal", "view-c-room_description": "Tillatelse til å vise offentlige kanaler", "view-d-room": "Se direkte meldinger", "view-d-room_description": "Tillatelse til å vise direkte meldinger", + "view-device-management": "Se enhetsstyring", "view-full-other-user-info": "Se full annen brukerinformasjon", "view-full-other-user-info_description": "Tillatelse til å se hele profilen til andre brukere, inkludert kontoopprettelsesdato, siste innlogging, etc.", "view-history": "Se historikk", "view-history_description": "Tillatelse til å se kanalhistorikken", + "onboarding.component.form.action.registerNow": "Registrer deg nå", "view-join-code": "Vis Bli medlem", "view-join-code_description": "Tillatelse til å vise kanalen bli med koden", "view-joined-room": "Se tilknyttet rom", "view-joined-room_description": "Tillatelse til å vise de tilkoblede kanalene", "view-l-room": "Se Livechat-rom", "view-l-room_description": "Tillatelse til å vise livechat-kanaler", + "onboarding.page.awaitingConfirmation.subtitle": "Vi har sendt deg en e-post til {{emailAddress}} med en bekreftelseslenke. Vennligst bekreft at sikkerhetskoden nedenfor samsvarer med den i e-posten.", "view-livechat-manager": "Se Livechat Manager", "view-livechat-manager_description": "Tillatelse til å vise andre livechat-ledere", "view-livechat-rooms": "Se Livechat-rom", @@ -2873,11 +4357,15 @@ "Viewing_room_administration": "Vise rom administrasjon", "Visibility": "Synlighet", "Visible": "Synlig", + "Visible_To_Workspace": "Synlig for arbeidsområdet", "Visitor": "Besøkende", "Visitor_Info": "Visitor Info", + "Visitor_not_found": "Besøkende ikke funnet", "Visitor_Navigation": "Visitor Navigasjon", "Visitor_page_URL": "URL for besøkende siden", "Visitor_time_on_site": "Besøkende tid på stedet", + "Voip_call_duration": "Samtalen varte i {{duration}}", + "Voip_call_ended_unexpectedly": "Samtalen ble avbrutt uventet: {{reason}}", "Wait_activation_warning": "Før du kan logge inn, må kontoen din aktiveres manuelt av en administrator.", "Waiting_queue": "Kø", "Waiting_queue_message": "Melding for kø", @@ -2891,6 +4379,7 @@ "Webdav_Server_URL": "WebDAV Server Access URL", "Webdav_Username": "WebDAV Brukernavn", "Webhook_URL": "Webhook URL", + "Webhook_URL_not_set": "Webhook-URL er ikke angitt", "Webhooks": "Webhooks", "WebRTC_direct_audio_call_from_%s": "Direkte lydanrop fra%s", "WebRTC_direct_video_call_from_%s": "Direkte videosamtale fra%s", @@ -2902,27 +4391,39 @@ "WebRTC_monitor_call_from_%s": "Overvåk anrop fra%s", "WebRTC_Servers": "STUN / TURN servere", "WebRTC_Servers_Description": "En liste over STUN- og TURN-servere adskilt med komma. \n Brukernavn, passord og port er tillatt i formatet «brukernavn:passord@stun:vert:port` eller `brukernavn:passord@tur:vert:port`.", + "WebRTC_call_ended_message": " Samtalen ble avsluttet {{endTime}} – varte {{callDuration}}", "Website": "nettsted", "Wednesday": "onsdag", "Weekly_Active_Users": "Ukentlige aktive brukere", "Welcome": "Velkommen %s.", + "Welcome_to": "Velkommen til [Site_Name]", + "Welcome_to_workspace": "Velkommen til {{Site_Name}}", "Welcome_to_the": "Velkommen til", + "Why_did_you_chose__score__": "Hvorfor valgte du {{score}}?", "Why_do_you_want_to_report_question_mark": "Hvorfor vil du rapportere?", "will_be_able_to": "vil kunne", + "Without_SLA": "Uten SLA", "Worldwide": "Verdensomspennende", "Would_you_like_to_return_the_inquiry": "Vil du returnere forespørselen?", "Yes": "Ja", "Yes_archive_it": "Ja, arkiver det!", "Yes_clear_all": "Ja, fjern alt!", + "Yes_continue": "Ja, fortsett!", "Yes_delete_it": "Ja, slett det!", "Yes_hide_it": "Ja, skjul det!", "Yes_leave_it": "Ja, la det være!", "Yes_mute_user": "Ja, stum bruker!", "Yes_prune_them": "Ja, beskjære dem!", + "Yes_pin_message": "Ja, fest melding", "Yes_remove_user": "Ja, fjern bruker!", "Yes_unarchive_it": "Ja, unarchive det!", "yesterday": "i går", "You": "Du", + "You_reacted_with": "Du reagerte med {{emoji}}", + "Users_reacted_with": "{{users}} reagerte med {{emoji}}", + "Users_and_more_reacted_with": "{{users}} og {{counter}} andre reagerte med {{emoji}}", + "You_and_users_Reacted_with": "Du og {{users}} reagerte med {{emoji}}", + "You_users_and_more_Reacted_with": "Du, {{users}} og {{counter}} andre reagerte med {{emoji}}", "you_are_in_preview_mode_of": "Du er i forhåndsvisningsmodus av kanal # {{room_name}}", "You_are_logged_in_as": "Du er innlogget som", "You_are_not_authorized_to_view_this_page": "Du er ikke autorisert til å vise denne siden.", @@ -2932,10 +4433,14 @@ "You_can_use_webhooks_to_easily_integrate_livechat_with_your_CRM": "Du kan bruke webhooks for enkelt å integrere livechat med CRM.", "You_cant_leave_a_livechat_room_Please_use_the_close_button": "Du kan ikke forlate et livechat-rom. Vennligst bruk lukkeknappen.", "You_have_been_muted": "Du har blitt dempet og kan ikke snakke i dette rommet", + "You_have_been_removed_from__roomName_": "Du har blitt fjernet fra rommet {{roomName}}", "You_have_n_codes_remaining": "Du har {{number}} koder igjen.", "You_have_not_verified_your_email": "Du har ikke bekreftet e-posten din.", "You_have_successfully_unsubscribed": "Du har sluttet å abonnere fra vår mailliste.", "You_must_join_to_view_messages_in_this_channel": "Du må bli med for å vise meldinger i denne kanalen", + "You_mentioned___mentions__but_theyre_not_in_this_room": "Du nevnte {{mentions}}, men de er ikke i dette rommet.", + "You_mentioned___mentions__but_theyre_not_in_this_room_You_can_ask_a_room_admin_to_add_them": "Du nevnte {{mentions}}, men de er ikke i dette rommet. Du kan be en romadministrator om å legge dem til.", + "You_mentioned___mentions__but_theyre_not_in_this_room_You_let_them_know_via_dm": "Du nevnte {{mentions}}, men de er ikke i dette rommet. Du gir dem beskjed via DM.", "You_need_confirm_email": "Du må bekrefte din e-post for å logge inn!", "You_need_install_an_extension_to_allow_screen_sharing": "Du må installere en utvidelse for å tillate skjermdeling", "You_need_to_change_your_password": "Du må endre passordet ditt", @@ -2951,30 +4456,112 @@ "Your_email_has_been_queued_for_sending": "E-posten din har vært i kø for å sende", "Your_entry_has_been_deleted": "Oppføringen din er slettet.", "Your_file_has_been_deleted": "Filen din er slettet.", + "Your_invite_link_will_expire_after__usesLeft__uses": "Invitasjonslenken din utløper etter {{usesLeft}} anvendelser.", + "Your_invite_link_will_expire_on__date__": "Invitasjonslenken din utløper {{date}}.", + "Your_invite_link_will_expire_on__date__or_after__usesLeft__uses": "Invitasjonskoblingen din utløper {{date}} eller etter {{usesLeft}} anvendelser.", "your_message": "din beskjed", "your_message_optional": "meldingen din (valgfritt)", "Your_password_is_wrong": "Ditt passord er feil!", "Your_push_was_sent_to_s_devices": "Din push ble sendt til%s-enheter", + "Your_request_to_join__roomName__has_been_made_it_could_take_up_to_15_minutes_to_be_processed": "Din forespørsel om å bli med i {{roomName}} er opprettet. Det kan ta opptil 15 minutter å behandle den. Du vil bli varslet når den er klar til bruk.", "Your_server_link": "Din serverkobling", "Your_workspace_is_ready": "Ditt arbeidsområde er klar til bruk 🎉", + "Youre_not_a_part_of__channel__and_I_mentioned_you_there": "Du er ikke en del av {{channel}} og jeg nevnte deg der", + "registration.page.login.errors.wrongCredentials": "Bruker finnes ikke eller så er passordet feil", + "registration.page.login.errors.invalidEmail": "Ugyldig e-post", "registration.page.registration.waitActivationWarning": "Før du kan logge inn, må kontoen din aktiveres manuelt av en administrator.", + "registration.page.login.register": "Ny her? <1>Opprett en konto", "registration.page.resetPassword.sent": "Hvis denne e-posten er registrert, sender vi instruksjoner om hvordan du tilbakestiller passordet ditt. Hvis du ikke mottar en epost, vennligst kom tilbake og prøv igjen.", + "registration.page.resetPassword.sendInstructions": "Send instruksjoner", + "registration.page.resetPassword.errors.invalidEmail": "Ugyldig epost", + "registration.page.guest.chooseHowToJoin": "Velg hvordan du vil bli med", + "registration.page.guest.loginWithRocketChat": "Logg inn med Rocket.Chat", + "registration.page.guest.continueAsGuest": "Fortsett som gjest", + "registration.component.welcome": "Velkommen til <1>Rocket.Chat arbeidsområdet", "registration.component.login": "Logg inn", "registration.component.login.userNotFound": "Bruker ikke funnet", + "registration.component.login.incorrectPassword": "feil passord", + "registration.component.switchLanguage": "Bytt til <1>{{name}}", "registration.component.resetPassword": "Tilbakestilling av passord", "registration.component.form.username": "Brukernavn", "registration.component.form.name": "Navn", + "registration.component.form.nameOptional": "Valgfritt navn", + "registration.component.form.createAnAccount": "Opprett en konto", + "registration.component.form.userAlreadyExist": "Brukernavn finnes allerede. Vennligst prøv et annet brukernavn.", "registration.component.form.emailAlreadyExists": "E-post finnes allerede", "registration.component.form.usernameAlreadyExists": "Brukernavn finnes allerede. Vennligst prøv et nytt brukernavn.", "registration.component.form.invalidEmail": "E-posten som er oppgitt, er ugyldig", "registration.component.form.email": "E-post", + "registration.component.form.emailPlaceholder": "eksempel@eksempel.no", "registration.component.form.password": "Passord", "registration.component.form.divider": "eller", "registration.component.form.submit": "Sende inn", + "registration.component.form.joinYourTeam": "Bli med teamet ditt", "registration.component.form.reasonToJoin": "Årsak til å bli med", "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", - "Enterprise": "Bedriften", - "UpgradeToGetMore_engagement-dashboard_Title": "Analytics" -} + "onboarding.component.form.action.registerWorkspace": "Registrer arbeidsområde", + "onboarding.component.form.action.registerOffline": "Registrer deg offline", + "onboarding.component.form.action.completeRegistration": "Fullfør registrering", + "onboarding.component.emailCodeFallback": "Ikke mottatt e-post? <1>Send på nytt eller <3>Endre e-post.", + "onboarding.form.awaitConfirmationForm.content.securityCode": "Sikkerhetskode", + "onboarding.form.organizationInfoForm.subtitle": "Vi trenger å vite hvem du er.", + "onboarding.form.registeredServerForm.title": "Registrer arbeidsområdet ditt", + "subscription.callout.guestUsers": "gjester", + "subscription.callout.roomsPerGuest": "maks gjest per rom", + "subscription.callout.privateApps": "installerte private apper", + "subscription.callout.monthlyActiveContacts": "månedlige aktive kontakter", + "Upload_anyway": "Last opp allikevel", + "App_limit_reached": "Appgrensen er nådd", + "App_limit_exceeded": "Appgrensen er overskredet", + "Private_apps_limit_reached": "Grensen for private apper er nådd", + "Private_apps_limit_exceeded": "Grensen for private apper er overskredet", + "Community_Private_apps_limit_exceeded": "Grensen for Community-apper er overskredet.", + "Theme_high_contrast": "Høy kontrast", + "Highlighted_chosen_word": "Uthevet valgt ord", + "Create_a_password": "Opprett et passord", + "Create_an_account": "Opprett en konto", + "Get_all_apps": "Få alle appene teamet ditt trenger", + "No_private_apps_installed": "Ingen private apper installert", + "Contact_email": "Kontakt-epost ", + "Customer": "Kunde", + "Time": "Tid", + "Undo_request": "Angre forespørsel", + "No_permission": "Ingen tillatelse", + "User_Status": "Brukerstatus", + "New_custom_status": "Ny egendefinert status", + "Service_disabled": "Tjenesten er nå deaktivert", + "User_status_disabled_learn_more": "Brukerstatus er deaktivert", + "Go_to_workspace_settings": "Gå til arbeidsområdeinnstillinger", + "User_status_temporarily_disabled": "Brukerstatus er midlertidig deaktivert", + "Use_token": "Bruk token", + "Disconnected": "Frakoblet", + "Registration_Token": "Registreringstoken", + "RegisterWorkspace_Button": "Registrer arbeidsområde", + "RegisterWorkspace_Features_Marketplace_Disconnect": "Det vil ikke lenger være mulig å installere apper.", + "RegisterWorkspace_Setup_Steps": "Steg {{step}} av {{numberOfSteps}}", + "RegisterWorkspace_Setup_Have_Account_Title": "Har en konto?", + "RegisterWorkspace_Setup_No_Account_Title": "Har du ikke en konto?", + "RegisterWorkspace_Syncing_Complete": "Synkronisering fullført", + "Uninstall_grandfathered_app": "Vil du avinstallere {{appName}}?", + "All_rooms": "Alle rom", + "All_visible": "Alle synlige", + "Filter_by_room": "Filtrer etter romtype", + "Filter_by_visibility": "Filtrer etter synlighet", + "Premium": "Premium", + "Enterprise": "Premium", + "Solve_issues": "Løs problemer", + "Outdated": "Utdatert", + "Latest": "Siste", + "New_version_available": "Ny versjon tilgjengelig", + "trial": "prøve", + "Subscription": "Abonnement", + "Private_apps": "Privatapper", + "n_days_left": "{{n}} dager igjen", + "Contact_sales": "Kontakt salg", + "Finish_purchase": "Fullfør kjøpet", + "free_per_month_user": "$0 per måned per bruker", + "UpgradeToGetMore_engagement-dashboard_Title": "Analytics", + "Buy_more": "Kjøp mer" +} \ 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 5ab6fe1c8912..80c503041a17 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json @@ -2,14 +2,14 @@ "500": "Wewnętrzny błąd serwera", "__count__empty_rooms_will_be_removed_automatically": "Liczba pokojów do automatycznego usunięcia: {{count}}.", "__count__empty_rooms_will_be_removed_automatically__rooms__": "Puste pokoje ({{count}}) zostaną automatycznie usunięte:
{{rooms}}.", - "__count__message_pruned_one": "{{count}} wiadomość(i) usuniętych", "__count__message_pruned_few": "{{count}} wiadomość(i) usuniętych", + "__count__message_pruned_one": "{{count}} wiadomość(i) usuniętych", "__count__message_pruned_many": "{{count}} wiadomość(i) usuniętych", "__count__message_pruned_other": "{{count}} wiadomość(i) usuniętych", - "__usersCount__member_joined_one": "+ {{count}} członek dołączył", "__usersCount__member_joined_few": "+ {{count}} członków dołączyło", - "__usersCount__member_joined_many": "+ {{count}} członków dołączyło", + "__usersCount__member_joined_one": "+ {{count}} członek dołączył", "__usersCount__member_joined_other": "+ {{count}} członków dołączyło", + "__usersCount__member_joined_many": "+ {{count}} członków dołączyło", "__usersCount__people_will_be_invited": "{{usersCount}} ludzi zostanie zostanie zaproszonych", "__username__is_no_longer__role__defined_by__user_by_": "Użytkownik {{username}} nie ma już roli {{role}}; zmienił to użytkownik {{user_by}}", "__username__was_set__role__by__user_by_": "Użytkownik {{username}} otrzymał rolę {{role}} od użytkownika {{user_by}}", @@ -636,13 +636,16 @@ "Auth_Token": "Token uwierzytelniający", "Authentication": "Uwierzytelnianie", "Author": "Autor", + "Calls_in_queue_few": "{{count}} połączeń w kolejce", "Author_Information": "Informacje o autorze", + "Calls_in_queue_many": "{{count}} połączeń w kolejce", "Author_Site": "Strona autora", "Authorization_URL": "Adres URL uwierzytelniania", "Authorize": "Autoryzuj", "Auto_Load_Images": "Automatycznie ładuj obrazy", "Auto_Selection": "Automatyczny wybór", "Auto_Translate": "Tłumacz automatycznie", + "Calls_in_queue": "Połączeń w kolejce: {{calls}}", "auto-translate": "Tłumacz automatycznie", "auto-translate_description": "Uprawnienie do używania narzędzia do tłumaczenia automatycznego", "Automatic_Translation": "Tłumaczenie automatyczne", @@ -775,8 +778,6 @@ "Calls": "Połączenia", "Calls_in_queue_zero": "Kolejka jest pusta", "Calls_in_queue_one": "Połączeń w kolejce: {{count}}", - "Calls_in_queue_few": "{{count}} połączeń w kolejce", - "Calls_in_queue_many": "{{count}} połączeń w kolejce", "Calls_in_queue_other": "{{count}} połączeń w kolejce", "Call_declined": "Połączenie odrzucone!", "Call_Information": "Informacje o połączeniu", @@ -1013,7 +1014,6 @@ "Connection_Closed": "Połączenie zamknięte", "Connection_Reset": "Reset połączenia", "Connection_error": "Błąd połączenia", - "LDAP_Connection_successful": "Nawiązano połączenie z LDAP", "Connection_failed": "Nie można nawiązać połączenia z LDAP", "Connectivity_Services": "Usługi łączności", "Consulting": "Doradztwo", @@ -2568,7 +2568,9 @@ "Language_Not_set": "Brak konkretów", "Language_Polish": "Polski", "Language_Portuguese": "Portugalski", + "message_counter_few": "{{count}} wiadomości", "Language_Romanian": "Rumuński", + "message_counter_many": "{{count}} wiadomości", "Language_Russian": "Rosyjski", "Language_Slovak": "Słowacki", "Language_Slovenian": "Słoweński", @@ -2608,6 +2610,7 @@ "LDAP_Connection": "Połączenie", "LDAP_Connection_Authentication": "Autentykacja", "LDAP_Connection_Encryption": "Szyforwanie", + "LDAP_Connection_successful": "Nawiązano połączenie z LDAP", "LDAP_Connection_Timeouts": "Timeout'y", "LDAP_UserSearch": "Wyszukiwanie użytkowników", "LDAP_UserSearch_Filter": "Filtr wyszukiwania", @@ -2643,6 +2646,8 @@ "LDAP_Background_Sync_Import_New_Users": "Synchronizacja tła Importuj nowych użytkowników", "LDAP_Background_Sync_Import_New_Users_Description": "Zaimportuje wszystkich użytkowników (w oparciu o kryteria filtru), które istnieją w LDAP i nie istnieje w Rocket.Chat", "LDAP_Background_Sync_Interval": "Interwał synchronizacji tła", + "meteor_status_reconnect_in_few": "spróbuj jeszcze raz za {{count}} sekund...", + "meteor_status_reconnect_in_many": "spróbuj jeszcze raz za {{count}} sekund...", "LDAP_Background_Sync_Interval_Description": "Odstęp między synchronizacjami. Przykład \"co 24 godziny\" lub \"pierwszego dnia tygodnia\", więcej przykładów w [Cron Text Parser] (http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Aktualizacja synchronizacji w tle Istniejących użytkowników", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Zsynchronizuje awatar, pola, nazwę użytkownika itp. (W zależności od konfiguracji) wszystkich użytkowników już zaimportowanych z LDAP na każdy ** Interwał synchronizacji **", @@ -3074,8 +3079,6 @@ "Message_Code_highlight": "Lista języków podświetlania kodu", "Message_Code_highlight_Description": "Comma separated list of languages (all supported languages at [highlight.js](https://github.com/highlightjs/highlight.js/tree/11.6.0#supported-languages)) that will be used to highlight code blocks", "message_counter_one": "{{count}} wiadomość", - "message_counter_few": "{{count}} wiadomości", - "message_counter_many": "{{count}} wiadomości", "message_counter_other": "{{count}} wiadomości", "Message_DateFormat": "Format daty", "Message_DateFormat_Description": "Zobacz także: [Moment.js](http://momentjs.com/docs/#/displaying/format/)", @@ -3177,8 +3180,6 @@ "meteor_status_failed": "Serwer nie mógł się połączyć", "meteor_status_offline": "Tryb offline.", "meteor_status_reconnect_in_one": "spróbuj jeszcze raz za chwilę...", - "meteor_status_reconnect_in_few": "spróbuj jeszcze raz za {{count}} sekund...", - "meteor_status_reconnect_in_many": "spróbuj jeszcze raz za {{count}} sekund...", "meteor_status_reconnect_in_other": "spróbuj jeszcze raz za {{count}} sekund...", "meteor_status_try_now_offline": "Połącz ponownie", "meteor_status_try_now_waiting": "Spróbuj teraz", @@ -3960,6 +3961,7 @@ "Running_Instances": "Ilość uruchomionych instancji", "Runtime_Environment": "Środowisko uruchomieniowe", "S_new_messages_since_s": "%s nowych wiadomości od %s", + "S_new_messages": "%s nowych wiadomości", "Same_As_Token_Sent_Via": "Taki sam jak \"Token wysłany przez\"", "Same_Style_For_Mentions": "Ten sam styl do wzmianek", "SAML": "SAML", @@ -5353,4 +5355,4 @@ "Enterprise": "Enterprise", "UpgradeToGetMore_engagement-dashboard_Title": "Analityka", "UpgradeToGetMore_auditing_Title": "Audyt wiadomoś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 2338d2d21f2c..08b85291398a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -10,8 +10,8 @@ "__count__tags__and__count__conversations__period__": "{{count}} tags e {{conversations}} conversas, {{period}}", "__departments__departments_and__count__conversations__period__": "{{departments}} departmentos e {{count}} conversas, {{period}}", "__usersCount__member_joined_one": "+ um membro entrou", - "__usersCount__member_joined_many": "+ {{count}} membros entraram", "__usersCount__member_joined_other": "+ {{count}} membros entraram", + "__usersCount__member_joined_many": "+ {{count}} membros entraram", "__usersCount__people_will_be_invited": "{{usersCount}} usuários vão ser convidados", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} não pertence mais a {{role}}, por {{user_by}}", "__username__was_set__role__by__user_by_": "{{username}} foi definido como {{role}} por {{user_by}}", @@ -621,6 +621,7 @@ "Authentication": "Autenticação", "Author": "Autor", "Author_Information": "Informação sobre o autor", + "Calls_in_queue_many": "{{count}} chamadas na fila", "Author_Site": "Página do autor", "Authorization_URL": "URL de autorização", "Authorize": "Autorizar", @@ -629,6 +630,7 @@ "Auto_Load_Images": "Carregar imagens automaticamente", "Auto_Selection": "Seleção automática", "Auto_Translate": "Traduzir automaticamente", + "Calls_in_queue": "{{calls}} chamadas na fila", "auto-translate": "Traduzir automaticamente", "auto-translate_description": "Permissão para usar a ferramenta de tradução automática", "Automatic_Translation": "Tradução automática", @@ -752,7 +754,6 @@ "Call_Center_Description": "Configure o canal de voz no Rocket.Chat", "Calls_in_queue_zero": "A fila está Vazia", "Calls_in_queue_one": "{{count}} chamadas na fila", - "Calls_in_queue_many": "{{count}} chamadas na fila", "Calls_in_queue_other": "{{count}} chamadas na fila", "Call_declined": "Chamada recusada!", "Call_Information": "Informação da chamada", @@ -969,7 +970,6 @@ "Connection_Closed": "Conexão fechada", "Connection_Reset": "Redefinição de conexão", "Connection_error": "Erro de conexão", - "LDAP_Connection_successful": "Conexão com LDAP bem-sucedida", "Connection_failed": "Falha na conexão com o LDAP", "Connectivity_Services": "Serviços de conectividade", "Consulting": "Consultoria", @@ -2425,6 +2425,7 @@ "Language_Polish": "Polonês", "Language_Portuguese": "Português", "Language_Romanian": "Romeno", + "message_counter_many": "{{count}} mensagens", "Language_Russian": "Russo", "Language_Slovak": "Eslovaco", "Language_Slovenian": "Esloveno", @@ -2465,6 +2466,7 @@ "LDAP_Connection": "Conexão", "LDAP_Connection_Authentication": "Autenticação", "LDAP_Connection_Encryption": "Criptografia", + "LDAP_Connection_successful": "Conexão com LDAP bem-sucedida", "LDAP_Connection_Timeouts": "Tempos limite", "LDAP_UserSearch": "Pesquisa de usuários", "LDAP_UserSearch_Filter": "Filtro de pesquisa", @@ -2500,6 +2502,7 @@ "LDAP_Background_Sync_Import_New_Users": "Sincronização de fundo da importação de novos usuários", "LDAP_Background_Sync_Import_New_Users_Description": "Importará todos os usuários (com base em seus critérios de filtro) que existem no LDAP e não existe em Rocket.Chat", "LDAP_Background_Sync_Interval": "Intervalo de sincronização de fundo", + "meteor_status_reconnect_in_many": "tentando novamente em {{count}} segundos...", "LDAP_Background_Sync_Interval_Description": "O intervalo entre as sincronizações. Exemplo de \"a cada 24 horas\" ou \"no primeiro dia da semana\", mais exemplos em [Cron Text Parser] (http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Atualização da sincronização de plano de fundo de usuários existentes", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Vai sincronizar o avatar, os campos, o nome de usuário, etc. (com base na sua configuração) de todos os usuários já importados do LDAP em cada ** intervalo de sincronização **", @@ -2901,7 +2904,6 @@ "Message_Code_highlight": "Lista de idiomas com destaque de código", "Message_Code_highlight_Description": "Lista de idiomas separados por vírgulas (todos os idiomas suportados em [highlight.js](https://github.com/highlightjs/highlight.js/tree/11.6.0#supported-languages)), que serão usados para destacar os blocos de código", "message_counter_one": "{{count}} mensagem", - "message_counter_many": "{{count}} mensagens", "message_counter_other": "{{count}} mensagens", "Message_DateFormat": "Formato de data", "Message_DateFormat_Description": "Veja também: [Moment.js](http://momentjs.com/docs/#/displaying/format/)", @@ -2995,7 +2997,6 @@ "meteor_status_failed": "A conexão com o servidor falhou", "meteor_status_offline": "Modo offline.", "meteor_status_reconnect_in_one": "tentando novamente em um segundo...", - "meteor_status_reconnect_in_many": "tentando novamente em {{count}} segundos...", "meteor_status_reconnect_in_other": "tentando novamente em {{count}} segundos...", "meteor_status_try_now_offline": "Conectar novamente", "meteor_status_try_now_waiting": "Tentar agora", @@ -3709,6 +3710,7 @@ "Running_Instances": "Instâncias em execução", "Runtime_Environment": "Ambiente de execução", "S_new_messages_since_s": "%s novas mensagens desde %s", + "S_new_messages": "%s novas mensagens", "Same_As_Token_Sent_Via": "O mesmo que \"Token enviado via\"", "Same_Style_For_Mentions": "O mesmo estilo para menções", "SAML": "SAML", @@ -4984,4 +4986,4 @@ "Enterprise": "Enterprise", "UpgradeToGetMore_engagement-dashboard_Title": "Analytics", "UpgradeToGetMore_auditing_Title": "Auditoria de mensagem" -} +} \ 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 9bc920849c8c..aabf52554792 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json @@ -2460,6 +2460,7 @@ "Running_Instances": "Instâncias em execução", "Runtime_Environment": "Ambiente de execução", "S_new_messages_since_s": "%s novas mensagens desde %s", + "S_new_messages": "%s novas mensagens", "Same_As_Token_Sent_Via": "O mesmo que \"Token Sent Via\"", "Same_Style_For_Mentions": "O mesmo estilo para menções", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json index 8a966903515c..8bbe25f79d04 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json @@ -2119,6 +2119,7 @@ "Running_Instances": "alergare Instanțe", "Runtime_Environment": "Mediu de rulare", "S_new_messages_since_s": "%s mesaje noi de la %s", + "S_new_messages": "%s noi mesaje", "Same_As_Token_Sent_Via": "La fel ca \"Token Trimise Via\"", "Same_Style_For_Mentions": "Același stil pentru mențiuni", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json index 2c734bdab144..0b2fe35714b1 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json @@ -2,14 +2,14 @@ "500": "Внутренняя ошибка сервера", "__count__empty_rooms_will_be_removed_automatically": "{{count}}пустые комнаты будут удалены автоматически.", "__count__empty_rooms_will_be_removed_automatically__rooms__": "{{count}} пустых чатов будет удалено автоматически:
{{rooms}}.", - "__count__message_pruned_one": "{{count}} сообщение удалено", "__count__message_pruned_few": "{{count}} сообщений удалено", + "__count__message_pruned_one": "{{count}} сообщение удалено", "__count__message_pruned_many": "{{count}} сообщений удалено", "__count__message_pruned_other": "{{count}} сообщений удалено", - "__usersCount__member_joined_one": "+ {{count}} участников присоединилось", "__usersCount__member_joined_few": "+ {{count}} участников присоединилось", - "__usersCount__member_joined_many": "+ {{count}} участников присоединилось", + "__usersCount__member_joined_one": "+ {{count}} участников присоединилось", "__usersCount__member_joined_other": "+ {{count}} участников присоединилось", + "__usersCount__member_joined_many": "+ {{count}} участников присоединилось", "__usersCount__people_will_be_invited": "{{usersCount}} человек будет приглашено", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} больше не {{role}} по решению {{user_by}}", "__username__was_set__role__by__user_by_": "{{username}} был установлен {{role}} по решению {{user_by}}", @@ -477,9 +477,13 @@ "App_Information": "Информация о приложении", "Apps_context_enterprise": "Организация", "App_Installation": "Установка приложения", + "Apps_Count_Enabled_few": "{{count}} приложений(-я) включено", + "Apps_Count_Enabled_many": "{{count}} приложений(-я) включено", "App_not_enabled": "Приложение не включено", + "Private_Apps_Count_Enabled_few": "{{count}} приватных приложений включено", "App_not_found": "Приложение не найдено", "App_status_auto_enabled": "Включено", + "Private_Apps_Count_Enabled_many": "{{count}} приватных приложений включено", "App_status_constructed": "построенный", "App_status_disabled": "Отключено", "App_status_error_disabled": "Отключено: неизвестная ошибка", @@ -507,12 +511,8 @@ "Apps_context_requested": "Запрошено", "Apps_context_private": "Приватные приложения", "Apps_Count_Enabled_one": "{{count}} приложение включено", - "Apps_Count_Enabled_few": "{{count}} приложений(-я) включено", - "Apps_Count_Enabled_many": "{{count}} приложений(-я) включено", "Apps_Count_Enabled_other": "{{count}} приложений(-я) включено", "Private_Apps_Count_Enabled_one": "{{count}} приватное приложение включено", - "Private_Apps_Count_Enabled_few": "{{count}} приватных приложений включено", - "Private_Apps_Count_Enabled_many": "{{count}} приватных приложений включено", "Private_Apps_Count_Enabled_other": "{{count}} приватных приложений включено", "Apps_Count_Enabled_tooltip": "В рабочих пространствах Community Edition можно использовать до {{number}} {{context}} приложений", "Apps_Engine_Version": "Версия движка приложений", @@ -666,13 +666,16 @@ "Auth_Token": "Токен авторизации", "Authentication": "Аутентификация", "Author": "Автор", + "Calls_in_queue_few": "{{count}} Звонков в очереди", "Author_Information": "Информация об авторе", + "Calls_in_queue_many": "{{count}} Звонков в очереди", "Author_Site": "Автор", "Authorization_URL": "Авторизация URL-адреса", "Authorize": "Авторизовать", "Auto_Load_Images": "Автозагрузка изображений", "Auto_Selection": "Автоматический выбор", "Auto_Translate": "Авто-перевод", + "Calls_in_queue": "Вызовов в очереди: {{calls}}", "auto-translate": "Автоматический перевод", "auto-translate_description": "Разрешение пользоваться автоматическим переводом", "Automatic_Translation": "Автоматический перевод", @@ -806,8 +809,6 @@ "Calls": "Звонки", "Calls_in_queue_zero": "Очередь пуста", "Calls_in_queue_one": "Вызовов в очереди: {{count}}", - "Calls_in_queue_few": "{{count}} Звонков в очереди", - "Calls_in_queue_many": "{{count}} Звонков в очереди", "Calls_in_queue_other": "{{count}} Звонков в очереди", "Call_declined": "Вызов отклонен!", "Call_Information": "Информация о вызове", @@ -1038,7 +1039,6 @@ "Connection_Closed": "Соединение закрыто", "Connection_Reset": "Сброс соединения", "Connection_error": "Ошибка подключения", - "LDAP_Connection_successful": "Подключение к LDAP успешное", "Connection_failed": "Сбой подключения LDAP", "Connectivity_Services": "Connectivity Services", "Consulting": "Консалтинг", @@ -2525,7 +2525,9 @@ "Language_Not_set": "Нет конкретных", "Language_Polish": "Польский", "Language_Portuguese": "Португальский", + "message_counter_few": "{{count}} сообщения", "Language_Romanian": "Румынский", + "message_counter_many": "{{count}} сообщения", "Language_Russian": "Русский", "Language_Slovak": "Словацкий", "Language_Slovenian": "Словенский", @@ -2562,6 +2564,7 @@ "LDAP_Connection": "Подключение", "LDAP_Connection_Authentication": "Аутентификация", "LDAP_Connection_Encryption": "Шифрование", + "LDAP_Connection_successful": "Подключение к LDAP успешное", "LDAP_Connection_Timeouts": "Время ожидания", "LDAP_UserSearch": "Поиск пользователя", "LDAP_UserSearch_Filter": "Фильтр поиска", @@ -2597,6 +2600,8 @@ "LDAP_Background_Sync_Import_New_Users": "Фоновая синхронизация импортирует новых пользователей", "LDAP_Background_Sync_Import_New_Users_Description": "Импортирует всех пользователей (на основе критериев вашего фильтра), которые существуют в LDAP, и не существует в Rocket.Chat", "LDAP_Background_Sync_Interval": "Интервал фоновой синхронизации", + "meteor_status_reconnect_in_few": "пытается снова через {{count}} секунд ...", + "meteor_status_reconnect_in_many": "пытается снова через {{count}} секунд ...", "LDAP_Background_Sync_Interval_Description": "Интервал между синхронизациями. Пример: `каждые 24 часа` или `в первый день недели`, больше примеров в [Cron Text Parser] (http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Фоновая синхронизация обновляет сущестующих пользователей", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Будут синхронизироваться аватар, поля, логин итд (на основе вашей конфигурации) всех пользователей уже импортированных из LDAP каждый **Интервал синхронизации**", @@ -2996,8 +3001,6 @@ "Message_Code_highlight": "Список языков, используемых для выделения кода", "Message_Code_highlight_Description": "Список языков с запятыми-разделителями (все поддерживаемые языки представлены на странице [highlight.js](https://github.com/highlightjs/highlight.js/tree/11.6.0#supported-languages)), которые будут использоваться для выделения блоков кода", "message_counter_one": "{{count}} сообщение", - "message_counter_few": "{{count}} сообщения", - "message_counter_many": "{{count}} сообщения", "message_counter_other": "{{count}} сообщения", "Message_DateFormat": "Формат даты", "Message_DateFormat_Description": "Смотрите также: [Moment.js](http://momentjs.com/docs/#/displaying/format/)", @@ -3092,8 +3095,6 @@ "meteor_status_failed": "Соединение с сервером не удалось", "meteor_status_offline": "Автономный режим.", "meteor_status_reconnect_in_one": "пытается снова в одну секунду ...", - "meteor_status_reconnect_in_few": "пытается снова через {{count}} секунд ...", - "meteor_status_reconnect_in_many": "пытается снова через {{count}} секунд ...", "meteor_status_reconnect_in_other": "пытается снова через {{count}} секунд ...", "meteor_status_try_now_offline": "Подключите снова", "meteor_status_try_now_waiting": "Попробуйте сейчас", @@ -3799,6 +3800,7 @@ "Running_Instances": "Запущенные виртуальные машины", "Runtime_Environment": "Среда выполнения", "S_new_messages_since_s": "%s новых сообщений с %s", + "S_new_messages": "%s новых сообщений", "Same_As_Token_Sent_Via": "То же, что и «Token Sent Via»", "Same_Style_For_Mentions": "Такой же стиль для упоминаний", "SAML": "SAML разметка", @@ -5100,4 +5102,4 @@ "Enterprise": "Корпорация", "UpgradeToGetMore_engagement-dashboard_Title": "Аналитика", "UpgradeToGetMore_auditing_Title": "Аудит сообщений" -} +} \ 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 b3062bd2787a..83f2dead338d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json @@ -2129,6 +2129,7 @@ "Running_Instances": "Spúšťanie inštancií", "Runtime_Environment": "Runtime Environment", "S_new_messages_since_s": "%s nové správy od%s", + "S_new_messages": "%s nové správy", "Same_As_Token_Sent_Via": "Rovnaké ako \"Token poslaný cez\"", "Same_Style_For_Mentions": "Rovnaký štýl pre zmienky", "SAML": "SAML", 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 d36f64dee4a8..6fe22e641474 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json @@ -2109,6 +2109,7 @@ "Running_Instances": "Vodenje primerkov", "Runtime_Environment": "Izvajalno okolje", "S_new_messages_since_s": "%s novo sporočilo od %s", + "S_new_messages": "%s nova sporočila", "Same_As_Token_Sent_Via": "Enako kot \"Žeton poslan preko\"", "Same_Style_For_Mentions": "Isti stil za omembe", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json index 8754bcdf3ea4..0b6f0817bff8 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json @@ -2119,6 +2119,7 @@ "Running_Instances": "drejtimin raste", "Runtime_Environment": "Runtime Environment", "S_new_messages_since_s": "%s mesazhe të reja nga %s", + "S_new_messages": "%s mesazhe të reja", "Same_As_Token_Sent_Via": "Njësoj si \"Token Sent Via\"", "Same_Style_For_Mentions": "E njëjta stil për të përmendur", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json index 629b20286646..55f30cb75c6e 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json @@ -1945,6 +1945,7 @@ "Running_Instances": "Покретање инстанци", "Runtime_Environment": "Рунтиме Енвиронмент", "S_new_messages_since_s": "%s нових порука од %s", + "S_new_messages": "%s нових порука", "Same_As_Token_Sent_Via": "Исто као \"Токен послати преко\"", "Same_Style_For_Mentions": "Исти стил за помињања", "SAML": "САМЛ", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json index 7fd1d55f9250..75e03aa72f77 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json @@ -678,6 +678,7 @@ "Auto_Load_Images": "Hämta bilder automatiskt", "Auto_Selection": "Automatiskt val", "Auto_Translate": "Automatisk översättning", + "Calls_in_queue": "{{calls}} samtal i kö", "auto-translate": "Översätt automatiskt", "auto-translate_description": "Tillstånd att använda det automatiska översättningsverktyget", "Automatic_Translation": "Automatisk översättning", @@ -1064,7 +1065,6 @@ "Connection_Closed": "Anslutningen är stängd", "Connection_Reset": "Anslutning återställd", "Connection_error": "Anslutningsfel", - "LDAP_Connection_successful": "LDAP-anslutningen har upprättats", "Connection_failed": "LDAP-anslutningen kunde inte upprättas", "Connectivity_Services": "Anslutningstjänster", "Consulting": "Consulting", @@ -2748,6 +2748,7 @@ "LDAP_Connection": "Anslutning", "LDAP_Connection_Authentication": "Autentisering", "LDAP_Connection_Encryption": "Kryptering", + "LDAP_Connection_successful": "LDAP-anslutningen har upprättats", "LDAP_Connection_Timeouts": "Tidsgränser", "LDAP_UserSearch": "Användarsökning", "LDAP_UserSearch_Filter": "Sökfilter", @@ -4181,6 +4182,7 @@ "Running_Instances": "Antal instanser som körs", "Runtime_Environment": "Körtidsmiljö", "S_new_messages_since_s": "%s nya meddelanden sedan %s", + "S_new_messages": "%s nya meddelanden", "Same_As_Token_Sent_Via": "Samma som \"Token Send Via\"", "Same_Style_For_Mentions": "Samma stil för omnämnanden", "SAML": "SAML", @@ -5749,4 +5751,4 @@ "Theme_Appearence": "Utseende för tema", "Enterprise": "Enterprise", "UpgradeToGetMore_engagement-dashboard_Title": "Analytics" -} +} \ 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 89366d9df2a0..4b5ab7ac74c0 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json @@ -2121,6 +2121,7 @@ "Running_Instances": "நிகழ்வுகளை இயக்க", "Runtime_Environment": "இயக்க சூழல்", "S_new_messages_since_s": "% கள் என்பதால்% கள் புதிய செய்திகள்", + "S_new_messages": "% கள் புதிய செய்திகள்", "Same_As_Token_Sent_Via": "அதே போல \"டோக்கன் அனுப்பிய வழி\"", "Same_Style_For_Mentions": "குறிப்பிடுவதற்கான அதே பாணி", "SAML": "SAML", 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 e97d28e2c30b..e520bf899109 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json @@ -2113,6 +2113,7 @@ "Running_Instances": "การใช้งานอินสแตนซ์", "Runtime_Environment": "สภาพแวดล้อมรันไทม์", "S_new_messages_since_s": "%s ข้อความใหม่นับตั้งแต่%s", + "S_new_messages": "%s ข้อความใหม่", "Same_As_Token_Sent_Via": "เหมือนกับ \"Token Sent Via\"", "Same_Style_For_Mentions": "สไตล์เดียวกันสำหรับการพูดถึง", "SAML": "SAML", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json index f63f6d8d73e3..c81016871d75 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json @@ -2522,6 +2522,7 @@ "Running_Instances": "Örneklerini Çalıştırma", "Runtime_Environment": "Çalışma Zamanı Ortamı", "S_new_messages_since_s": "%s yeni ileti (%s'den beri)", + "S_new_messages": "%s yeni ileti", "Same_As_Token_Sent_Via": "\"Token Sent Via\" ile aynı", "Same_Style_For_Mentions": "Bahisler için aynı tarz", "SAML": "SAML", @@ -3264,4 +3265,4 @@ "RegisterWorkspace_Features_Omnichannel_Title": "Çoklu Kanal", "Enterprise": "Kuruluş", "UpgradeToGetMore_engagement-dashboard_Title": "Mantıksal Analiz" -} +} \ 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 57a828f92ffb..737d8c8e4b9a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json @@ -912,6 +912,7 @@ "Rooms": "ئۆي", "Running_Instances": "ھازىر يۈرگۈزۈلىۋاتقان مىسال", "S_new_messages_since_s": "دىن كەلگەن%s يېڭى ئۇچۇر %s", + "S_new_messages": "تال يېڭى ئۇچۇر%s", "SAML": "SAML", "SAML_Custom_Cert": "ئۆزلۈكىدىن بېكىتىش ئىسپاتى", "SAML_Custom_Entry_point": "ئۆزلۈكىدىن بېكىتىش ئېغىزى", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json index f3b973df28c9..f30d69e0cf8c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json @@ -1891,6 +1891,8 @@ "Language_Not_set": "Немає специфіки", "Language_Polish": "Польська", "Language_Portuguese": "Португальська", + "message_counter_few": "{{count}} повідомлень", + "message_counter_many": "{{count}} повідомлень", "Language_Russian": "Російська", "Language_Spanish": "Іспанська", "Language_Version": "Українська версія", @@ -2164,8 +2166,6 @@ "Message_BadWordsFilterList": "Додати погані слова в чорний список", "Message_BadWordsFilterListDescription": "Додати список розділених комами список поганих слів, щоб фільтрувати", "message_counter_one": "{{count}} повідомлення", - "message_counter_few": "{{count}} повідомлень", - "message_counter_many": "{{count}} повідомлень", "message_counter_other": "{{count}} повідомлень", "Message_DateFormat": "Формат дати", "Message_DateFormat_Description": "Дивіться також: [Moment.js](http://momentjs.com/docs/#/displaying/format/)", @@ -2656,6 +2656,7 @@ "Running_Instances": "Запущено екземплярів", "Runtime_Environment": "Runtime Environment", "S_new_messages_since_s": "%s нових повідомлень з моменту %s", + "S_new_messages": "%s нових повідомлень", "Same_As_Token_Sent_Via": "Той же, що й \"Token Sent Via\"", "Same_Style_For_Mentions": "Той самий стиль для згадування", "SAML": "SAML", @@ -3353,4 +3354,4 @@ "Enterprise": "Підприємство", "UpgradeToGetMore_engagement-dashboard_Title": "Аналітика", "UpgradeToGetMore_auditing_Title": "Аудит повідомлень" -} +} \ 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 db223f0ba6b6..4b9936642086 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json @@ -2218,6 +2218,7 @@ "Running_Instances": "Chạy Các Trường hợp", "Runtime_Environment": "Môi trường thực thi", "S_new_messages_since_s": "%s tin nhắn mới kể từ%s", + "S_new_messages": "%s tin nhắn mới", "Same_As_Token_Sent_Via": "Tương tự như \"Token Sent Via\"", "Same_Style_For_Mentions": "Cùng một phong cách để đề cập đến", "SAML": "SAML", @@ -2852,4 +2853,4 @@ "registration.component.form.sendConfirmationEmail": "Gửi email xác nhận", "Enterprise": "Doanh nghiệp", "UpgradeToGetMore_engagement-dashboard_Title": "phân tích" -} +} \ 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 afeecab7fb7c..d3a3295c5956 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json @@ -2141,6 +2141,7 @@ "Running_Instances": "运行实例", "Runtime_Environment": "运行环境", "S_new_messages_since_s": "%s 新消息,自从 %s", + "S_new_messages": "%s条新消息", "Same_As_Token_Sent_Via": "与“通过发送的令牌”相同", "Same_Style_For_Mentions": "同样的风格提及", "SAML": "SAML", 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 ab810feb04fb..29b4d7522504 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json @@ -587,6 +587,7 @@ "Auto_Load_Images": "自動載入圖片", "Auto_Selection": "自動選擇", "Auto_Translate": "自動翻譯", + "Calls_in_queue": "{{count}} 個通話在佇列中", "auto-translate": "自動翻譯", "auto-translate_description": "有權限使用自動翻譯工具", "Automatic_Translation": "自動翻譯", @@ -700,7 +701,6 @@ "Call": "呼叫", "Calling": "正在通話", "Call_Center": "通話中心", - "Calls_in_queue": "{{count}} 個通話在佇列中", "Call_declined": "通話已拒絕!", "Call_Information": "通話資訊", "Call_provider": "通話提供者", @@ -914,7 +914,6 @@ "Connection_Closed": "連接關閉", "Connection_Reset": "連線重置", "Connection_error": "連線錯誤", - "LDAP_Connection_successful": "LDAP 連接成功", "Connection_failed": "LDAP 連線失敗", "Connectivity_Services": "連線的服務", "Consulting": "諮詢", @@ -2362,6 +2361,7 @@ "LDAP_Connection": "連線", "LDAP_Connection_Authentication": "驗證", "LDAP_Connection_Encryption": "加密", + "LDAP_Connection_successful": "LDAP 連接成功", "LDAP_Connection_Timeouts": "逾時", "LDAP_UserSearch": "使用者搜尋", "LDAP_UserSearch_Filter": "搜尋過濾", @@ -3502,6 +3502,7 @@ "Running_Instances": "正在執行的實例", "Runtime_Environment": "執行環境", "S_new_messages_since_s": "%s 新訊息,來自 %s", + "S_new_messages": "% 新訊息", "Same_As_Token_Sent_Via": "與“通過發送的 Token”相同", "Same_Style_For_Mentions": "同樣的風格提及", "SAML": "SAML", @@ -4586,4 +4587,4 @@ "Enterprise": "企業", "UpgradeToGetMore_engagement-dashboard_Title": "分析", "UpgradeToGetMore_auditing_Title": "訊息稽核" -} +} \ 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 54658c54de04..e14fd08dd8d5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json @@ -809,7 +809,6 @@ "Connect_SSL_TLS": "使用 SSL/TLS 连接", "Connection_Closed": "连接关闭", "Connection_Reset": "连接重置", - "LDAP_Connection_successful": "LDAP 连接成功", "Connectivity_Services": "连接性服务", "Consulting": "咨询", "Contact": "联系人", @@ -2151,6 +2150,7 @@ "Layout_Terms_of_Service": "服务条款", "LDAP": "LDAP", "LDAP_Description": "LDAP(轻量目录访问协议)是一种层次数据库,常被企业用于提供单点登录机制——该机制允许用户使用同一套帐号密码登录多个网站或服务。想要了解LDAP认证的设置及示例,可参考我们的wiki页: https://rocket.chat/docs/administrator-guides/authentication/ldap/", + "LDAP_Connection_successful": "LDAP 连接成功", "LDAP_Advanced_Sync": "高级同步", "LDAP_Authentication": "启用", "LDAP_Authentication_Password": "密码", @@ -3165,6 +3165,7 @@ "Running_Instances": "正在运行的实例", "Runtime_Environment": "运行环境", "S_new_messages_since_s": "%s 新消息,自从 %s", + "S_new_messages": "%s 条新消息", "Same_As_Token_Sent_Via": "与“通过发送的令牌”相同", "Same_Style_For_Mentions": "同样的风格提及", "SAML": "SAML", @@ -4133,4 +4134,4 @@ "Enterprise": "企业", "UpgradeToGetMore_engagement-dashboard_Title": "分析", "UpgradeToGetMore_auditing_Title": "消息审计" -} +} \ No newline at end of file From 5c29cec75e31c47cadfaba6caffe2b82814fadd2 Mon Sep 17 00:00:00 2001 From: Shivang Yadav <125182653+shivang-16@users.noreply.github.com> Date: Mon, 5 Feb 2024 20:28:55 +0530 Subject: [PATCH 011/207] fix: Favoriting room through rooms page (#31554) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Henrique Guimarães Ribeiro <43561537+rique223@users.noreply.github.com> --- .changeset/serious-cows-compete.md | 5 +++++ .../client/views/admin/rooms/EditRoom.tsx | 11 +++++----- .../Info/EditRoomInfo/EditRoomInfo.tsx | 10 ++++----- apps/meteor/tests/e2e/administration.spec.ts | 22 +++++++++++++++++++ apps/meteor/tests/e2e/page-objects/admin.ts | 16 ++++++++++++++ 5 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 .changeset/serious-cows-compete.md diff --git a/.changeset/serious-cows-compete.md b/.changeset/serious-cows-compete.md new file mode 100644 index 000000000000..be419a9bd9cd --- /dev/null +++ b/.changeset/serious-cows-compete.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed a bug on the rooms page's "Favorite" setting, which previously failed to designate selected rooms as favorites by default. diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx index f6c3a0db04f1..bc9899bd438d 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -13,7 +13,7 @@ import { TextAreaInput, FieldError, } from '@rocket.chat/fuselage'; -import { useMutableCallback, useUniqueId } from '@rocket.chat/fuselage-hooks'; +import { useEffectEvent, useUniqueId } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useRouter, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; import { useForm, Controller } from 'react-hook-form'; @@ -96,9 +96,10 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps) => { const handleArchive = useArchiveRoom(room); - const handleUpdateRoomData = useMutableCallback(async ({ isDefault, roomName, favorite, ...formData }) => { + const handleUpdateRoomData = useEffectEvent(async ({ isDefault, roomName, favorite, ...formData }) => { const data = getDirtyFields(formData, dirtyFields); delete data.archived; + delete data.favorite; try { await saveAction({ @@ -117,9 +118,9 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps) => { } }); - const handleSave = useMutableCallback(async (data) => { - await Promise.all([isDirty && handleUpdateRoomData(data), changeArchiving && handleArchive()].filter(Boolean)); - }); + const handleSave = useEffectEvent((data) => + Promise.all([isDirty && handleUpdateRoomData(data), changeArchiving && handleArchive()].filter(Boolean)), + ); const formId = useUniqueId(); const roomNameField = useUniqueId(); diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx index 09268dc6a43a..2a9397364757 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx +++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx @@ -20,7 +20,7 @@ import { Box, TextAreaInput, } from '@rocket.chat/fuselage'; -import { useMutableCallback, useUniqueId } from '@rocket.chat/fuselage-hooks'; +import { useEffectEvent, useUniqueId } from '@rocket.chat/fuselage-hooks'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useSetting, useTranslation, useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts'; import React, { useMemo } from 'react'; @@ -98,7 +98,7 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) => const handleArchive = useArchiveRoom(room); - const handleUpdateRoomData = useMutableCallback(async ({ hideSysMes, joinCodeRequired, ...formData }) => { + const handleUpdateRoomData = useEffectEvent(async ({ hideSysMes, joinCodeRequired, ...formData }) => { const data = getDirtyFields(formData, dirtyFields); delete data.archived; @@ -119,9 +119,9 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) => } }); - const handleSave = useMutableCallback(async (data) => { - await Promise.all([isDirty && handleUpdateRoomData(data), changeArchiving && handleArchive()].filter(Boolean)); - }); + const handleSave = useEffectEvent((data) => + Promise.all([isDirty && handleUpdateRoomData(data), changeArchiving && handleArchive()].filter(Boolean)), + ); const formId = useUniqueId(); const roomNameField = useUniqueId(); diff --git a/apps/meteor/tests/e2e/administration.spec.ts b/apps/meteor/tests/e2e/administration.spec.ts index 6df7e425c396..8c3bd5c23f6b 100644 --- a/apps/meteor/tests/e2e/administration.spec.ts +++ b/apps/meteor/tests/e2e/administration.spec.ts @@ -87,6 +87,28 @@ test.describe.parallel('administration', () => { await poAdmin.getRoomRow(targetChannel).click(); await expect(poAdmin.archivedInput).toBeChecked(); }); + + test.describe.serial('Default rooms', () => { + test('expect target channell to be default', async () => { + await poAdmin.inputSearchRooms.type(targetChannel); + await poAdmin.getRoomRow(targetChannel).click(); + await poAdmin.defaultLabel.click(); + await poAdmin.btnSave.click(); + + await poAdmin.getRoomRow(targetChannel).click(); + await expect(poAdmin.defaultInput).toBeChecked(); + }); + + test('should mark target default channel as "favorite by default"', async () => { + await poAdmin.inputSearchRooms.type(targetChannel); + await poAdmin.getRoomRow(targetChannel).click(); + await poAdmin.favoriteLabel.click(); + await poAdmin.btnSave.click(); + + await poAdmin.getRoomRow(targetChannel).click(); + await expect(poAdmin.favoriteInput).toBeChecked(); + }); + }); }); test.describe('Permissions', () => { diff --git a/apps/meteor/tests/e2e/page-objects/admin.ts b/apps/meteor/tests/e2e/page-objects/admin.ts index 17e5185becaf..022fd609077f 100644 --- a/apps/meteor/tests/e2e/page-objects/admin.ts +++ b/apps/meteor/tests/e2e/page-objects/admin.ts @@ -36,6 +36,22 @@ export class Admin { return this.page.locator('input[name="archived"]'); } + get favoriteLabel(): Locator { + return this.page.locator('label >> text=Favorite'); + } + + get favoriteInput(): Locator { + return this.page.locator('input[name="favorite"]'); + } + + get defaultLabel(): Locator { + return this.page.locator('label >> text=Default'); + } + + get defaultInput(): Locator { + return this.page.locator('input[name="isDefault"]'); + } + get inputSearchUsers(): Locator { return this.page.locator('input[placeholder="Search Users"]'); } From 4442737096729e414dd799f9b82b0d4f1edc9875 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Mon, 5 Feb 2024 12:43:12 -0300 Subject: [PATCH 012/207] test: make misc api tests fully independent (#31585) --- apps/meteor/tests/data/teams.helper.ts | 17 ++ .../tests/end-to-end/api/00-miscellaneous.js | 253 ++++++------------ 2 files changed, 93 insertions(+), 177 deletions(-) create mode 100644 apps/meteor/tests/data/teams.helper.ts diff --git a/apps/meteor/tests/data/teams.helper.ts b/apps/meteor/tests/data/teams.helper.ts new file mode 100644 index 000000000000..308fc60f445e --- /dev/null +++ b/apps/meteor/tests/data/teams.helper.ts @@ -0,0 +1,17 @@ +import { ITeam, TEAM_TYPE } from "@rocket.chat/core-typings" +import { api, request } from "./api-data" + +export const createTeam = async (credentials: Record, teamName: string, type: TEAM_TYPE): Promise => { + const response = await request.post(api('teams.create')).set(credentials).send({ + name: teamName, + type, + }); + + return response.body.team; +}; + +export const deleteTeam = async (credentials: Record, teamName: string): Promise => { + await request.post(api('teams.delete')).set(credentials).send({ + teamName, + }); +}; \ No newline at end of file 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 e9fec42e4b66..d545441c1b7c 100644 --- a/apps/meteor/tests/end-to-end/api/00-miscellaneous.js +++ b/apps/meteor/tests/end-to-end/api/00-miscellaneous.js @@ -1,11 +1,13 @@ +import { TEAM_TYPE } from '@rocket.chat/core-typings'; import { expect } from 'chai'; import { after, before, describe, it } from 'mocha'; -import { getCredentials, api, login, request, credentials } from '../../data/api-data.js'; -import { updateSetting } from '../../data/permissions.helper'; -import { createRoom } from '../../data/rooms.helper'; +import { getCredentials, api, request, credentials } from '../../data/api-data.js'; +import { updatePermission, updateSetting } from '../../data/permissions.helper'; +import { createRoom, deleteRoom } from '../../data/rooms.helper'; +import { createTeam, deleteTeam } from '../../data/teams.helper'; import { adminEmail, adminUsername, adminPassword, password } from '../../data/user'; -import { createUser, login as doLogin } from '../../data/users.helper'; +import { createUser, deleteUser, login as doLogin } from '../../data/users.helper'; import { IS_EE } from '../../e2e/config/constants'; describe('miscellaneous', function () { @@ -133,10 +135,13 @@ describe('miscellaneous', function () { .end(done); }); - it('/me', (done) => { - request + it('/me', async () => { + const user = await createUser(); + const userCredentials = await doLogin(user.username, password); + + await request .get(api('me')) - .set(credentials) + .set(userCredentials) .expect('Content-Type', 'application/json') .expect(200) .expect((res) => { @@ -181,56 +186,43 @@ describe('miscellaneous', function () { ].filter((p) => Boolean(p)); expect(res.body).to.have.property('success', true); - expect(res.body).to.have.property('_id', credentials['X-User-Id']); - expect(res.body).to.have.property('username', login.user); + expect(res.body).to.have.property('_id', user._id); + expect(res.body).to.have.property('username', user.username); expect(res.body).to.have.property('active'); expect(res.body).to.have.property('name'); expect(res.body).to.have.property('roles').and.to.be.an('array'); - expect(res.body).to.have.nested.property('emails[0].address', adminEmail); + expect(res.body).to.have.nested.property('emails[0].address', user.emails[0].address); expect(res.body).to.have.nested.property('settings.preferences').and.to.be.an('object'); expect(res.body.settings.preferences).to.have.all.keys(allUserPreferencesKeys); expect(res.body.services).to.not.have.nested.property('password.bcrypt'); - }) - .end(done); + }); + + await deleteUser(user); }); describe('/directory', () => { let user; let testChannel; - before((done) => { - const username = `user.test.${Date.now()}`; - const email = `${username}@rocket.chat`; - request - .post(api('users.create')) - .set(credentials) - .send({ email, name: username, username, password }) - .end((err, res) => { - user = res.body.user; - done(); - }); - }); - after((done) => { - request - .post(api('users.delete')) - .set(credentials) - .send({ - userId: user._id, - }) - .end(done); - user = undefined; + let normalUserCredentials; + const teamName = `new-team-name-${Date.now()}`; + let teamCreated = {}; + + before(async () => { + await updatePermission('create-team', ['admin', 'user']); + user = await createUser(); + normalUserCredentials = await doLogin(user.username, password); + testChannel = (await createRoom({ name: `channel.test.${Date.now()}`, type: 'c' })).body.channel; + teamCreated = await createTeam(normalUserCredentials, teamName, TEAM_TYPE.PUBLIC); }); - it('create a channel', (done) => { - request - .post(api('channels.create')) - .set(credentials) - .send({ - name: `channel.test.${Date.now()}`, - }) - .end((err, res) => { - testChannel = res.body.channel; - done(); - }); + + after(async () => { + await Promise.all([ + deleteTeam(normalUserCredentials, teamName), + deleteUser(user), + deleteRoom({ type: 'c', roomId: testChannel._id }), + ]); }); + it('should return an array(result) when search by user and execute successfully', (done) => { request .get(api('directory')) @@ -258,32 +250,10 @@ describe('miscellaneous', function () { .end(done); }); - let normalUser; - before((done) => { - request - .post(api('login')) - .send({ - username: user.username, - password, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('status', 'success'); - expect(res.body).to.have.property('data').and.to.be.an('object'); - expect(res.body.data).to.have.property('userId'); - expect(res.body.data).to.have.property('authToken'); - normalUser = res.body.data; - }) - .end(done); - }); it('should not return the emails field for non admins', (done) => { request .get(api('directory')) - .set({ - 'X-Auth-Token': normalUser.authToken, - 'X-User-Id': normalUser.userId, - }) + .set(normalUserCredentials) .query({ query: JSON.stringify({ text: user.username, @@ -398,36 +368,10 @@ describe('miscellaneous', function () { .end(done); }); - const teamName = `new-team-name-${Date.now()}`; - let teamCreated = {}; - before((done) => { - request - .post(api('teams.create')) - .set(credentials) - .send({ - name: teamName, - type: 0, - }) - .expect((res) => { - teamCreated = res.body.team; - }) - .end(done); - }); - - after((done) => { - request - .post(api('teams.delete')) - .set(credentials) - .send({ - teamName, - }) - .end(done); - }); - it('should return an object containing rooms and totalCount from teams', (done) => { request .get(api('directory')) - .set(credentials) + .set(normalUserCredentials) .query({ query: JSON.stringify({ text: '', @@ -442,7 +386,7 @@ describe('miscellaneous', function () { .expect((res) => { expect(res.body).to.have.property('result'); expect(res.body.result).to.be.an(`array`); - expect(res.body).to.have.property('total', 1); + expect(res.body).to.have.property('total'); expect(res.body.total).to.be.an('number'); expect(res.body.result[0]).to.have.property('_id', teamCreated.roomId); expect(res.body.result[0]).to.have.property('fname'); @@ -458,80 +402,33 @@ describe('miscellaneous', function () { .end(done); }); }); + describe('[/spotlight]', () => { let user; - before((done) => { - const username = `user.test.${Date.now()}`; - const email = `${username}@rocket.chat`; - request - .post(api('users.create')) - .set(credentials) - .send({ email, name: username, username, password }) - .end((err, res) => { - user = res.body.user; - done(); - }); - }); - let userCredentials; let testChannel; let testTeam; - before((done) => { - request - .post(api('login')) - .send({ - user: user.username, - password, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - userCredentials = {}; - userCredentials['X-Auth-Token'] = res.body.data.authToken; - userCredentials['X-User-Id'] = res.body.data.userId; - }) - .end(done); - }); let testChannelSpecialChars; - const fnameSpecialCharsRoom = `test ГДΕληνικά`; - before((done) => { - updateSetting('UI_Allow_room_names_with_special_chars', true) - .then(() => { - createRoom({ type: 'c', name: fnameSpecialCharsRoom, credentials: userCredentials }).end((err, res) => { - testChannelSpecialChars = res.body.channel; - }); - }) - .then(done); + const fnameSpecialCharsRoom = `test ГДΕληνικά-${Date.now()}`; + const teamName = `team-test-${Date.now()}`; + + before(async () => { + user = await createUser(); + userCredentials = await doLogin(user.username, password); + await updateSetting('UI_Allow_room_names_with_special_chars', true); + testChannelSpecialChars = (await createRoom({ type: 'c', name: fnameSpecialCharsRoom, credentials: userCredentials })).body.channel; + testChannel = (await createRoom({ type: 'c', name: `channel.test.${Date.now()}`, credentials: userCredentials })).body.channel; + testTeam = await createTeam(userCredentials, teamName, TEAM_TYPE.PUBLIC); }); + after(async () => { - await request.post(api('users.delete')).set(credentials).send({ - userId: user._id, - }); - user = undefined; - await updateSetting('UI_Allow_room_names_with_special_chars', false); - }); - it('create a channel', (done) => { - request - .post(api('channels.create')) - .set(userCredentials) - .send({ - name: `channel.test.${Date.now()}`, - }) - .end((err, res) => { - testChannel = res.body.channel; - done(); - }); - }); - before('create a team', async () => { - const res = await request - .post(api('teams.create')) - .set(userCredentials) - .send({ - name: `team-test-${Date.now()}`, - type: 0, - }); - testTeam = res.body.team; + await Promise.all([ + deleteUser(user), + updateSetting('UI_Allow_room_names_with_special_chars', false), + deleteTeam(userCredentials, teamName), + ]); }); + it('should fail when does not have query param', (done) => { request .get(api('spotlight')) @@ -701,24 +598,26 @@ describe('miscellaneous', function () { }); describe('[/shield.svg]', () => { + before(() => updateSetting('API_Enable_Shields', false)); + + after(() => updateSetting('API_Enable_Shields', true)); + it('should fail if API_Enable_Shields is disabled', (done) => { - updateSetting('API_Enable_Shields', false).then(() => { - request - .get(api('shield.svg')) - .query({ - type: 'online', - icon: true, - channel: 'general', - name: 'Rocket.Chat', - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - expect(res.body).to.have.property('errorType', 'error-endpoint-disabled'); - }) - .end(done); - }); + request + .get(api('shield.svg')) + .query({ + type: 'online', + icon: true, + channel: 'general', + name: 'Rocket.Chat', + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('errorType', 'error-endpoint-disabled'); + }) + .end(done); }); it('should succeed if API_Enable_Shields is enabled', (done) => { From de6997ee1c5f066793b1f923077730a2c0a3f5b7 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Mon, 5 Feb 2024 13:18:05 -0300 Subject: [PATCH 013/207] test: make channels api tests fully independent (#31606) --- apps/meteor/tests/data/api-data.js | 2 - apps/meteor/tests/data/channel.ts | 1 - apps/meteor/tests/data/uploads.helper.ts | 43 +- .../tests/end-to-end/api/02-channels.js | 1496 ++++++++--------- apps/meteor/tests/end-to-end/api/03-groups.js | 2 +- .../tests/end-to-end/api/04-direct-message.js | 2 +- 6 files changed, 775 insertions(+), 771 deletions(-) diff --git a/apps/meteor/tests/data/api-data.js b/apps/meteor/tests/data/api-data.js index b311af16e764..cab98c41eb15 100644 --- a/apps/meteor/tests/data/api-data.js +++ b/apps/meteor/tests/data/api-data.js @@ -15,7 +15,6 @@ export function wait(cb, time) { export const apiUsername = `api${username}-${Date.now()}`; export const apiEmail = `api${email}-${Date.now()}`; -export const apiPublicChannelName = `api${publicChannelName}-${Date.now()}`; export const apiPrivateChannelName = `api${privateChannelName}-${Date.now()}`; export const apiRoleNameUsers = `api${roleNameUsers}`; @@ -25,7 +24,6 @@ export const apiRoleScopeSubscriptions = `${roleScopeSubscriptions}`; export const apiRoleDescription = `api${roleDescription}`; export const reservedWords = ['admin', 'administrator', 'system', 'user']; -export const channel = {}; export const group = {}; export const message = {}; export const directMessage = {}; diff --git a/apps/meteor/tests/data/channel.ts b/apps/meteor/tests/data/channel.ts index e3a23691b7ba..1cd02fb09022 100644 --- a/apps/meteor/tests/data/channel.ts +++ b/apps/meteor/tests/data/channel.ts @@ -1,2 +1 @@ -export const publicChannelName = `channel-test-${Date.now()}`; export const privateChannelName = `private-channel-test-${Date.now()}`; diff --git a/apps/meteor/tests/data/uploads.helper.ts b/apps/meteor/tests/data/uploads.helper.ts index 29c7a143484c..8eb1e7931965 100644 --- a/apps/meteor/tests/data/uploads.helper.ts +++ b/apps/meteor/tests/data/uploads.helper.ts @@ -7,18 +7,30 @@ import { password } from './user'; import { createUser, login } from './users.helper'; import { imgURL } from './interactions'; import { updateSetting } from './permissions.helper'; -import { createRoom } from './rooms.helper'; +import { createRoom, deleteRoom } from './rooms.helper'; import { createVisitor } from './livechat/rooms'; -export async function testFileUploads(filesEndpoint: 'channels.files' | 'groups.files' | 'im.files', room: { _id: string; name?: string; t: string;}, invalidRoomError = 'error-room-not-found') { +export async function testFileUploads(filesEndpoint: 'channels.files' | 'groups.files' | 'im.files', roomType: 'c' | 'd' | 'p', invalidRoomError = 'error-room-not-found') { + let testRoom: Record; + const propertyMap = { + 'c': 'channel', + 'p': 'group', + 'd': 'room', + }; + before(async function () { await updateSetting('VoIP_Enabled', true); await updateSetting('Message_KeepHistory', true); + + testRoom = (await createRoom({ type: roomType, ...(roomType === 'd' ? { username: 'rocket.cat' } : { name: `channel-files-${Date.now()}` }) } as any)).body[propertyMap[roomType]]; }); after(async function () { - await updateSetting('VoIP_Enabled', false); - await updateSetting('Message_KeepHistory', false); + await Promise.all([ + deleteRoom({ type: 'c', roomId: testRoom._id }), + updateSetting('VoIP_Enabled', false), + updateSetting('Message_KeepHistory', false), + ]); }); const createVoipRoom = async function () { @@ -34,6 +46,7 @@ export async function testFileUploads(filesEndpoint: 'channels.files' | 'groups. username: null, members: null, }); + return roomResponse.body.room; }; @@ -74,7 +87,7 @@ export async function testFileUploads(filesEndpoint: 'channels.files' | 'groups. .get(api(filesEndpoint)) .set(credentials) .query({ - roomId: room._id, + roomId: testRoom._id, }) .expect('Content-Type', 'application/json') .expect(200) @@ -90,7 +103,7 @@ export async function testFileUploads(filesEndpoint: 'channels.files' | 'groups. .get(api(filesEndpoint)) .set(credentials) .query({ - roomId: room._id, + roomId: testRoom._id, count: 5, offset: 0, }) @@ -104,14 +117,14 @@ export async function testFileUploads(filesEndpoint: 'channels.files' | 'groups. }); it('should succeed when searching by roomName', function (done) { - if (!room.name) { + if (!testRoom.name) { this.skip(); } request .get(api(filesEndpoint)) .set(credentials) .query({ - roomName: room.name, + roomName: testRoom.name, }) .expect('Content-Type', 'application/json') .expect(200) @@ -123,14 +136,14 @@ export async function testFileUploads(filesEndpoint: 'channels.files' | 'groups. }); it('should succeed when searching by roomName even requested with count and offset params', function (done) { - if (!room.name) { + if (!testRoom.name) { this.skip(); } request .get(api(filesEndpoint)) .set(credentials) .query({ - roomName: room.name, + roomName: testRoom.name, count: 5, offset: 0, }) @@ -145,7 +158,7 @@ export async function testFileUploads(filesEndpoint: 'channels.files' | 'groups. it('should not return thumbnails', async function () { await request - .post(api(`rooms.upload/${room._id}`)) + .post(api(`rooms.upload/${testRoom._id}`)) .set(credentials) .attach('file', imgURL) .expect('Content-Type', 'application/json') @@ -158,7 +171,7 @@ export async function testFileUploads(filesEndpoint: 'channels.files' | 'groups. .get(api(filesEndpoint)) .set(credentials) .query({ - roomId: room._id, + roomId: testRoom._id, }) .expect('Content-Type', 'application/json') .expect(200) @@ -179,7 +192,7 @@ export async function testFileUploads(filesEndpoint: 'channels.files' | 'groups. let fileId: string; await request - .post(api(`rooms.upload/${room._id}`)) + .post(api(`rooms.upload/${testRoom._id}`)) .set(credentials) .attach('file', imgURL) .expect('Content-Type', 'application/json') @@ -195,7 +208,7 @@ export async function testFileUploads(filesEndpoint: 'channels.files' | 'groups. .post(api('chat.delete')) .set(credentials) .send({ - roomId: room._id, + roomId: testRoom._id, msgId, }) .expect('Content-Type', 'application/json') @@ -205,7 +218,7 @@ export async function testFileUploads(filesEndpoint: 'channels.files' | 'groups. .get(api(filesEndpoint)) .set(credentials) .query({ - roomId: room._id, + roomId: testRoom._id, }) .expect('Content-Type', 'application/json') .expect(200) diff --git a/apps/meteor/tests/end-to-end/api/02-channels.js b/apps/meteor/tests/end-to-end/api/02-channels.js index f0f61aa5661e..5291b3621b43 100644 --- a/apps/meteor/tests/end-to-end/api/02-channels.js +++ b/apps/meteor/tests/end-to-end/api/02-channels.js @@ -1,11 +1,11 @@ import { expect } from 'chai'; import { after, before, describe, it } from 'mocha'; -import { getCredentials, api, request, credentials, apiPublicChannelName, channel, reservedWords } from '../../data/api-data.js'; +import { getCredentials, api, request, credentials, reservedWords } from '../../data/api-data.js'; import { CI_MAX_ROOMS_PER_GUEST as maxRoomsPerGuest } from '../../data/constants'; import { createIntegration, removeIntegration } from '../../data/integration.helper'; import { updatePermission, updateSetting } from '../../data/permissions.helper'; -import { createRoom } from '../../data/rooms.helper'; +import { createRoom, deleteRoom } from '../../data/rooms.helper'; import { testFileUploads } from '../../data/uploads.helper'; import { adminUsername, password } from '../../data/user'; import { createUser, login, deleteUser } from '../../data/users.helper'; @@ -24,7 +24,11 @@ function getRoomInfo(roomId) { }); } +const channel = {}; + describe('[Channels]', function () { + const apiPublicChannelName = `api-channel-test-${Date.now()}`; + this.retries(0); before((done) => getCredentials(done)); @@ -50,6 +54,498 @@ describe('[Channels]', function () { .end(done); }); + after(async () => { + await deleteRoom({ type: 'c', roomId: channel._id }); + }); + + it('/channels.invite', async () => { + const roomInfo = await getRoomInfo(channel._id); + + return request + .post(api('channels.invite')) + .set(credentials) + .send({ + roomId: channel._id, + userId: 'rocket.cat', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('channel._id'); + expect(res.body).to.have.nested.property('channel.name', apiPublicChannelName); + expect(res.body).to.have.nested.property('channel.t', 'c'); + expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs + 1); + }); + }); + + it('/channels.addModerator', (done) => { + request + .post(api('channels.addModerator')) + .set(credentials) + .send({ + roomId: channel._id, + userId: 'rocket.cat', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + + it('/channels.addModerator should fail with missing room Id', (done) => { + request + .post(api('channels.addModerator')) + .set(credentials) + .send({ + userId: 'rocket.cat', + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + }) + .end(done); + }); + + it('/channels.addModerator should fail with missing user Id', (done) => { + request + .post(api('channels.addModerator')) + .set(credentials) + .send({ + roomId: channel._id, + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + }) + .end(done); + }); + + it('/channels.removeModerator', (done) => { + request + .post(api('channels.removeModerator')) + .set(credentials) + .send({ + roomId: channel._id, + userId: 'rocket.cat', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + + it('/channels.removeModerator should fail on invalid room id', (done) => { + request + .post(api('channels.removeModerator')) + .set(credentials) + .send({ + userId: 'rocket.cat', + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + }) + .end(done); + }); + + it('/channels.removeModerator should fail on invalid user id', (done) => { + request + .post(api('channels.removeModerator')) + .set(credentials) + .send({ + roomId: channel._id, + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + }) + .end(done); + }); + + it('/channels.addOwner', (done) => { + request + .post(api('channels.addOwner')) + .set(credentials) + .send({ + roomId: channel._id, + userId: 'rocket.cat', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + + it('/channels.removeOwner', (done) => { + request + .post(api('channels.removeOwner')) + .set(credentials) + .send({ + roomId: channel._id, + userId: 'rocket.cat', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + + it('/channels.kick', async () => { + const roomInfo = await getRoomInfo(channel._id); + + return request + .post(api('channels.kick')) + .set(credentials) + .send({ + roomId: channel._id, + userId: 'rocket.cat', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('channel._id'); + expect(res.body).to.have.nested.property('channel.name', apiPublicChannelName); + expect(res.body).to.have.nested.property('channel.t', 'c'); + expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs + 1); + }); + }); + + it('/channels.invite', async () => { + const roomInfo = await getRoomInfo(channel._id); + + return request + .post(api('channels.invite')) + .set(credentials) + .send({ + roomId: channel._id, + userId: 'rocket.cat', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('channel._id'); + expect(res.body).to.have.nested.property('channel.name', apiPublicChannelName); + expect(res.body).to.have.nested.property('channel.t', 'c'); + expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs + 1); + }); + }); + + it('/channels.addOwner', (done) => { + request + .post(api('channels.addOwner')) + .set(credentials) + .send({ + roomId: channel._id, + userId: 'rocket.cat', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + + it('/channels.archive', (done) => { + request + .post(api('channels.archive')) + .set(credentials) + .send({ + roomId: channel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + + it('/channels.unarchive', (done) => { + request + .post(api('channels.unarchive')) + .set(credentials) + .send({ + roomId: channel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + + it('/channels.close', (done) => { + request + .post(api('channels.close')) + .set(credentials) + .send({ + roomId: channel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + + it('/channels.close', (done) => { + request + .post(api('channels.close')) + .set(credentials) + .send({ + roomName: apiPublicChannelName, + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('error', `The channel, ${apiPublicChannelName}, is already closed to the sender`); + }) + .end(done); + }); + + it('/channels.open', (done) => { + request + .post(api('channels.open')) + .set(credentials) + .send({ + roomId: channel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + + it('/channels.list', (done) => { + request + .get(api('channels.list')) + .set(credentials) + .query({ + roomId: channel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('count'); + expect(res.body).to.have.property('total'); + }) + .end(done); + }); + + it('/channels.list.joined', (done) => { + request + .get(api('channels.list.joined')) + .set(credentials) + .query({ + roomId: channel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('count'); + expect(res.body).to.have.property('total'); + }) + .end(done); + }); + it('/channels.counters', (done) => { + request + .get(api('channels.counters')) + .set(credentials) + .query({ + roomId: channel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('joined', true); + expect(res.body).to.have.property('members'); + expect(res.body).to.have.property('unreads'); + expect(res.body).to.have.property('unreadsFrom'); + expect(res.body).to.have.property('msgs'); + expect(res.body).to.have.property('latest'); + expect(res.body).to.have.property('userMentions'); + }) + .end(done); + }); + + it('/channels.rename', async () => { + const roomInfo = await getRoomInfo(channel._id); + + function failRenameChannel(name) { + it(`should not rename a channel to the reserved name ${name}`, (done) => { + request + .post(api('channels.rename')) + .set(credentials) + .send({ + roomId: channel._id, + name, + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('error', `${name} is already in use :( [error-field-unavailable]`); + }) + .end(done); + }); + } + + reservedWords.forEach((name) => { + failRenameChannel(name); + }); + + return request + .post(api('channels.rename')) + .set(credentials) + .send({ + roomId: channel._id, + name: `EDITED${apiPublicChannelName}`, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('channel._id'); + expect(res.body).to.have.nested.property('channel.name', `EDITED${apiPublicChannelName}`); + expect(res.body).to.have.nested.property('channel.t', 'c'); + expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs + 1); + }); + }); + + it('/channels.addAll', (done) => { + request + .post(api('channels.addAll')) + .set(credentials) + .send({ + roomId: channel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('channel._id'); + expect(res.body).to.have.nested.property('channel.name', `EDITED${apiPublicChannelName}`); + expect(res.body).to.have.nested.property('channel.t', 'c'); + }) + .end(done); + }); + + it('/channels.addLeader', (done) => { + request + .post(api('channels.addLeader')) + .set(credentials) + .send({ + roomId: channel._id, + userId: 'rocket.cat', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.a.property('success', true); + }) + .end(done); + }); + it('/channels.removeLeader', (done) => { + request + .post(api('channels.removeLeader')) + .set(credentials) + .send({ + roomId: channel._id, + userId: 'rocket.cat', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + + it('/channels.setJoinCode', async () => { + const roomInfo = await getRoomInfo(channel._id); + + return request + .post(api('channels.setJoinCode')) + .set(credentials) + .send({ + roomId: channel._id, + joinCode: '123', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('channel._id'); + expect(res.body).to.have.nested.property('channel.name', `EDITED${apiPublicChannelName}`); + expect(res.body).to.have.nested.property('channel.t', 'c'); + expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs); + }); + }); + + it('/channels.setReadOnly', async () => { + const roomInfo = await getRoomInfo(channel._id); + + return request + .post(api('channels.setReadOnly')) + .set(credentials) + .send({ + roomId: channel._id, + readOnly: true, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('channel._id'); + expect(res.body).to.have.nested.property('channel.name', `EDITED${apiPublicChannelName}`); + expect(res.body).to.have.nested.property('channel.t', 'c'); + expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs + 1); + }); + }); + it('/channels.leave', async () => { + const roomInfo = await getRoomInfo(channel._id); + + return request + .post(api('channels.leave')) + .set(credentials) + .send({ + roomId: channel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('channel._id'); + expect(res.body).to.have.nested.property('channel.name', `EDITED${apiPublicChannelName}`); + expect(res.body).to.have.nested.property('channel.t', 'c'); + expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs + 1); + }); + }); + describe('[/channels.create]', () => { let guestUser; let room; @@ -93,7 +589,7 @@ describe('[Channels]', function () { }), ); } - await Promise.all(promises); + const channelIds = (await Promise.all(promises)).map((r) => r.body.channel).map((channel) => channel._id); request .post(api('channels.create')) @@ -123,17 +619,25 @@ describe('[Channels]', function () { expect(res.body.members).to.have.lengthOf(1); }); }); + + await Promise.all(channelIds.map((id) => deleteRoom({ type: 'c', roomId: id }))); }); }); describe('[/channels.info]', () => { + const testChannelName = `api-channel-test-${Date.now()}`; let testChannel = {}; let channelMessage = {}; + + after(async () => { + await deleteRoom({ type: 'c', roomId: testChannel._id }); + }); + it('creating new channel...', (done) => { request .post(api('channels.create')) .set(credentials) .send({ - name: apiPublicChannelName, + name: testChannelName, }) .expect('Content-Type', 'application/json') .expect(200) @@ -147,7 +651,7 @@ describe('[Channels]', function () { .post(api('channels.create')) .set(credentials) .send({ - name: apiPublicChannelName, + name: testChannelName, }) .expect('Content-Type', 'application/json') .expect(400) @@ -169,7 +673,7 @@ describe('[Channels]', function () { .expect((res) => { expect(res.body).to.have.property('success', true); expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', apiPublicChannelName); + expect(res.body).to.have.nested.property('channel.name', testChannelName); expect(res.body).to.have.nested.property('channel.t', 'c'); expect(res.body).to.have.nested.property('channel.msgs', 0); }) @@ -301,9 +805,13 @@ describe('[Channels]', function () { }); describe('[/channels.online]', () => { + const createdChannels = []; + const createdUsers = []; + const createUserAndChannel = async () => { const testUser = await createUser(); const testUserCredentials = await login(testUser.username, password); + createdUsers.push(testUser); await request.post(api('users.setStatus')).set(testUserCredentials).send({ message: '', @@ -317,6 +825,7 @@ describe('[Channels]', function () { type: 'c', members: [testUser.username], }); + createdChannels.push(roomResponse.body.channel); return { testUser, @@ -325,6 +834,13 @@ describe('[Channels]', function () { }; }; + after(async () => { + await Promise.all([ + createdUsers.map((user) => deleteUser(user)), + createdChannels.map((channel) => deleteRoom({ type: 'c', roomId: channel._id })), + ]); + }); + it('should return an error if no query', () => request .get(api('channels.online')) @@ -395,7 +911,7 @@ describe('[Channels]', function () { }); describe('[/channels.files]', async () => { - await testFileUploads('channels.files', channel); + await testFileUploads('channels.files', 'c'); }); describe('[/channels.join]', () => { @@ -403,62 +919,28 @@ describe('[Channels]', function () { let testChannelWithCode; let testUser; let testUserCredentials; - before('Create test user', (done) => { - const username = `user.test.${Date.now()}`; - const email = `${username}@rocket.chat`; - request - .post(api('users.create')) - .set(credentials) - .send({ email, name: username, username, password }) - .end((err, res) => { - testUser = res.body.user; - done(); - }); - }); - before('Login as test user', (done) => { - request - .post(api('login')) - .send({ - user: testUser.username, - password, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - testUserCredentials = {}; - testUserCredentials['X-Auth-Token'] = res.body.data.authToken; - testUserCredentials['X-User-Id'] = res.body.data.userId; - }) - .end(done); - }); - before('Create no code channel', (done) => { - request - .post(api('channels.create')) - .set(testUserCredentials) - .send({ - name: `${apiPublicChannelName}-nojoincode`, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - testChannelNoCode = res.body.channel; - }) - .end(done); + + before('Create test user', async () => { + testUser = await createUser(); + testUserCredentials = await login(testUser.username, password); + testChannelNoCode = (await createRoom({ type: 'c', credentials: testUserCredentials, name: `${apiPublicChannelName}-nojoincode` })) + .body.channel; + testChannelWithCode = ( + await createRoom({ type: 'c', credentials: testUserCredentials, name: `${apiPublicChannelName}-withjoincode` }) + ).body.channel; + await updatePermission('edit-room', ['admin', 'owner', 'moderator']); }); - before('Create code channel', (done) => { - request - .post(api('channels.create')) - .set(testUserCredentials) - .send({ - name: `${apiPublicChannelName}-withjoincode`, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - testChannelWithCode = res.body.channel; - }) - .end(done); + + after(async () => { + await Promise.all([ + deleteRoom({ type: 'c', roomId: testChannelNoCode._id }), + deleteRoom({ type: 'c', roomId: testChannelWithCode._id }), + deleteUser(testUser), + updatePermission('edit-room', ['admin', 'owner', 'moderator']), + updatePermission('join-without-join-code', ['admin', 'bot', 'app']), + ]); }); + before('Set code for channel', (done) => { request .post(api('channels.setJoinCode')) @@ -552,260 +1034,57 @@ describe('[Channels]', function () { request .post(api('channels.join')) .set(credentials) - .send({ - roomId: testChannelWithCode._id, - joinCode: '123', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('channel._id', testChannelWithCode._id); - }) - .end(done); - }); - }); - - describe('with join-without-join-code permission', () => { - before('set join-without-join-code permission to true', async () => { - await updatePermission('join-without-join-code', ['admin']); - }); - - before('leave channel', (done) => { - request - .post(api('channels.leave')) - .set(credentials) - .send({ - roomId: testChannelWithCode._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); - }); - - it('should succeed when joining code-needed channel without join code and with join-without-join-code permission', (done) => { - request - .post(api('channels.join')) - .set(credentials) - .send({ - roomId: testChannelWithCode._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('channel._id', testChannelWithCode._id); - }) - .end(done); - }); - }); - }); - }); - - it('/channels.invite', async () => { - const roomInfo = await getRoomInfo(channel._id); - - return request - .post(api('channels.invite')) - .set(credentials) - .send({ - roomId: channel._id, - userId: 'rocket.cat', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', apiPublicChannelName); - expect(res.body).to.have.nested.property('channel.t', 'c'); - expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs + 1); - }); - }); - - it('/channels.addModerator', (done) => { - request - .post(api('channels.addModerator')) - .set(credentials) - .send({ - roomId: channel._id, - userId: 'rocket.cat', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); - }); - - it('/channels.addModerator should fail with missing room Id', (done) => { - request - .post(api('channels.addModerator')) - .set(credentials) - .send({ - userId: 'rocket.cat', - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - }) - .end(done); - }); - - it('/channels.addModerator should fail with missing user Id', (done) => { - request - .post(api('channels.addModerator')) - .set(credentials) - .send({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - }) - .end(done); - }); - - it('/channels.removeModerator', (done) => { - request - .post(api('channels.removeModerator')) - .set(credentials) - .send({ - roomId: channel._id, - userId: 'rocket.cat', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); - }); - - it('/channels.removeModerator should fail on invalid room id', (done) => { - request - .post(api('channels.removeModerator')) - .set(credentials) - .send({ - userId: 'rocket.cat', - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - }) - .end(done); - }); - - it('/channels.removeModerator should fail on invalid user id', (done) => { - request - .post(api('channels.removeModerator')) - .set(credentials) - .send({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - }) - .end(done); - }); - - it('/channels.addOwner', (done) => { - request - .post(api('channels.addOwner')) - .set(credentials) - .send({ - roomId: channel._id, - userId: 'rocket.cat', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); - }); - - it('/channels.removeOwner', (done) => { - request - .post(api('channels.removeOwner')) - .set(credentials) - .send({ - roomId: channel._id, - userId: 'rocket.cat', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); - }); - - it('/channels.kick', async () => { - const roomInfo = await getRoomInfo(channel._id); - - return request - .post(api('channels.kick')) - .set(credentials) - .send({ - roomId: channel._id, - userId: 'rocket.cat', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', apiPublicChannelName); - expect(res.body).to.have.nested.property('channel.t', 'c'); - expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs + 1); + .send({ + roomId: testChannelWithCode._id, + joinCode: '123', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('channel._id', testChannelWithCode._id); + }) + .end(done); + }); }); - }); - it('/channels.invite', async () => { - const roomInfo = await getRoomInfo(channel._id); + describe('with join-without-join-code permission', () => { + before('set join-without-join-code permission to true', async () => { + await updatePermission('join-without-join-code', ['admin']); + }); - return request - .post(api('channels.invite')) - .set(credentials) - .send({ - roomId: channel._id, - userId: 'rocket.cat', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', apiPublicChannelName); - expect(res.body).to.have.nested.property('channel.t', 'c'); - expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs + 1); - }); - }); + before('leave channel', (done) => { + request + .post(api('channels.leave')) + .set(credentials) + .send({ + roomId: testChannelWithCode._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); - it('/channels.addOwner', (done) => { - request - .post(api('channels.addOwner')) - .set(credentials) - .send({ - roomId: channel._id, - userId: 'rocket.cat', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); + it('should succeed when joining code-needed channel without join code and with join-without-join-code permission', (done) => { + request + .post(api('channels.join')) + .set(credentials) + .send({ + roomId: testChannelWithCode._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('channel._id', testChannelWithCode._id); + }) + .end(done); + }); + }); + }); }); describe('/channels.setDescription', () => { @@ -984,138 +1263,27 @@ describe('[Channels]', function () { }); }); - it('/channels.archive', (done) => { - request - .post(api('channels.archive')) - .set(credentials) - .send({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); - }); - - it('/channels.unarchive', (done) => { - request - .post(api('channels.unarchive')) - .set(credentials) - .send({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); - }); - - it('/channels.close', (done) => { - request - .post(api('channels.close')) - .set(credentials) - .send({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); - }); - - it('/channels.close', (done) => { - request - .post(api('channels.close')) - .set(credentials) - .send({ - roomName: apiPublicChannelName, - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - expect(res.body).to.have.property('error', `The channel, ${apiPublicChannelName}, is already closed to the sender`); - }) - .end(done); - }); - - it('/channels.open', (done) => { - request - .post(api('channels.open')) - .set(credentials) - .send({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); - }); + describe('/channels.members', () => { + let testUser; - it('/channels.list', (done) => { - request - .get(api('channels.list')) - .set(credentials) - .query({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.property('count'); - expect(res.body).to.have.property('total'); - }) - .end(done); - }); + before(async () => { + testUser = await createUser(); + await updateSetting('Accounts_SearchFields', 'username, name, bio, nickname'); + await request + .post(api('channels.invite')) + .set(credentials) + .send({ + roomId: channel._id, + userId: testUser._id, + }) + .expect('Content-Type', 'application/json') + .expect(200); + }); - it('/channels.list.joined', (done) => { - request - .get(api('channels.list.joined')) - .set(credentials) - .query({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.property('count'); - expect(res.body).to.have.property('total'); - }) - .end(done); - }); - it('/channels.counters', (done) => { - request - .get(api('channels.counters')) - .set(credentials) - .query({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.property('joined', true); - expect(res.body).to.have.property('members'); - expect(res.body).to.have.property('unreads'); - expect(res.body).to.have.property('unreadsFrom'); - expect(res.body).to.have.property('msgs'); - expect(res.body).to.have.property('latest'); - expect(res.body).to.have.property('userMentions'); - }) - .end(done); - }); + after(async () => { + await Promise.all([updateSetting('Accounts_SearchFields', 'username, name, bio, nickname'), deleteUser(testUser)]); + }); - describe('/channels.members', () => { it('should return an array of members by channel', (done) => { request .get(api('channels.members')) @@ -1162,75 +1330,34 @@ describe('[Channels]', function () { .set(credentials) .query({ roomId: channel._id, - filter: 'rocket.cat', + filter: testUser.username, }) .expect('Content-Type', 'application/json') .expect(200) .expect((res) => { expect(res.body).to.have.property('success', true); expect(res.body).to.have.property('members').and.to.be.an('array'); - expect(res.body).to.have.property('count'); expect(res.body).to.have.property('count', 1); + expect(res.body.members[0]._id).to.be.equal(testUser._id); + expect(res.body).to.have.property('count'); expect(res.body).to.have.property('total'); - expect(res.body).to.have.property('offset'); - }) - .end(done); - }); - }); - - it('/channels.rename', async () => { - const roomInfo = await getRoomInfo(channel._id); - - function failRenameChannel(name) { - it(`should not rename a channel to the reserved name ${name}`, (done) => { - request - .post(api('channels.rename')) - .set(credentials) - .send({ - roomId: channel._id, - name, - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - expect(res.body).to.have.property('error', `${name} is already in use :( [error-field-unavailable]`); - }) - .end(done); - }); - } - - reservedWords.forEach((name) => { - failRenameChannel(name); + expect(res.body).to.have.property('offset'); + }) + .end(done); }); - - return request - .post(api('channels.rename')) - .set(credentials) - .send({ - roomId: channel._id, - name: `EDITED${apiPublicChannelName}`, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', `EDITED${apiPublicChannelName}`); - expect(res.body).to.have.nested.property('channel.t', 'c'); - expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs + 1); - }); }); describe('/channels.getIntegrations', () => { let integrationCreatedByAnUser; let userCredentials; let createdChannel; + let user; + before((done) => { createRoom({ name: `test-integration-channel-${Date.now()}`, type: 'c' }).end((err, res) => { createdChannel = res.body.channel; createUser().then((createdUser) => { - const user = createdUser; + user = createdUser; login(user.username, password).then((credentials) => { userCredentials = credentials; updatePermission('manage-incoming-integrations', ['user']).then(() => { @@ -1258,8 +1385,14 @@ describe('[Channels]', function () { }); }); - after((done) => { - removeIntegration(integrationCreatedByAnUser._id, 'incoming').then(done); + after(async () => { + await Promise.all([ + deleteRoom({ type: 'c', roomId: createdChannel._id }), + removeIntegration(integrationCreatedByAnUser._id, 'incoming'), + updatePermission('manage-incoming-integrations', ['admin']), + updatePermission('manage-own-incoming-integrations', ['admin']), + deleteUser(user), + ]); }); it('should return the list of integrations of created channel and it should contain the integration created by user when the admin DOES have the permission', (done) => { @@ -1336,57 +1469,14 @@ describe('[Channels]', function () { }); }); - it('/channels.addAll', (done) => { - request - .post(api('channels.addAll')) - .set(credentials) - .send({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', `EDITED${apiPublicChannelName}`); - expect(res.body).to.have.nested.property('channel.t', 'c'); - }) - .end(done); - }); + describe('/channels.setCustomFields:', () => { + let withCFChannel; + let withoutCFChannel; - it('/channels.addLeader', (done) => { - request - .post(api('channels.addLeader')) - .set(credentials) - .send({ - roomId: channel._id, - userId: 'rocket.cat', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.a.property('success', true); - }) - .end(done); - }); - it('/channels.removeLeader', (done) => { - request - .post(api('channels.removeLeader')) - .set(credentials) - .send({ - roomId: channel._id, - userId: 'rocket.cat', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); - }); + after(async () => { + await deleteRoom({ type: 'c', roomId: withCFChannel._id }); + }); - describe('/channels.setCustomFields:', () => { - let cfchannel; it('create channel with customFields', (done) => { const customFields = { field0: 'value0' }; request @@ -1397,7 +1487,7 @@ describe('[Channels]', function () { customFields, }) .end((err, res) => { - cfchannel = res.body.channel; + withCFChannel = res.body.channel; done(); }); }); @@ -1406,7 +1496,7 @@ describe('[Channels]', function () { .get(api('channels.info')) .set(credentials) .query({ - roomId: cfchannel._id, + roomId: withCFChannel._id, }) .expect('Content-Type', 'application/json') .expect(200) @@ -1422,7 +1512,7 @@ describe('[Channels]', function () { .post(api('channels.setCustomFields')) .set(credentials) .send({ - roomId: cfchannel._id, + roomId: withCFChannel._id, customFields, }) .expect('Content-Type', 'application/json') @@ -1430,7 +1520,7 @@ describe('[Channels]', function () { .expect((res) => { expect(res.body).to.have.property('success', true); expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', cfchannel.name); + expect(res.body).to.have.nested.property('channel.name', withCFChannel.name); expect(res.body).to.have.nested.property('channel.t', 'c'); expect(res.body).to.have.nested.property('channel.customFields.field9', 'value9'); expect(res.body).to.have.not.nested.property('channel.customFields.field0', 'value0'); @@ -1441,7 +1531,7 @@ describe('[Channels]', function () { .get(api('channels.info')) .set(credentials) .query({ - roomId: cfchannel._id, + roomId: withCFChannel._id, }) .expect('Content-Type', 'application/json') .expect(200) @@ -1456,7 +1546,7 @@ describe('[Channels]', function () { .post(api('channels.delete')) .set(credentials) .send({ - roomName: cfchannel.name, + roomName: withCFChannel.name, }) .expect('Content-Type', 'application/json') .expect(200) @@ -1473,7 +1563,7 @@ describe('[Channels]', function () { name: `channel.cf.${Date.now()}`, }) .end((err, res) => { - cfchannel = res.body.channel; + withoutCFChannel = res.body.channel; done(); }); }); @@ -1483,7 +1573,7 @@ describe('[Channels]', function () { .post(api('channels.setCustomFields')) .set(credentials) .send({ - roomId: cfchannel._id, + roomId: withoutCFChannel._id, customFields, }) .expect('Content-Type', 'application/json') @@ -1491,7 +1581,7 @@ describe('[Channels]', function () { .expect((res) => { expect(res.body).to.have.property('success', true); expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', cfchannel.name); + expect(res.body).to.have.nested.property('channel.name', withoutCFChannel.name); expect(res.body).to.have.nested.property('channel.t', 'c'); expect(res.body).to.have.nested.property('channel.customFields.field1', 'value1'); }); @@ -1503,7 +1593,7 @@ describe('[Channels]', function () { .post(api('channels.setCustomFields')) .set(credentials) .send({ - roomName: cfchannel.name, + roomName: withoutCFChannel.name, customFields, }) .expect('Content-Type', 'application/json') @@ -1511,7 +1601,7 @@ describe('[Channels]', function () { .expect((res) => { expect(res.body).to.have.property('success', true); expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', cfchannel.name); + expect(res.body).to.have.nested.property('channel.name', withoutCFChannel.name); expect(res.body).to.have.nested.property('channel.t', 'c'); expect(res.body).to.have.nested.property('channel.customFields.field2', 'value2'); expect(res.body).to.have.nested.property('channel.customFields.field3', 'value3'); @@ -1526,7 +1616,7 @@ describe('[Channels]', function () { .post(api('channels.setCustomFields')) .set(credentials) .send({ - roomName: cfchannel.name, + roomName: withoutCFChannel.name, customFields, }) .expect('Content-Type', 'application/json') @@ -1534,7 +1624,7 @@ describe('[Channels]', function () { .expect((res) => { expect(res.body).to.have.property('success', true); expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', cfchannel.name); + expect(res.body).to.have.nested.property('channel.name', withoutCFChannel.name); expect(res.body).to.have.nested.property('channel.t', 'c'); expect(res.body).to.have.not.nested.property('channel.customFields.field2', 'value2'); expect(res.body).to.have.not.nested.property('channel.customFields.field3', 'value3'); @@ -1549,7 +1639,7 @@ describe('[Channels]', function () { .post(api('channels.setCustomFields')) .set(credentials) .send({ - roomName: cfchannel.name, + roomName: withoutCFChannel.name, customFields, }) .expect('Content-Type', 'application/json') @@ -1564,7 +1654,7 @@ describe('[Channels]', function () { .post(api('channels.delete')) .set(credentials) .send({ - roomName: cfchannel.name, + roomName: withoutCFChannel.name, }) .expect('Content-Type', 'application/json') .expect(200) @@ -1575,57 +1665,26 @@ describe('[Channels]', function () { }); }); - it('/channels.setJoinCode', async () => { - const roomInfo = await getRoomInfo(channel._id); - - return request - .post(api('channels.setJoinCode')) - .set(credentials) - .send({ - roomId: channel._id, - joinCode: '123', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', `EDITED${apiPublicChannelName}`); - expect(res.body).to.have.nested.property('channel.t', 'c'); - expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs); - }); - }); + describe('/channels.setDefault', () => { + let testChannel; + const name = `setDefault-${Date.now()}`; - it('/channels.setReadOnly', async () => { - const roomInfo = await getRoomInfo(channel._id); + before(async () => { + testChannel = (await createRoom({ type: 'c', name })).body.channel; + }); - return request - .post(api('channels.setReadOnly')) - .set(credentials) - .send({ - roomId: channel._id, - readOnly: true, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', `EDITED${apiPublicChannelName}`); - expect(res.body).to.have.nested.property('channel.t', 'c'); - expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs + 1); - }); - }); + after(async () => { + await deleteRoom({ type: 'c', roomId: testChannel._id }); + }); - describe('/channels.setDefault', () => { it('should set channel as default', async () => { - const roomInfo = await getRoomInfo(channel._id); + const roomInfo = await getRoomInfo(testChannel._id); return request .post(api('channels.setDefault')) .set(credentials) .send({ - roomId: channel._id, + roomId: testChannel._id, default: true, }) .expect('Content-Type', 'application/json') @@ -1633,20 +1692,20 @@ describe('[Channels]', function () { .expect((res) => { expect(res.body).to.have.property('success', true); expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', `EDITED${apiPublicChannelName}`); + expect(res.body).to.have.nested.property('channel.name', name); expect(res.body).to.have.nested.property('channel.t', 'c'); expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs); expect(res.body).to.have.nested.property('channel.default', true); }); }); it('should unset channel as default', async () => { - const roomInfo = await getRoomInfo(channel._id); + const roomInfo = await getRoomInfo(testChannel._id); return request .post(api('channels.setDefault')) .set(credentials) .send({ - roomId: channel._id, + roomId: testChannel._id, default: false, }) .expect('Content-Type', 'application/json') @@ -1654,7 +1713,7 @@ describe('[Channels]', function () { .expect((res) => { expect(res.body).to.have.property('success', true); expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', `EDITED${apiPublicChannelName}`); + expect(res.body).to.have.nested.property('channel.name', name); expect(res.body).to.have.nested.property('channel.t', 'c'); expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs); expect(res.body).to.have.nested.property('channel.default', false); @@ -1662,29 +1721,20 @@ describe('[Channels]', function () { }); }); - it('/channels.leave', async () => { - const roomInfo = await getRoomInfo(channel._id); + describe('/channels.setType', () => { + let testChannel; + const name = `setType-${Date.now()}`; - return request - .post(api('channels.leave')) - .set(credentials) - .send({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', `EDITED${apiPublicChannelName}`); - expect(res.body).to.have.nested.property('channel.t', 'c'); - expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs + 1); - }); - }); + before(async () => { + testChannel = (await createRoom({ type: 'c', name })).body.channel; + }); + + after(async () => { + await deleteRoom({ type: 'c', roomId: testChannel._id }); + }); - describe('/channels.setType', () => { it('should change the type public channel to private', async () => { - const roomInfo = await getRoomInfo(channel._id); + const roomInfo = await getRoomInfo(testChannel._id); request .post(api('channels.setType')) @@ -1698,7 +1748,7 @@ describe('[Channels]', function () { .expect((res) => { expect(res.body).to.have.property('success', true); expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', `EDITED${apiPublicChannelName}`); + expect(res.body).to.have.nested.property('channel.name', name); expect(res.body).to.have.nested.property('channel.t', 'p'); expect(res.body).to.have.nested.property('channel.msgs', roomInfo.channel.msgs + 1); }); @@ -1707,18 +1757,15 @@ describe('[Channels]', function () { describe('/channels.delete:', () => { let testChannel; - it('/channels.create', (done) => { - request - .post(api('channels.create')) - .set(credentials) - .send({ - name: `channel.test.${Date.now()}`, - }) - .end((err, res) => { - testChannel = res.body.channel; - done(); - }); + + before(async () => { + testChannel = (await createRoom({ type: 'c', name: `channel.test.${Date.now()}` })).body.channel; }); + + after(async () => { + await deleteRoom({ type: 'c', roomId: testChannel._id }); + }); + it('/channels.delete', (done) => { request .post(api('channels.delete')) @@ -1793,18 +1840,15 @@ describe('[Channels]', function () { describe('/channels.roles', () => { let testChannel; - it('/channels.create', (done) => { - request - .post(api('channels.create')) - .set(credentials) - .send({ - name: `channel.roles.test.${Date.now()}`, - }) - .end((err, res) => { - testChannel = res.body.channel; - done(); - }); + + before(async () => { + testChannel = (await createRoom({ type: 'c', name: `channel.roles.test.${Date.now()}` })).body.channel; + }); + + after(async () => { + await deleteRoom({ type: 'c', roomId: testChannel._id }); }); + it('/channels.invite', (done) => { request .post(api('channels.invite')) @@ -1868,18 +1912,15 @@ describe('[Channels]', function () { describe('/channels.moderators', () => { let testChannel; - it('/channels.create', (done) => { - request - .post(api('channels.create')) - .set(credentials) - .send({ - name: `channel.roles.test.${Date.now()}`, - }) - .end((err, res) => { - testChannel = res.body.channel; - done(); - }); + + before(async () => { + testChannel = (await createRoom({ type: 'c', name: `channel.moderators.test.${Date.now()}` })).body.channel; + }); + + after(async () => { + await deleteRoom({ type: 'c', roomId: testChannel._id }); }); + it('/channels.invite', (done) => { request .post(api('channels.invite')) @@ -1917,14 +1958,24 @@ describe('[Channels]', function () { .end(done); }); }); + describe('/channels.anonymousread', () => { - after(() => updateSetting('Accounts_AllowAnonymousRead', false)); + let testChannel; + + before(async () => { + testChannel = (await createRoom({ type: 'c', name: `channel.anonymousread.test.${Date.now()}` })).body.channel; + }); + + after(async () => { + await Promise.all([updateSetting('Accounts_AllowAnonymousRead', false), deleteRoom({ type: 'c', roomId: testChannel._id })]); + }); + it('should return an error when the setting "Accounts_AllowAnonymousRead" is disabled', (done) => { updateSetting('Accounts_AllowAnonymousRead', false).then(() => { request .get(api('channels.anonymousread')) .query({ - roomId: 'GENERAL', + roomId: testChannel._id, }) .expect('Content-Type', 'application/json') .expect(400) @@ -1943,7 +1994,7 @@ describe('[Channels]', function () { request .get(api('channels.anonymousread')) .query({ - roomId: 'GENERAL', + roomId: testChannel._id, }) .expect('Content-Type', 'application/json') .expect(200) @@ -1959,7 +2010,7 @@ describe('[Channels]', function () { request .get(api('channels.anonymousread')) .query({ - roomId: 'GENERAL', + roomId: testChannel._id, count: 5, offset: 0, }) @@ -1975,15 +2026,18 @@ describe('[Channels]', function () { }); describe('/channels.convertToTeam', () => { - before((done) => { - request - .post(api('channels.create')) - .set(credentials) - .send({ name: `channel-${Date.now()}` }) - .then((response) => { - this.newChannel = response.body.channel; - }) - .then(() => done()); + let testChannel; + + before(async () => { + testChannel = (await createRoom({ type: 'c', name: `channel.convertToTeam.test.${Date.now()}` })).body.channel; + }); + + after(async () => { + await Promise.all([ + updatePermission('create-team', ['admin', 'user']), + updatePermission('edit-room', ['admin', 'owner', 'moderator']), + deleteRoom({ type: 'c', roomId: testChannel._id }), + ]); }); it('should fail to convert channel if lacking edit-room permission', async () => { @@ -1993,7 +2047,7 @@ describe('[Channels]', function () { await request .post(api('channels.convertToTeam')) .set(credentials) - .send({ channelId: this.newChannel._id }) + .send({ channelId: testChannel._id }) .expect(403) .expect((res) => { expect(res.body).to.have.a.property('success', false); @@ -2007,7 +2061,7 @@ describe('[Channels]', function () { await request .post(api('channels.convertToTeam')) .set(credentials) - .send({ channelId: this.newChannel._id }) + .send({ channelId: testChannel._id }) .expect(403) .expect((res) => { expect(res.body).to.have.a.property('success', false); @@ -2019,8 +2073,8 @@ describe('[Channels]', function () { .post(api('channels.convertToTeam')) .set(credentials) .send({ - channelName: this.newChannel.name, - channelId: this.newChannel._id, + channelName: testChannel.name, + channelId: testChannel._id, }) .expect(400) .expect((res) => { @@ -2037,7 +2091,7 @@ describe('[Channels]', function () { await request .post(api('channels.convertToTeam')) .set(credentials) - .send({ channelId: this.newChannel._id }) + .send({ channelId: testChannel._id }) .expect(200) .expect((res) => { expect(res.body).to.have.a.property('success', true); @@ -2048,7 +2102,7 @@ describe('[Channels]', function () { await request .post(api('teams.convertToChannel')) .set(credentials) - .send({ teamName: this.newChannel.name }) + .send({ teamName: testChannel.name }) .expect(200) .expect((res) => { expect(res.body).to.have.a.property('success', true); @@ -2057,7 +2111,7 @@ describe('[Channels]', function () { await request .post(api('channels.convertToTeam')) .set(credentials) - .send({ channelName: this.newChannel.name }) + .send({ channelName: testChannel.name }) .expect(200) .expect((res) => { expect(res.body).to.have.a.property('success', true); @@ -2072,7 +2126,7 @@ describe('[Channels]', function () { request .post(api('channels.convertToTeam')) .set(credentials) - .send({ channelId: this.newChannel._id }) + .send({ channelId: testChannel._id }) .expect(400) .expect((res) => { expect(res.body).to.have.a.property('success', false); @@ -2081,64 +2135,12 @@ describe('[Channels]', function () { }); }); - describe.skip('/channels.setAutojoin', () => { - // let testTeam; + describe("Setting: 'Use Real Name': true", () => { let testChannel; - // let testUser1; - // let testUser2; - before(async () => { - const teamCreateRes = await request - .post(api('teams.create')) - .set(credentials) - .send({ name: `team-${Date.now()}` }); - - const { team } = teamCreateRes.body; - - const user1 = await createUser(); - const user2 = await createUser(); - - const channelCreateRes = await request - .post(api('channels.create')) - .set(credentials) - .send({ - name: `team-channel-${Date.now()}`, - extraData: { - teamId: team._id, - }, - }); - - const { channel } = channelCreateRes.body; - - // testTeam = team; - testChannel = channel; - // testUser1 = user1; - // testUser2 = user2; - - await request - .post(api('teams.addMembers')) - .set(credentials) - .send({ - name: team.name, - members: [{ userId: user1._id }, { userId: user2._id }], - }); - }); - - it('should add all existing team members', async () => { - const resAutojoin = await request - .post(api('channels.setAutojoin')) - .set(credentials) - .send({ roomName: testChannel.name, autojoin: true }) - .expect(200); - expect(resAutojoin.body).to.have.a.property('success', true); - const channelInfoResponse = await request.get(api('channels.info')).set(credentials).query({ roomId: testChannel._id }); - const { channel } = channelInfoResponse.body; - - return expect(channel.usersCount).to.be.equals(3); + before(async () => { + testChannel = (await createRoom({ type: 'c', name: `channel.anonymousread.test.${Date.now()}` })).body.channel; }); - }); - - describe("Setting: 'Use Real Name': true", () => { before(async () => { await updateSetting('UI_Use_Real_Name', true); @@ -2146,13 +2148,13 @@ describe('[Channels]', function () { .post(api('channels.join')) .set(credentials) .send({ - roomId: channel._id, + roomId: testChannel._id, }) .expect('Content-Type', 'application/json') .expect(200) .expect((res) => { expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('channel._id', channel._id); + expect(res.body).to.have.nested.property('channel._id', testChannel._id); }); await request @@ -2161,7 +2163,7 @@ describe('[Channels]', function () { .send({ message: { text: 'Sample message', - rid: channel._id, + rid: testChannel._id, }, }) .expect('Content-Type', 'application/json') @@ -2170,21 +2172,13 @@ describe('[Channels]', function () { expect(res.body).to.have.property('success', true); }); }); - after(async () => { - await updateSetting('UI_Use_Real_Name', false); - await request - .post(api('channels.leave')) - .set(credentials) - .send({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('channel._id', channel._id); - }); + after(async () => { + await Promise.all([ + updateSetting('Accounts_AllowAnonymousRead', false), + updateSetting('UI_Use_Real_Name', false), + deleteRoom({ type: 'c', roomId: testChannel._id }), + ]); }); it('/channels.list', (done) => { @@ -2199,7 +2193,7 @@ describe('[Channels]', function () { expect(res.body).to.have.property('total'); expect(res.body).to.have.property('channels').and.to.be.an('array'); - const retChannel = res.body.channels.find(({ _id }) => _id === channel._id); + const retChannel = res.body.channels.find(({ _id }) => _id === testChannel._id); expect(retChannel).to.have.nested.property('lastMessage.u.name', 'RocketChat Internal Admin Test'); }) @@ -2218,7 +2212,7 @@ describe('[Channels]', function () { expect(res.body).to.have.property('total'); expect(res.body).to.have.property('channels').and.to.be.an('array'); - const retChannel = res.body.channels.find(({ _id }) => _id === channel._id); + const retChannel = res.body.channels.find(({ _id }) => _id === testChannel._id); expect(retChannel).to.have.nested.property('lastMessage.u.name', 'RocketChat Internal Admin Test'); }) diff --git a/apps/meteor/tests/end-to-end/api/03-groups.js b/apps/meteor/tests/end-to-end/api/03-groups.js index df736ecbeb86..07b03494900f 100644 --- a/apps/meteor/tests/end-to-end/api/03-groups.js +++ b/apps/meteor/tests/end-to-end/api/03-groups.js @@ -1066,7 +1066,7 @@ describe('[Groups]', function () { }); describe('/groups.files', async () => { - await testFileUploads('groups.files', group); + await testFileUploads('groups.files', 'p'); }); describe('/groups.listAll', () => { diff --git a/apps/meteor/tests/end-to-end/api/04-direct-message.js b/apps/meteor/tests/end-to-end/api/04-direct-message.js index a8ea87e2eddc..be8868ef6b48 100644 --- a/apps/meteor/tests/end-to-end/api/04-direct-message.js +++ b/apps/meteor/tests/end-to-end/api/04-direct-message.js @@ -333,7 +333,7 @@ describe('[Direct Messages]', function () { }); describe('[/im.files]', async () => { - await testFileUploads('im.files', directMessage, 'invalid-channel'); + await testFileUploads('im.files', 'd', 'invalid-channel'); }); describe('/im.messages', () => { From c2ca061488d61dc48ff6f94a0c07952f68f1ab60 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Mon, 5 Feb 2024 13:53:32 -0300 Subject: [PATCH 014/207] feat: Freezes permissions table first column (#31626) --- .changeset/five-dragons-joke.md | 5 ++++ .../components/GenericTable/GenericTable.tsx | 5 ++-- .../PermissionsTable/PermissionsTable.tsx | 25 +++++++++++++++++-- .../PermissionsTable/RoleHeader.tsx | 2 +- 4 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 .changeset/five-dragons-joke.md diff --git a/.changeset/five-dragons-joke.md b/.changeset/five-dragons-joke.md new file mode 100644 index 000000000000..09a636ec3213 --- /dev/null +++ b/.changeset/five-dragons-joke.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +Freezes the permission table's first column allowing the user to visualize the permission name when scrolling horizontally diff --git a/apps/meteor/client/components/GenericTable/GenericTable.tsx b/apps/meteor/client/components/GenericTable/GenericTable.tsx index c44deeaa9016..6fa3f247aa29 100644 --- a/apps/meteor/client/components/GenericTable/GenericTable.tsx +++ b/apps/meteor/client/components/GenericTable/GenericTable.tsx @@ -1,12 +1,13 @@ import { Box, Table } from '@rocket.chat/fuselage'; -import React, { type ForwardedRef, type ReactNode, type TableHTMLAttributes, forwardRef } from 'react'; +import type { ComponentProps } from 'react'; +import React, { type ForwardedRef, type ReactNode, forwardRef } from 'react'; import ScrollableContentWrapper from '../ScrollableContentWrapper'; type GenericTableProps = { fixed?: boolean; children: ReactNode; -} & Omit, 'is'>; +} & ComponentProps; export const GenericTable = forwardRef(function GenericTable( { fixed = true, children, ...props }: GenericTableProps, diff --git a/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx b/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx index 1d688da630a9..583f9d237252 100644 --- a/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx +++ b/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx @@ -1,4 +1,5 @@ -import { Margins, Tabs, Button, Pagination } from '@rocket.chat/fuselage'; +import { css } from '@rocket.chat/css-in-js'; +import { Margins, Tabs, Button, Pagination, Palette } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useRoute, usePermission, useMethod, useTranslation, useSetModal } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -55,6 +56,26 @@ const PermissionsTable = ({ isEnterprise }: { isEnterprise: boolean }): ReactEle }); }); + const fixedColumnStyle = css` + tr > th { + &:first-child { + position: sticky; + left: 0; + background-color: ${Palette.surface['surface-light']}; + z-index: 12; + } + } + tr > td { + &:first-child { + position: sticky; + left: 0; + box-shadow: -1px 0 0 ${Palette.stroke['stroke-light']} inset; + background-color: ${Palette.surface['surface-light']}; + z-index: 11; + } + } + `; + return ( @@ -89,7 +110,7 @@ const PermissionsTable = ({ isEnterprise }: { isEnterprise: boolean }): ReactEle {permissions?.length === 0 && } {permissions?.length > 0 && ( <> - + {t('Name')} {roleList?.map(({ _id, name, description }) => ( diff --git a/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx b/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx index 69f99d67f114..006514ce6b31 100644 --- a/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx +++ b/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx @@ -25,7 +25,7 @@ const RoleHeader = ({ _id, name, description }: RoleHeaderProps): ReactElement = return ( - From ed451251a3abfd64b26c9bdc8d18e9b84754a1f3 Mon Sep 17 00:00:00 2001 From: Gustavo Reis Bauer Date: Mon, 5 Feb 2024 14:27:49 -0300 Subject: [PATCH 015/207] fix: Looking at the user's permission before rendering the 'Start Call' button on UserInfo (#31600) --- .changeset/small-beers-call.md | 5 +++++ .../useUserInfoActions/actions/useCallAction.tsx | 12 +++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 .changeset/small-beers-call.md diff --git a/.changeset/small-beers-call.md b/.changeset/small-beers-call.md new file mode 100644 index 000000000000..0e0f7414b8fc --- /dev/null +++ b/.changeset/small-beers-call.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Looking at the user's permission before rendering the 'Start Call' button on the UserInfo panel, so if the user does not have the permissions, the button does not show diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useCallAction.tsx b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useCallAction.tsx index 4889a50dbb7d..fa0fc46fe804 100644 --- a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useCallAction.tsx +++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useCallAction.tsx @@ -1,6 +1,6 @@ import type { IUser } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; -import { useTranslation, useUserRoom, useUserId, useUserSubscriptionByName } from '@rocket.chat/ui-contexts'; +import { useTranslation, useUserRoom, useUserId, useUserSubscriptionByName, useSetting, usePermission } from '@rocket.chat/ui-contexts'; import { useMemo } from 'react'; import { closeUserCard } from '../../../../../../app/ui/client/lib/userCard'; @@ -20,6 +20,9 @@ export const useCallAction = (user: Pick): UserInfoAc const isRinging = useVideoConfIsRinging(); const ownUserId = useUserId(); + const enabledForDMs = useSetting('VideoConf_Enable_DMs'); + const permittedToCallManagement = usePermission('call-management', room?._id); + const videoCallOption = useMemo(() => { const action = async (): Promise => { if (isCalling || isRinging || !room) { @@ -35,7 +38,10 @@ export const useCallAction = (user: Pick): UserInfoAc } }; - return room && !isRoomFederated(room) && user._id !== ownUserId + const shouldShowStartCall = + room && !isRoomFederated(room) && user._id !== ownUserId && enabledForDMs && permittedToCallManagement && !isCalling && !isRinging; + + return shouldShowStartCall ? { content: t('Start_call'), icon: 'phone' as const, @@ -43,7 +49,7 @@ export const useCallAction = (user: Pick): UserInfoAc type: 'communication' as UserInfoActionType, } : undefined; - }, [t, room, dispatchPopup, dispatchWarning, isCalling, isRinging, ownUserId, user._id]); + }, [room, user._id, ownUserId, enabledForDMs, permittedToCallManagement, isCalling, isRinging, t, dispatchPopup, dispatchWarning]); return videoCallOption; }; From a21ae019185ef9749e93e6af19abc9dde8a74617 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Mon, 5 Feb 2024 18:23:10 -0300 Subject: [PATCH 016/207] fix: Messages not updating properly after pruning the room (#31658) --- .changeset/twenty-yaks-grab.md | 5 +++++ apps/meteor/app/lib/server/functions/cleanRoomHistory.ts | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/twenty-yaks-grab.md diff --git a/.changeset/twenty-yaks-grab.md b/.changeset/twenty-yaks-grab.md new file mode 100644 index 000000000000..38cf79704956 --- /dev/null +++ b/.changeset/twenty-yaks-grab.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes an issue where messages are not updating properly after pruning the room diff --git a/apps/meteor/app/lib/server/functions/cleanRoomHistory.ts b/apps/meteor/app/lib/server/functions/cleanRoomHistory.ts index 2b495ec4b0d0..133eba555a69 100644 --- a/apps/meteor/app/lib/server/functions/cleanRoomHistory.ts +++ b/apps/meteor/app/lib/server/functions/cleanRoomHistory.ts @@ -120,6 +120,7 @@ export async function cleanRoomHistory({ ignoreDiscussion, ts, users: fromUsers, + ids: selectedMessageIds, }); } return count; From f612d741f3a87e07204d53551ed7eee860fb7f6d Mon Sep 17 00:00:00 2001 From: Abhi Patel <74670675+abhipatel0211@users.noreply.github.com> Date: Tue, 6 Feb 2024 20:05:39 +0530 Subject: [PATCH 017/207] fix: Login form GUI crash after closing 2FA modal (#31625) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Henrique Guimarães Ribeiro <43561537+rique223@users.noreply.github.com> --- .changeset/tall-bears-compete.md | 5 +++++ packages/web-ui-registration/src/LoginForm.tsx | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .changeset/tall-bears-compete.md diff --git a/.changeset/tall-bears-compete.md b/.changeset/tall-bears-compete.md new file mode 100644 index 000000000000..6929ba4d0afd --- /dev/null +++ b/.changeset/tall-bears-compete.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/web-ui-registration": patch +--- + +Fixed a bug that caused the Login page to crash when closing the Two-Factor Authentication modal using the Cancel button or the X button. diff --git a/packages/web-ui-registration/src/LoginForm.tsx b/packages/web-ui-registration/src/LoginForm.tsx index 55c638df6707..3290b9f16191 100644 --- a/packages/web-ui-registration/src/LoginForm.tsx +++ b/packages/web-ui-registration/src/LoginForm.tsx @@ -56,7 +56,7 @@ const LOGIN_SUBMIT_ERRORS = { }, } as const; -export type LoginErrors = keyof typeof LOGIN_SUBMIT_ERRORS; +export type LoginErrors = keyof typeof LOGIN_SUBMIT_ERRORS | 'totp-canceled'; export const LoginForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRouter }): ReactElement => { const { @@ -111,7 +111,12 @@ export const LoginForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRoute }, [errorOnSubmit]); const renderErrorOnSubmit = (error: LoginErrors) => { + if (error === 'totp-canceled') { + return null; + } + const { type, i18n } = LOGIN_SUBMIT_ERRORS[error]; + return ( {t(i18n)} From 46e8272e08b1618182e6bada9104481d7803a553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Guimar=C3=A3es=20Ribeiro?= Date: Tue, 6 Feb 2024 18:50:42 -0300 Subject: [PATCH 018/207] feat: Improve 2fa modals and change resend email to button (#31621) --- .changeset/thirty-beds-taste.md | 5 +++++ .../TwoFactorModal/TwoFactorEmailModal.tsx | 15 ++++++++------- .../TwoFactorModal/TwoFactorTotpModal.tsx | 9 +++++---- .../views/account/security/TwoFactorTOTP.tsx | 2 +- .../packages/rocketchat-i18n/i18n/af.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/ar.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/az.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/be-BY.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/bg.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/bs.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/ca.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/cs.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/cy.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/da.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/de-AT.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/de-IN.i18n.json | 3 +-- .../packages/rocketchat-i18n/i18n/de.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/el.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/en.i18n.json | 14 +++++++++----- .../packages/rocketchat-i18n/i18n/eo.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/es.i18n.json | 6 ++---- .../packages/rocketchat-i18n/i18n/fa.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/fi.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/fr.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/hr.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/hu.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/id.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/it.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/ja.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/ka-GE.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/km.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/ko.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/ku.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/lo.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/lt.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/lv.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/mn.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/ms-MY.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/nl.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/no.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/pl.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/pt-BR.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/pt.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/ro.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/ru.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/sk-SK.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/sl-SI.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/sq.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/sr.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/sv.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/ta-IN.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/th-TH.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/tr.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/uk.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/vi-VN.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/zh-HK.i18n.json | 1 - .../packages/rocketchat-i18n/i18n/zh-TW.i18n.json | 2 -- .../packages/rocketchat-i18n/i18n/zh.i18n.json | 2 -- 58 files changed, 31 insertions(+), 95 deletions(-) create mode 100644 .changeset/thirty-beds-taste.md diff --git a/.changeset/thirty-beds-taste.md b/.changeset/thirty-beds-taste.md new file mode 100644 index 000000000000..fd525635c870 --- /dev/null +++ b/.changeset/thirty-beds-taste.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Improved the layout of the 2FA modals and changed the email 2FA resend email anchor to a button. diff --git a/apps/meteor/client/components/TwoFactorModal/TwoFactorEmailModal.tsx b/apps/meteor/client/components/TwoFactorModal/TwoFactorEmailModal.tsx index 19074f7f6b34..9c7bd8191c92 100644 --- a/apps/meteor/client/components/TwoFactorModal/TwoFactorEmailModal.tsx +++ b/apps/meteor/client/components/TwoFactorModal/TwoFactorEmailModal.tsx @@ -1,4 +1,4 @@ -import { Box, FieldGroup, TextInput, Field, FieldLabel, FieldRow, FieldError } from '@rocket.chat/fuselage'; +import { Box, FieldGroup, TextInput, Field, FieldLabel, FieldRow, FieldError, Button } from '@rocket.chat/fuselage'; import { useAutoFocus, useUniqueId } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement, ChangeEvent, SyntheticEvent } from 'react'; @@ -51,26 +51,27 @@ const TwoFactorEmailModal = ({ onConfirm, onClose, emailOrUsername, invalidAttem wrapperFunction={(props) => } onCancel={onClose} confirmText={t('Verify')} - title={t('Two-factor_authentication_email')} + title={t('Enter_authentication_code')} onClose={onClose} variant='warning' - icon='info' confirmDisabled={!code} + tagline={t('Email_two-factor_authentication')} + icon={null} > - {t('Verify_your_email_with_the_code_we_sent')} + {t('Enter_the_code_we_just_emailed_you')} - + {invalidAttempt && {t('Invalid_password')}} - + ); }; diff --git a/apps/meteor/client/components/TwoFactorModal/TwoFactorTotpModal.tsx b/apps/meteor/client/components/TwoFactorModal/TwoFactorTotpModal.tsx index 04aa7a4f94f0..6f36c9c8ce26 100644 --- a/apps/meteor/client/components/TwoFactorModal/TwoFactorTotpModal.tsx +++ b/apps/meteor/client/components/TwoFactorModal/TwoFactorTotpModal.tsx @@ -34,19 +34,20 @@ const TwoFactorTotpModal = ({ onConfirm, onClose, invalidAttempt }: TwoFactorTot wrapperFunction={(props) => } onCancel={onClose} confirmText={t('Verify')} - title={t('Two Factor Authentication')} + title={t('Enter_TOTP_password')} onClose={onClose} variant='warning' - icon='info' confirmDisabled={!code} + tagline={t('Two-factor_authentication')} + icon={null} > - {t('Open_your_authentication_app_and_enter_the_code')} + {t('Enter_the_code_provided_by_your_authentication_app_to_continue')} - + {invalidAttempt && {t('Invalid_password')}} diff --git a/apps/meteor/client/views/account/security/TwoFactorTOTP.tsx b/apps/meteor/client/views/account/security/TwoFactorTOTP.tsx index e095efcba2d6..a00a64ee991b 100644 --- a/apps/meteor/client/views/account/security/TwoFactorTOTP.tsx +++ b/apps/meteor/client/views/account/security/TwoFactorTOTP.tsx @@ -113,7 +113,7 @@ const TwoFactorTOTP = (props: ComponentProps): ReactElement => { return ( - {t('Two-factor_authentication')} + {t('Two-factor_authentication_via_TOTP')} {!totpEnabled && !registeringTotp && ( <> {t('Two-factor_authentication_is_currently_disabled')} diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json index 2ce3f4f8a0ed..f9412da3aee1 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json @@ -1845,7 +1845,6 @@ "Open_channel_user_search": "`%s` - Open kanaal / gebruikers soek", "Open_days_of_the_week": "Open Dae van die Week", "Open_Livechats": "Open Livechats", - "Open_your_authentication_app_and_enter_the_code": "Maak jou verifikasieprogram oop en voer die kode in. U kan ook een van u rugsteunkodes gebruik.", "Opened": "geopen", "Opened_in_a_new_window": "Geopen in 'n nuwe venster.", "Opens_a_channel_group_or_direct_message": "Maak 'n kanaal-, groep- of direkte boodskap oop", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json index a31bdc1bd25f..a677f60895ac 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json @@ -3213,7 +3213,6 @@ "Open_Livechats": "دردشات جارية", "Open_menu": "Open_menu", "Open_thread": "فتح موضوع", - "Open_your_authentication_app_and_enter_the_code": "افتح تطبيق المصادقة الخاص بك وأدخل الرمز. يمكنك أيضًا استخدام أحد رموز النسخ الاحتياطي.", "Opened": "مفتوح", "Opened_in_a_new_window": "تم الفتح في نافذة جديدة", "Opens_a_channel_group_or_direct_message": "فتح قناة أو مجموعة أو رسالة مباشرة", @@ -4537,7 +4536,6 @@ "Verified": "تم التحقق", "Verify": "تحقق", "Verify_your_email": "تحقق من بريدك الإلكتروني", - "Verify_your_email_with_the_code_we_sent": "تحقق من بريدك الإلكتروني للحصول على الرمز الذي أرسلناه", "Version": "الإصدار", "Version_version": "الإصدار {{version}}", "Video_Chat_Window": "دردشة الفيديو", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json index d7e76cd783d9..61e5a5dba863 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json @@ -1845,7 +1845,6 @@ "Open_channel_user_search": "`%s` - Açıq Kanal / İstifadəçi axtarışı", "Open_days_of_the_week": "Həftənin Açıq Günləri", "Open_Livechats": "Açıq Livechats", - "Open_your_authentication_app_and_enter_the_code": "Doğrulama tətbiqini açın və kodu daxil edin. Ayrıca yedek kodlarınızdan birini də istifadə edə bilərsiniz.", "Opened": "Açıldı", "Opened_in_a_new_window": "Yeni bir pəncərədə açıldı.", "Opens_a_channel_group_or_direct_message": "Bir kanal, qrup və ya birbaşa mesajı açır", 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 55a753d7b972..cb52163f1c5f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json @@ -1861,7 +1861,6 @@ "Open_channel_user_search": "`%s` - Open Channel / Пошук карыстальніка", "Open_days_of_the_week": "Адкрытыя Дні тыдня", "Open_Livechats": "адкрытыя Livechats", - "Open_your_authentication_app_and_enter_the_code": "Адкрыйце прыкладанне аўтэнтыфікацыі і ўвядзіце код. Вы таксама можаце выкарыстоўваць адзін з рэзервовых кодаў.", "Opened": "адкрыты", "Opened_in_a_new_window": "Адкрыты ў новым акне.", "Opens_a_channel_group_or_direct_message": "Адкрывае канал, групу ці прамое паведамленне", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json index 455c499adca8..492a6c2dc616 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json @@ -1842,7 +1842,6 @@ "Open_channel_user_search": "`%s` - Отваряне на канал / Потребителско търсене", "Open_days_of_the_week": "Дни на отворените врати на седмицата", "Open_Livechats": "Отворете Livechats", - "Open_your_authentication_app_and_enter_the_code": "Отворете приложението си за удостоверяване и въведете кода. Можете също така да използвате един от резервните си кодове.", "Opened": "Отворен", "Opened_in_a_new_window": "Отваря се в нов прозорец.", "Opens_a_channel_group_or_direct_message": "Отваря канал, група или директно съобщение", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json index 8dd9ddbfbb73..4b4481e620f3 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json @@ -1838,7 +1838,6 @@ "Open_channel_user_search": "`%s` - Otvori kanal / Pretraživanje korisnika", "Open_days_of_the_week": "Otvoreni Dani u Tjednu", "Open_Livechats": "Otvori Livechat", - "Open_your_authentication_app_and_enter_the_code": "Otvorite aplikaciju za provjeru autentičnosti i unesite kôd. Možete upotrijebiti i jedan od vaših pričuvnih kodova.", "Opened": "Otvoreno", "Opened_in_a_new_window": "Otvoren u novom prozoru.", "Opens_a_channel_group_or_direct_message": "Otvori sobu, grupu ili direktnu poruku", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json index dfb6a98cdb5c..d6538ddd8366 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json @@ -3166,7 +3166,6 @@ "Open_Livechats": "Xats en curs", "Open_menu": "Menú obert", "Open_thread": "Fil obert", - "Open_your_authentication_app_and_enter_the_code": "Obre l'app d'autenticació i entra el codi. També pots utilitzar un dels codis de recuperació.", "Opened": "Obert", "Opened_in_a_new_window": "Obert en una nova finestra.", "Opens_a_channel_group_or_direct_message": "Obrir un canal, grup o missatge directe", @@ -4443,7 +4442,6 @@ "Verified": "Verificat", "Verify": "Verifica", "Verify_your_email": "Verifica el teu correu electrònic", - "Verify_your_email_with_the_code_we_sent": "Verifiqui el seu correu electrònic per al codi que enviem", "Version": "Versió", "Version_version": "Version {{version}}", "Video_Chat_Window": "Vídeo-xat", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json index 8a617d173f5d..8721449b0cf4 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json @@ -2680,7 +2680,6 @@ "Open_Days": "Otevřené dny", "Open_days_of_the_week": "Otevírací doba přes týden", "Open_Livechats": "Probíhající chaty", - "Open_your_authentication_app_and_enter_the_code": "Otevřete autentizační aplikaci a zadejte vygenerovaný kód. Můžete použít jeden ze svých záložních kódů.", "Opened": "Otevřený", "Opened_in_a_new_window": "Otevřeno v novém okně", "Opens_a_channel_group_or_direct_message": "Otevře místnost, skupinu nebo přímou zprávu", @@ -3762,7 +3761,6 @@ "Verified": "Ověřený", "Verify": "Ověřit", "Verify_your_email": "Ověřte svůj email", - "Verify_your_email_with_the_code_we_sent": "Zadejte kód z emailu", "Version": "Verze", "Version_version": "Verze {{version}}", "Video_Chat_Window": "Video chat", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json index 4070f86a2896..26d6af592d26 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json @@ -1840,7 +1840,6 @@ "Open_channel_user_search": "`%s` - Sianel Agored / Chwiliad Defnyddwyr", "Open_days_of_the_week": "Diwrnodau Agored yr Wythnos", "Open_Livechats": "Agorwch Farchnata", - "Open_your_authentication_app_and_enter_the_code": "Agorwch eich app dilysu a nodwch y cod. Gallwch hefyd ddefnyddio un o'ch codau wrth gefn.", "Opened": "Agorwyd", "Opened_in_a_new_window": "Agorwyd mewn ffenestr newydd.", "Opens_a_channel_group_or_direct_message": "Yn agor sianel, grŵp neu neges uniongyrchol", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json index 0bcfce6c606c..51f7290b324a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json @@ -2689,7 +2689,6 @@ "Open_Days": "Åbne dage", "Open_days_of_the_week": "Åbne dage i ugen", "Open_Livechats": "Åbn Livechats", - "Open_your_authentication_app_and_enter_the_code": "Åbn din autentificeringsapp og indtast koden. Du kan også bruge en af ​​dine backupkoder.", "Opened": "åbnede", "Opened_in_a_new_window": "Åbnet i et nyt vindue.", "Opens_a_channel_group_or_direct_message": "Åbner en kanal, gruppe eller direkte besked", @@ -3779,7 +3778,6 @@ "Verified": "Bekræftet", "Verify": "Verificere", "Verify_your_email": "Bekræft din e-mail", - "Verify_your_email_with_the_code_we_sent": "Bekræft din e-mail med den kode vi har tilsendt", "Version": "Version", "Version_version": "Version {{version}}", "Video_Chat_Window": "Videochat", 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 3a3a714b32cb..c2cd820646e9 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json @@ -1848,7 +1848,6 @@ "Open_channel_user_search": "`%s` - Öffnen Sie Kanal / Benutzersuche", "Open_days_of_the_week": "Offene Tage der Woche", "Open_Livechats": "Öffnen Sie die Livechats", - "Open_your_authentication_app_and_enter_the_code": "Öffnen Sie Ihre Authentifizierungs-App und geben Sie den Code ein. Sie können auch einen Ihrer Ersatzcodes verwenden.", "Opened": "Geöffnet", "Opened_in_a_new_window": "In einem neuen Fenster geöffnet.", "Opens_a_channel_group_or_direct_message": "Eröffnet einen Kanal, eine Gruppe oder Direktnachrichten", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/de-IN.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/de-IN.i18n.json index 5fffe589894a..f7de8c69378c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de-IN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de-IN.i18n.json @@ -2107,7 +2107,6 @@ "Open_conversations": "Offene Konversationen", "Open_days_of_the_week": "Offene Wochentage", "Open_Livechats": "Offene Livechats", - "Open_your_authentication_app_and_enter_the_code": "Öffne Deine Authentifizierungs-App und gib den Code ein. Alternativ kannst Du auch einen Deiner Backup-Codes verwenden", "Opened": "Geöffnet", "Opened_in_a_new_window": "In einem neuem Fenster geöffnet", "Opens_a_channel_group_or_direct_message": "Öffnet einen Kanal, eine Gruppe oder Direktnachrichten", @@ -3101,4 +3100,4 @@ "Your_question": "Deine Frage", "Your_server_link": "Dein Server-Link", "Your_workspace_is_ready": "Dein Arbeitsbereich ist einsatzbereit 🎉" -} +} \ 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 7c86c298ebf3..5101aa2b9776 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json @@ -3596,7 +3596,6 @@ "Open_menu": "Menü_öffnen", "Open_settings": "Einstellungen öffnen", "Open_thread": "Thread öffnen", - "Open_your_authentication_app_and_enter_the_code": "Öffnen Sie Ihre Authentifizierungs-App und geben Sie den Code ein. Sie können auch einen Ihrer Backup-Codes verwenden", "Opened": "Geöffnet", "Opened_in_a_new_window": "In einem neuem Fenster geöffnet", "Opens_a_channel_group_or_direct_message": "Öffnet einen Channel, eine Gruppe oder Direktnachrichten", @@ -5061,7 +5060,6 @@ "Verified": "Überprüft", "Verify": "Überprüfen", "Verify_your_email": "Bestätigen Sie Ihre E-Mail", - "Verify_your_email_with_the_code_we_sent": "Prüfen Sie Ihre Mails auf den Code, den wir Ihnen eben geschickt haben.", "Version": "Version", "Version_version": "Version {{version}}", "App_version_incompatible_tooltip": "App nicht kompatibel mit Rocket.Chat-Version", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json index 60f77225fbf0..d10bc47830a7 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json @@ -1853,7 +1853,6 @@ "Open_channel_user_search": "`%s` - Ανοικτό κανάλι / Αναζήτηση χρηστών", "Open_days_of_the_week": "Ανοιχτές Ημέρες της Εβδομάδας", "Open_Livechats": "Άνοιγμα Livechats", - "Open_your_authentication_app_and_enter_the_code": "Ανοίξτε την εφαρμογή ελέγχου ταυτότητας και εισαγάγετε τον κωδικό. Μπορείτε επίσης να χρησιμοποιήσετε έναν από τους εφεδρικούς κωδικούς σας.", "Opened": "άνοιξε", "Opened_in_a_new_window": "Άνοιξε σε νέο παράθυρο.", "Opens_a_channel_group_or_direct_message": "Ανοίγει κανάλι, ομάδα ή άμεσο μήνυμα", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index f63a8974030a..3c574ac6d555 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1074,7 +1074,7 @@ "Cloud_registration_required": "Registration Required", "Cloud_registration_required_description": "Looks like during setup you didn't chose to register your workspace.", "Cloud_registration_required_link_text": "Click here to register your workspace.", - "Cloud_resend_email": "Resend Email", + "Cloud_resend_email": "Resend email", "Cloud_Service_Agree_PrivacyTerms": "Cloud Service Privacy Terms Agreement", "Cloud_Service_Agree_PrivacyTerms_Description": "I agree with the [Terms](https://rocket.chat/terms) & [Privacy Policy](https://rocket.chat/privacy)", "Cloud_Service_Agree_PrivacyTerms_Login_Disabled_Warning": "You should accept the cloud privacy terms (Setup Wizard > Cloud Info > Cloud Service Privacy Terms Agreement) to connect to your cloud workspace", @@ -1880,6 +1880,7 @@ "Email_subject": "Email Subject", "Enterprise_License": "Enterprise License", "Enterprise_License_Description": "If your workspace is registered and license is provided by Rocket.Chat cloud you don't need to manually update the license here.", + "Email_two-factor_authentication": "Email two-factor authentication", "Email_verified": "Email verified", "Enterprise_Only": "Enterprise only", "Email_sent": "Email sent", @@ -1940,10 +1941,13 @@ "Enter_authentication_code": "Enter authentication code", "Enter_Behaviour": "Enter key Behaviour", "Enter_Behaviour_Description": "This changes if the enter key will send a message or do a line break", + "Enter_code_here": "Enter code here", "Enter_E2E_password": "Enter E2E password", "Enter_name_here": "Enter name here", "Enter_Normal": "Normal mode (send with Enter)", + "Enter_the_code_we_just_emailed_you": "Enter the code we just emailed you.", "Enter_to": "Enter to", + "Enter_TOTP_password": "Enter TOTP password", "Enter_your_E2E_password": "Enter your E2E password", "Enter_your_password_to_delete_your_account": "Enter your password to delete your account. This cannot be undone.", "Enter_your_username_to_delete_your_account": "Enter your username to delete your account. This cannot be undone.", @@ -3947,7 +3951,7 @@ "Open_settings": "Open settings", "Open-source_conference_call_solution": "Open-source conference call solution.", "Open_thread": "Open Thread", - "Open_your_authentication_app_and_enter_the_code": "Open your authentication app and enter the code. You can also use one of your backup codes.", + "Enter_the_code_provided_by_your_authentication_app_to_continue": "Enter the code provided by your authentication app to continue. You can also use one of your backup codes.", "Opened": "Opened", "Opened_in_a_new_window": "Opened in a new window.", "Opens_a_channel_group_or_direct_message": "Opens a channel, group or direct message", @@ -5318,9 +5322,10 @@ "Turn_off_answer_calls": "Turn off answer calls", "Turn_off_video": "Turn off video", "Two Factor Authentication": "Two Factor Authentication", - "Two-factor_authentication": "Two-factor authentication via TOTP", + "Two-factor_authentication": "Two-factor authentication", + "Two-factor_authentication_via_TOTP": "Two-factor authentication via TOTP", "Two-factor_authentication_disabled": "Two-factor authentication disabled", - "Two-factor_authentication_email": "Two-factor authentication via Email", + "Two-factor_authentication_email": "Two-factor authentication via email", "Two-factor_authentication_email_is_currently_disabled": "Two-factor authentication via Email is currently disabled", "Two-factor_authentication_enabled": "Two-factor authentication enabled", "Two-factor_authentication_is_currently_disabled": "Two-factor authentication via TOTP is currently disabled", @@ -5572,7 +5577,6 @@ "Verified": "Verified", "Verify": "Verify", "Verify_your_email": "Verify your email", - "Verify_your_email_with_the_code_we_sent": "Verify your email with the code we sent", "Version": "Version", "Version_version": "Version {{version}}", "App_Request_Admin_Message": "Hi {{admin_name}}, {{user_name}} submitted a request to install {{app_name}} app on this workspace. \n \n This is the message they included: \n>{{message}} \n \n To learn more and install the {{app_name}} app, [click here]({{learn_more}}).", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json index 0f13dce91a00..f5ff30796309 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json @@ -1845,7 +1845,6 @@ "Open_channel_user_search": "`%s` - Malferma Kanalo / Uzanto-serĉo", "Open_days_of_the_week": "Malfermaj Tagoj de la Semajno", "Open_Livechats": "Malfermu Livechats", - "Open_your_authentication_app_and_enter_the_code": "Malfermu vian aŭtentigan aperon kaj eniru la kodon. Vi ankaŭ povas uzi unu el viaj kopiaj kodoj.", "Opened": "Malfermita", "Opened_in_a_new_window": "Malfermita en nova fenestro.", "Opens_a_channel_group_or_direct_message": "Malfermas kanalon, grupon aŭ rektan mesaĝon", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json index d6fa873437dc..47ac5b69953c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json @@ -3199,7 +3199,6 @@ "Open_Livechats": "Chats en curso", "Open_menu": "Open_menu", "Open_thread": "Hilo abierto", - "Open_your_authentication_app_and_enter_the_code": "Abre tu aplicación de autenticación e introduce el código. También puedes usar uno de tus códigos de respaldo.", "Opened": "Abierto", "Opened_in_a_new_window": "Abierto en una nueva ventana.", "Opens_a_channel_group_or_direct_message": "Abre un canal, grupo o mensaje directo", @@ -3940,9 +3939,7 @@ "Star": "Destacar", "Star_Message": "Destacar mensaje", "Starred_Messages": "Mensajes destacados", - "subscription.callout.description.limitsExceeded_many": "Su espacio de trabajo ha superado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "Start": "Iniciar", - "subscription.callout.description.limitsReached_many": "Su espacio de trabajo ha alcanzado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "Start_audio_call": "Iniciar llamada de audio", "Start_Chat": "Iniciar chat", "Start_of_conversation": "Inicio de la conversación", @@ -4515,7 +4512,6 @@ "Verified": "Verificado", "Verify": "Verificar", "Verify_your_email": "Verifica tu correo electrónico", - "Verify_your_email_with_the_code_we_sent": "Consulta tu correo electrónico para encontrar el código que hemos enviado", "Version": "Versión", "Version_version": "Versión {{version}}", "Video_Chat_Window": "Videochat", @@ -4912,8 +4908,10 @@ "subscription.callout.servicesDisruptionsOccurring": "Se están produciendo interrupciones en los servicios.", "subscription.callout.capabilitiesDisabled": "Características desactivadas", "subscription.callout.description.limitsExceeded_one": "Su espacio de trabajo ha superado el límite de <1> {{val}} . <3> Administre su suscripción para incrementar los límites.", + "subscription.callout.description.limitsExceeded_many": "Su espacio de trabajo ha superado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "subscription.callout.description.limitsExceeded_other": "Su espacio de trabajo ha superado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "subscription.callout.description.limitsReached_one": "Su espacio de trabajo ha alcanzado el límite <1> {{val}} . <3> Administre su suscripción para incrementar los límites.", + "subscription.callout.description.limitsReached_many": "Su espacio de trabajo ha alcanzado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "subscription.callout.description.limitsReached_other": "Su espacio de trabajo ha alcanzado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "subscription.callout.allPremiumCapabilitiesDisabled": "Todas las funciones premium desactivadas", "subscription.callout.activeUsers": "puestos", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json index f5f5e9be472b..a4dfa77e34f9 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json @@ -2148,7 +2148,6 @@ "Open_Days": "روز های فعال", "Open_days_of_the_week": "روزهای هفته باز شود", "Open_Livechats": "'گفت و گوی زنده باز", - "Open_your_authentication_app_and_enter_the_code": "برنامه تأیید اعتبار خود را باز کنید و کد را وارد کنید. شما همچنین می توانید یکی از کدهای پشتیبان خود را استفاده کنید.", "Opened": "باز", "Opened_in_a_new_window": "باز در یک پنجره جدید", "Opens_a_channel_group_or_direct_message": "کانال، گروه یا پیام مستقیم را باز می کند", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json index fbcc6cb3d7f4..2746e382a91f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json @@ -3657,7 +3657,6 @@ "Open_settings": "Avaa asetukset", "Open-source_conference_call_solution": "Avoimen lähdekoodin neuvottelupuheluratkaisu.", "Open_thread": "Avaa säie", - "Open_your_authentication_app_and_enter_the_code": "Avaa todennussovellus ja anna koodi. Voit myös käyttää varakoodia.", "Opened": "Avattu", "Opened_in_a_new_window": "Avattu uudessa ikkunassa.", "Opens_a_channel_group_or_direct_message": "Avaa kanavan, ryhmän tai suoran viestin", @@ -5179,7 +5178,6 @@ "Verified": "Vahvistettu", "Verify": "Vahvista", "Verify_your_email": "Vahvista sähköpostiosoitteesi", - "Verify_your_email_with_the_code_we_sent": "Tarkista lähettämämme koodi sähköpostissasi", "Version": "Versio", "Version_version": "Versio {{version}}", "App_Request_Admin_Message": "Hei {{admin_name}}, {{user_name}} lähetti pyynnön asentaa {{app_name}} tähän työtilaan. \n \n Tämä on hänen viestinsä: \n>{{message}} \n \n Katso lisätietoja ja asenna {{app_name}} [napsauttamalla tätä]({{learn_more}}).", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json index 9e16888c5502..e60c6127d655 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json @@ -3199,7 +3199,6 @@ "Open_Livechats": "Chats en cours", "Open_menu": "Ouvrir_menu", "Open_thread": "Ouvrir le fil", - "Open_your_authentication_app_and_enter_the_code": "Ouvrez votre application d'authentification et entrez le code. Vous pouvez également utiliser l'un de vos codes de sauvegarde.", "Opened": "Ouvert", "Opened_in_a_new_window": "Ouvert dans une nouvelle fenêtre.", "Opens_a_channel_group_or_direct_message": "Ouvre un canal, un groupe ou un message direct", @@ -4530,7 +4529,6 @@ "Verified": "Vérifié", "Verify": "Vérifier", "Verify_your_email": "Vérifier votre e-mail", - "Verify_your_email_with_the_code_we_sent": "Vérifiez si vous avez reçu le code que nous avons envoyé par e-mail", "Version": "Version", "Version_version": "Version {{version}}", "Video_Chat_Window": "Chat vidéo", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json index 41f1d1c5ef1a..240f86dbe9b0 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json @@ -1978,7 +1978,6 @@ "Open_channel_user_search": "`%s` - Otvori kanal / Pretraživanje korisnika", "Open_days_of_the_week": "Otvoreni Dani u Tjednu", "Open_Livechats": "Otvori Livechat", - "Open_your_authentication_app_and_enter_the_code": "Otvorite aplikaciju za provjeru autentičnosti i unesite kôd. Možete upotrijebiti i jedan od vaših pričuvnih kodova.", "Opened": "Otvoreno", "Opened_in_a_new_window": "Otvoren u novom prozoru.", "Opens_a_channel_group_or_direct_message": "Otvori sobu, grupu ili direktnu poruku", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json index b03d85857fa3..5a756161ca6e 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json @@ -3518,7 +3518,6 @@ "Open_settings": "Beállítások megnyitása", "Open-source_conference_call_solution": "Nyílt forráskódú konferenciahívási megoldás.", "Open_thread": "Szál megnyitása", - "Open_your_authentication_app_and_enter_the_code": "Nyissa meg a hitelesítő alkalmazását, és adja meg a kódot. Használhatja a biztonsági tartalékkódok egyikét is.", "Opened": "Megnyitva", "Opened_in_a_new_window": "Megnyitva egy új ablakban.", "Opens_a_channel_group_or_direct_message": "Megnyit egy csatornát, csoportot vagy közvetlen üzenetet", @@ -4984,7 +4983,6 @@ "Verified": "Ellenőrizve", "Verify": "Ellenőrzés", "Verify_your_email": "Ellenőrizze az e-mail-címét", - "Verify_your_email_with_the_code_we_sent": "Ellenőrizze az e-mail-címét azzal a kóddal, amit küldtünk", "Version": "Verzió", "Version_version": "Verzió: {{version}}", "App_version_incompatible_tooltip": "Az alkalmazás nem kompatibilis a Rocket.Chat verziójával", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json index 56d527dbb0f6..ba4c9e1605cf 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json @@ -1852,7 +1852,6 @@ "Open_channel_user_search": "`%s` - Pencarian Open Channel / User", "Open_days_of_the_week": "Open Days of the Week", "Open_Livechats": "Buka Livechats", - "Open_your_authentication_app_and_enter_the_code": "Buka aplikasi autentikasi Anda dan masukkan kode. Anda juga dapat menggunakan salah satu kode cadangan Anda.", "Opened": "dibuka", "Opened_in_a_new_window": "Dibuka di jendela baru.", "Opens_a_channel_group_or_direct_message": "Membuka saluran, grup atau pesan langsung", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json index d041b9c099fa..fa7b76d6c53d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json @@ -1917,7 +1917,6 @@ "Open_channel_user_search": "`%s` - Ricerca canale / utente aperto", "Open_days_of_the_week": "Giorni di apertura", "Open_Livechats": "Apri Livechat", - "Open_your_authentication_app_and_enter_the_code": "Apri la tua app di autenticazione e inserisci il codice. Puoi anche utilizzare uno dei tuoi codici di backup.", "Opened": "Ha aperto", "Opened_in_a_new_window": "Apri in una nuova finestra", "Opens_a_channel_group_or_direct_message": "Apre un canale, canale privato o messaggi diretto", @@ -2715,7 +2714,6 @@ "Verified": "Verificata", "Verify": "✓ Verifica", "Verify_your_email": "Verifica il tuo indirizzo email", - "Verify_your_email_with_the_code_we_sent": "Controlla l'e-mail con il codice che ti abbiamo inviato", "Version": "Versione", "Video_Chat_Window": "Chat Video", "Video_Conference": "Video conferenza", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json index b5a67a7cc13b..bdf03e52249f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json @@ -3167,7 +3167,6 @@ "Open_Livechats": "ライブチャットを使用中", "Open_menu": "Open_menu", "Open_thread": "スレッドを開く", - "Open_your_authentication_app_and_enter_the_code": "認証アプリを開き、コードを入力します。いずれかのバックアップコードを使用することもできます。", "Opened": "開いている", "Opened_in_a_new_window": "新しいウィンドウで開きます。", "Opens_a_channel_group_or_direct_message": "チャネル、グループ、またはダイレクトメッセージを開きます", @@ -4478,7 +4477,6 @@ "Verified": "確認済み", "Verify": "確認", "Verify_your_email": "あなたのメールを確認します", - "Verify_your_email_with_the_code_we_sent": "送信したコードのメールを確認します", "Version": "バージョン", "Version_version": "バージョン{{version}}", "Video_Chat_Window": "ビデオチャット", 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 5b4b6ae47ef1..0da6ec549547 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json @@ -2519,7 +2519,6 @@ "Open": "გახსნა", "Open_channel_user_search": "`%s` - გახსენით არხი / მომხმარებლის ძებნა", "Open_Livechats": "ჩეთები მიმდინარეობს", - "Open_your_authentication_app_and_enter_the_code": "გახსენით თქვენი ავთენტიფიკაციის პროგრამა და შეიყვანეთ კოდი. თქვენ ასევე შეგიძლიათ გამოიყენოთ ერთი სარეზერვო კოდი.", "Opened": "გაიხსნა", "Opened_in_a_new_window": "გახსნა ახალ ფანჯარაში.", "Opens_a_channel_group_or_direct_message": "ხსნის არხს, ჯგუფს ან პირდაპირ შეტყობინებას", @@ -3491,7 +3490,6 @@ "Verified": "გადამოწმებულია", "Verify": "დამოწმება", "Verify_your_email": "დაამოწმეთ თქვენი ელ. ფოსტა", - "Verify_your_email_with_the_code_we_sent": "გადაამოწმეთ თქვენი ელ.ფოსტა ჩვენს მიერ გაგზავნილი კოდისთვის", "Version": "ვერსია", "Version_version": "ვერსია {{version}}", "Video_Chat_Window": "ვიდეო ჩატი", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json index 8d50ef2e44fd..8c14713fefad 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json @@ -2153,7 +2153,6 @@ "Open_conversations": "បើកការសន្ទនា", "Open_days_of_the_week": "បើកថ្ងៃនៃសប្តាហ៍", "Open_Livechats": "បើក Livechats", - "Open_your_authentication_app_and_enter_the_code": "បើកកម្មវិធីផ្ទៀងផ្ទាត់របស់អ្នកហើយបញ្ចូលកូដ។ អ្នកក៏អាចប្រើលេខកូដបម្រុងទុកមួយរបស់អ្នកផងដែរ។", "Opened": "បានបើក", "Opened_in_a_new_window": "បើកក្នុងបង្អួចថ្មី។", "Opens_a_channel_group_or_direct_message": "បើកឆានែលក្រុមឬសារដោយផ្ទាល់", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json index dab99bb28f1e..9a49d6c359df 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json @@ -2732,7 +2732,6 @@ "Open_Days": "영업일", "Open_days_of_the_week": "Livechat 열어두는 날", "Open_Livechats": "진행중인 대화", - "Open_your_authentication_app_and_enter_the_code": "인증 앱을 열고 코드를 입력하십시오. 백업 코드 중 하나를 사용할 수도 있습니다.", "Opened": "개설됨", "Opened_in_a_new_window": "새 창에서 열렸습니다.", "Opens_a_channel_group_or_direct_message": "대화방 또는 1:1 대화방을 엽니다.", @@ -3819,7 +3818,6 @@ "Verified": "검증", "Verify": "검증하기", "Verify_your_email": "이메일 검증", - "Verify_your_email_with_the_code_we_sent": "우리가 보낸 코드에 대한 이메일을 확인하십시오.", "Version": "버전", "Version_version": "{{version}} 버전", "Video_Chat_Window": "화상 채팅", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json index f0512de9c5a1..4c012c61e0d7 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json @@ -1840,7 +1840,6 @@ "Open_channel_user_search": "`%s` - Hilbijartina Open Channel / Bikarhêner", "Open_days_of_the_week": "Rojên Dîroka Veja", "Open_Livechats": "Open Livechats Open", - "Open_your_authentication_app_and_enter_the_code": "Vebijêrkek te ya pejirandinê vekin û kodê binivîse. Hûn dikarin ji kodên bilez bikar bînin.", "Opened": "vekirin,", "Opened_in_a_new_window": "Di paceya nû de vekirî ye.", "Opens_a_channel_group_or_direct_message": "Peyamek, grûp an peyamê yekser veke", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json index 09e0f2e7a6de..7b9409f9e83b 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json @@ -1882,7 +1882,6 @@ "Open_channel_user_search": "`%s` - ການຊອກຫາຊ່ອງທາງ / ການຊອກຫາຜູ້ໃຊ້", "Open_days_of_the_week": "ເປີດວັນຂອງອາທິດ", "Open_Livechats": "ເປີດ Livechats", - "Open_your_authentication_app_and_enter_the_code": "ເປີດ app authentication ຂອງທ່ານແລະເຂົ້າລະຫັດ. ທ່ານຍັງສາມາດໃຊ້ຫນຶ່ງໃນລະຫັດສໍາຮອງຂອງທ່ານໄດ້.", "Opened": "ເປີດ", "Opened_in_a_new_window": "ເປີດຢູ່ໃນປ່ອງຢ້ຽມໃຫມ່.", "Opens_a_channel_group_or_direct_message": "ເປີດຊ່ອງທາງ, ກຸ່ມຫຼືຂໍ້ຄວາມໂດຍກົງ", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json index 764fa95787a5..19457e9cce7c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json @@ -1900,7 +1900,6 @@ "Open_channel_user_search": "`%s` - atidaryta kanalo / vartotojo paieška", "Open_days_of_the_week": "Atvirų savaitės dienos", "Open_Livechats": "Atidarykite \"Livechats\"", - "Open_your_authentication_app_and_enter_the_code": "Atidarykite savo autentifikavimo programą ir įveskite kodą. Taip pat galite naudoti vieną iš savo atsarginių kodų.", "Opened": "Atidaryta", "Opened_in_a_new_window": "Atidaryta naujame lange.", "Opens_a_channel_group_or_direct_message": "Atidaro kanalą, grupę ar tiesioginį pranešimą", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json index 08277dd8c44f..2d5d684a027f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json @@ -1858,7 +1858,6 @@ "Open_channel_user_search": "`%s` - atveriet kanālu / lietotāja meklēšanu", "Open_days_of_the_week": "Atvērtās nedēļas dienas", "Open_Livechats": "Atvērt Livechats", - "Open_your_authentication_app_and_enter_the_code": "Atveriet savu autentifikācijas lietotni un ievadiet kodu. Varat arī izmantot vienu no saviem rezerves kodiem.", "Opened": "Atvērts", "Opened_in_a_new_window": "Atvērts jaunā logā.", "Opens_a_channel_group_or_direct_message": "Atver kanālu, grupu vai ziņojumu", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json index 76bcf7616855..b58eace8ac25 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json @@ -1840,7 +1840,6 @@ "Open_channel_user_search": "`%s` - Нээлттэй суваг / Хэрэглэгчийн хайлт", "Open_days_of_the_week": "Долоо хоногийн нээлттэй өдөр", "Open_Livechats": "Нээлттэй Livechats", - "Open_your_authentication_app_and_enter_the_code": "Өөрийн нэвтрэлтийн програмыг нээгээд кодыг оруулна уу. Та мөн өөрийн нөөц кодуудын аль нэгийг ашиглаж болно.", "Opened": "Нээгдсэн", "Opened_in_a_new_window": "Шинэ цонхонд нээгдсэн.", "Opens_a_channel_group_or_direct_message": "Суваг, бүлгийн эсвэл шууд зурвасыг нээх", 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 00245940ea42..1ef28a41a280 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json @@ -1851,7 +1851,6 @@ "Open_channel_user_search": "`%s` - Buka Saluran / Carian pengguna", "Open_days_of_the_week": "Hari Terbuka Seminggu", "Open_Livechats": "Buka Livechats", - "Open_your_authentication_app_and_enter_the_code": "Buka apl pengesahan anda dan masukkan kod itu. Anda juga boleh menggunakan salah satu kod sandaran anda.", "Opened": "dibuka", "Opened_in_a_new_window": "Dibuka dalam tetingkap baru.", "Opens_a_channel_group_or_direct_message": "Membuka saluran, kumpulan atau mesej langsung", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json index daf3e2aae7f2..e7e78719fd12 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json @@ -3189,7 +3189,6 @@ "Open_Livechats": "Chats in uitvoering", "Open_menu": "Open_menu", "Open_thread": "Open draad", - "Open_your_authentication_app_and_enter_the_code": "Open uw authenticatie-app en voer de code in. U kunt ook een van uw back-upcodes gebruiken.", "Opened": "Geopend", "Opened_in_a_new_window": "Geopend in een nieuw venster.", "Opens_a_channel_group_or_direct_message": "Opent een kanaal, groep of privébericht", @@ -4517,7 +4516,6 @@ "Verified": "Geverifieerd", "Verify": "Verifiëren", "Verify_your_email": "Controleer je e-mailadres", - "Verify_your_email_with_the_code_we_sent": "Verifieer uw e-mail voor de code die we hebben verzonden", "Version": "Versie", "Version_version": "Versie {{version}}", "Video_Chat_Window": "Videochat", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json index 1bdb45345c25..89e3510be974 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json @@ -3123,7 +3123,6 @@ "Open_Outlook": "Åpne Outlook", "Open_settings": "Åpne innstillinger", "Open_thread": "Åpne tråd", - "Open_your_authentication_app_and_enter_the_code": "Åpne autentiseringsprogrammet ditt og skriv inn koden. Du kan også bruke en av sikkerhetskodene dine.", "Opened": "åpnet", "Opened_in_a_new_window": "Åpnet i nytt vindu.", "Opens_a_channel_group_or_direct_message": "Åpner en kanal, gruppe eller direkte melding", @@ -4296,7 +4295,6 @@ "Verified": "Verified", "Verify": "Bekreft", "Verify_your_email": "Bekreft e-posten din", - "Verify_your_email_with_the_code_we_sent": "Bekreft e-posten din med koden vi har sendt", "Version": "Versjon", "Version_version": "Versjon {{version}}", "Video_Chat_Window": "Video Chat", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json index 80c503041a17..1e33996fb5df 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json @@ -3464,7 +3464,6 @@ "Open_menu": "Open_menu", "Open_settings": "Otwórz ustawienia", "Open_thread": "Otwórz wątek", - "Open_your_authentication_app_and_enter_the_code": "Otwórz aplikację uwierzytelniającą i wprowadź kod. Możesz również użyć jednego z kodów zapasowych.", "Opened": "Otworzony", "Opened_in_a_new_window": "Otwarty w nowym oknie.", "Opens_a_channel_group_or_direct_message": "Otwiera kanał, grupę lub wiadomość bezpośrednią", @@ -4916,7 +4915,6 @@ "Verified": "Zweryfikowany", "Verify": "Zweryfikować", "Verify_your_email": "Zweryfikuj swój email", - "Verify_your_email_with_the_code_we_sent": "Zweryfikuj swój e-mail pod kątem wysłanego przez nas kodu", "Version": "Wersja", "Version_version": "Wersja {{version}}", "Video_Conference_Description": "Skonfiguruj połączenia konferencyjne dla swojej przestrzeni roboczej.", 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 08b85291398a..22ca1bc28ffd 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -3264,7 +3264,6 @@ "Open_Livechats": "Conversas em andamento", "Open_menu": "Abrir menu", "Open_thread": "Conversa aberta", - "Open_your_authentication_app_and_enter_the_code": "Abra seu aplicativo de autenticação e insira o código. Você também pode usar um de seus códigos de backup.", "Opened": "Aberto", "Opened_in_a_new_window": "Aberto em nova janela.", "Opens_a_channel_group_or_direct_message": "Abre um canal, grupo ou mensagem direta", @@ -4616,7 +4615,6 @@ "Verified": "Verificado", "Verify": "Verificar", "Verify_your_email": "Verifique seu e-mail", - "Verify_your_email_with_the_code_we_sent": "Verifique o seu e-mail com o código que enviamos", "Version": "Versão", "Version_version": "Versão {{version}}", "Video_Chat_Window": "Vídeochat", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json index aabf52554792..1b932ba51cb1 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json @@ -2149,7 +2149,6 @@ "Open_conversations": "Conversas Abertas", "Open_days_of_the_week": "Dias abertos da semana", "Open_Livechats": "Aberto o Livechats", - "Open_your_authentication_app_and_enter_the_code": "Abra o seu aplicativo de autenticação e insira o código. Pode também, usar um dos seus códigos de backup.", "Opened": "Aberto", "Opened_in_a_new_window": "Aberto em nova janela.", "Opens_a_channel_group_or_direct_message": "Abre um canal, grupo ou mensagem directa", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json index 8bbe25f79d04..2593f37615be 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json @@ -1844,7 +1844,6 @@ "Open_channel_user_search": "`%s` - Open Channel / Căutare utilizator", "Open_days_of_the_week": "Zilele deschise ale săptămânii", "Open_Livechats": "Deschideți Livechats", - "Open_your_authentication_app_and_enter_the_code": "Deschideți aplicația de autentificare și introduceți codul. De asemenea, puteți utiliza unul dintre codurile de rezervă.", "Opened": "Deschis", "Opened_in_a_new_window": "Deschis într-o fereastră nouă.", "Opens_a_channel_group_or_direct_message": "Deschide un canal, un grup sau un mesaj direct", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json index 0b2fe35714b1..daf957d03060 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json @@ -3361,7 +3361,6 @@ "Open_Livechats": "Открыть Livechat", "Open_menu": "Open_menu", "Open_thread": "Открыть тред", - "Open_your_authentication_app_and_enter_the_code": "Откройте ваше приложение аутентификации и введите код. Вы также можете использовать один из кодов восстановления (backup codes).", "Opened": "Открыто", "Opened_in_a_new_window": "Открыть в новом окне", "Opens_a_channel_group_or_direct_message": "Открывает канал, группу или личную переписку", @@ -4708,7 +4707,6 @@ "Verified": "Подтверждён", "Verify": "Подтверждение", "Verify_your_email": "Подтвердите Ваш электронный адрес", - "Verify_your_email_with_the_code_we_sent": "Проверьте свою электронную почту на наличие кода, который мы отправили", "Version": "Версия", "Version_version": "Версия {{version}}", "Video_Conference_Description": "Настройте звонки для вашего сервера.", 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 83f2dead338d..f71652a80321 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json @@ -1854,7 +1854,6 @@ "Open_channel_user_search": "`%s` - Otvorený kanál / Užívateľské vyhľadávanie", "Open_days_of_the_week": "Otvorené dni týždňa", "Open_Livechats": "Otvorte Livechat", - "Open_your_authentication_app_and_enter_the_code": "Otvorte svoju autentizačnú aplikáciu a zadajte kód. Môžete tiež použiť jeden z vašich záložných kódov.", "Opened": "otvoril", "Opened_in_a_new_window": "Otvorí sa v novom okne.", "Opens_a_channel_group_or_direct_message": "Otvorí kanál, skupinu alebo priamu správu", 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 6fe22e641474..e604aacaf83d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json @@ -1834,7 +1834,6 @@ "Open_channel_user_search": "`%s` - Odpri Kanal / Iskanje uporabnikov", "Open_days_of_the_week": "Odpri dnevi v tednu", "Open_Livechats": "Odpri klepete v živo", - "Open_your_authentication_app_and_enter_the_code": "Odprite aplikacijo za preverjanje pristnosti in vnesite kodo. Uporabite lahko tudi eno od vaših nadomestnih kod.", "Opened": "Odprto", "Opened_in_a_new_window": "Odprto v novem oknu.", "Opens_a_channel_group_or_direct_message": "Odpre kanal, skupino ali direktno sporočilo", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json index 0b6f0817bff8..e00b6e61ed54 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json @@ -1844,7 +1844,6 @@ "Open_channel_user_search": "`%s` - Kërkimi i hapur i kanalit / përdoruesit", "Open_days_of_the_week": "Ditët e Hapura të Javës", "Open_Livechats": "Open Livechats", - "Open_your_authentication_app_and_enter_the_code": "Hapni aplikacionin e legalizimit dhe futni kodin. Gjithashtu mund të përdorni një nga kodet rezervë.", "Opened": "Hapur", "Opened_in_a_new_window": "U hap në një dritare të re.", "Opens_a_channel_group_or_direct_message": "Hap një kanal, grup ose mesazh të drejtpërdrejtë", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json index 55f30cb75c6e..983a5a8668d4 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json @@ -1683,7 +1683,6 @@ "Open_channel_user_search": "`%s` - Отворени канал / Тражење корисника", "Open_days_of_the_week": "Отворени дани недеље", "Open_Livechats": "Отвори Ливецхатс", - "Open_your_authentication_app_and_enter_the_code": "Отворите апликацију за потврду идентитета и унесите га. Такође можете да користите један од ваших резервних кодова.", "Opened": "Отворен", "Opened_in_a_new_window": "Отвара се у новом прозору.", "Opens_a_channel_group_or_direct_message": "Отвара канал, групу или директну поруку", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json index 75e03aa72f77..03e5fe291668 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json @@ -3662,7 +3662,6 @@ "Open_settings": "Öppna inställningarna", "Open-source_conference_call_solution": "Lösning för konferenssamtal med öppen källkod.", "Open_thread": "Öppna tråden", - "Open_your_authentication_app_and_enter_the_code": "Öppna din autentiseringsapp och ange koden. Du kan också använda en av dina säkerhetskoder.", "Opened": "Öppnad", "Opened_in_a_new_window": "Öppnas i ett nytt fönster.", "Opens_a_channel_group_or_direct_message": "Öppnar ett kanal-, grupp- eller direktmeddelande", @@ -5181,7 +5180,6 @@ "Verified": "Verifierad", "Verify": "Verifiera", "Verify_your_email": "Verifiera din e-post", - "Verify_your_email_with_the_code_we_sent": "Titta i e-posten efter koden vi skickat", "Version": "Version", "Version_version": "Version {{version}}", "App_Request_Admin_Message": "Hej {{admin_name}}, {{user_name}} skickade en förfrågan om att installera appen {{app_name}} på den här arbetsytan. \n \n Detta meddelande skickades med i samband med förfrågan: \n>{{message}} \n \n Om du vill veta mer och installera appen {{app_name}}, [klicka här]({{learn_more}}).", 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 4b5ab7ac74c0..7702bf7f2dcd 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json @@ -1845,7 +1845,6 @@ "Open_channel_user_search": "`%s` - திறந்த சேனல் / பயனர் தேடல்", "Open_days_of_the_week": "வாரம் திறந்த நாட்கள்", "Open_Livechats": "Livechats ஐ திறக்கவும்", - "Open_your_authentication_app_and_enter_the_code": "உங்கள் அங்கீகார பயன்பாட்டைத் திறந்து குறியீடு உள்ளிடவும். உங்கள் காப்பு பிரதி குறியீடுகள் ஒன்றையும் நீங்கள் பயன்படுத்தலாம்.", "Opened": "திறக்கப்பட்ட", "Opened_in_a_new_window": "புதிய சாளரத்தில் திறக்கப்பட்டது.", "Opens_a_channel_group_or_direct_message": "ஒரு சேனல், குழு அல்லது நேரடி செய்தியைத் திறக்கும்", 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 e520bf899109..5707ac5c4ea4 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json @@ -1838,7 +1838,6 @@ "Open_channel_user_search": "`%s` - เปิดการค้นหาช่อง / ผู้ใช้", "Open_days_of_the_week": "เปิดวันอาทิตย์", "Open_Livechats": "เปิดไลฟ์แชท", - "Open_your_authentication_app_and_enter_the_code": "เปิดแอปพลิเคชันการตรวจสอบสิทธิ์และป้อนรหัส คุณยังสามารถใช้รหัสสำรองของคุณได้อีกด้วย", "Opened": "เปิด", "Opened_in_a_new_window": "เปิดในหน้าต่างใหม่", "Opens_a_channel_group_or_direct_message": "เปิดช่องทางกลุ่มหรือข้อความโดยตรง", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json index c81016871d75..b933b54e8dee 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json @@ -2205,7 +2205,6 @@ "Open_conversations": "Açık Konuşmalar", "Open_days_of_the_week": "Haftanın Günleri", "Open_Livechats": "Canlı Görüşmeleri Aç", - "Open_your_authentication_app_and_enter_the_code": "Kimlik doğrulama uygulamasını açın ve kodu girin. Yedek kodlarınızdan birini de kullanabilirsiniz.", "Opened": "Açıldı", "Opened_in_a_new_window": "Yeni bir pencerede açıldı.", "Opens_a_channel_group_or_direct_message": "Bir kanal, grup veya doğrudan ileti açar", @@ -3097,7 +3096,6 @@ "Verified": "Doğrulanmış", "Verify": "Doğrula", "Verify_your_email": "E-postanızı doğrulayın", - "Verify_your_email_with_the_code_we_sent": "Gönderdiğimiz kod için e-postanızı doğrulayın", "Version": "Sürüm", "Video_Chat_Window": "Görüntülü Görüşme", "Video_Conference": "Görüntülü Görüşme", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json index f30d69e0cf8c..ca6bb2ecb72a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json @@ -2365,7 +2365,6 @@ "Open_channel_user_search": "`%s` - відкритий пошук каналів / користувачів", "Open_days_of_the_week": "Відкриті дні тижня", "Open_Livechats": "Відкрийте Livechats", - "Open_your_authentication_app_and_enter_the_code": "Відкрийте програму автентифікації та введіть код. Ви також можете використовувати один з ваших резервних кодів.", "Opened": "відкритий", "Opened_in_a_new_window": "Відкрито в новому вікні.", "Opens_a_channel_group_or_direct_message": "Відкриває канал, групове або пряме повідомлення", 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 4b9936642086..bea35acf1daa 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json @@ -1944,7 +1944,6 @@ "Open_channel_user_search": "`%s` - Mở kênh / Tìm kiếm người dùng", "Open_days_of_the_week": "Mở các ngày trong tuần", "Open_Livechats": "Mở Livechat", - "Open_your_authentication_app_and_enter_the_code": "Mở ứng dụng xác thực của bạn và nhập mã. Bạn cũng có thể sử dụng một trong các mã dự phòng của mình.", "Opened": "Đã mở", "Opened_in_a_new_window": "Mở trong một cửa sổ mới.", "Opens_a_channel_group_or_direct_message": "Mở kênh, nhóm hoặc tin nhắn trực tiếp", 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 d3a3295c5956..f6515407896d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json @@ -1869,7 +1869,6 @@ "Open_channel_user_search": "`%s` - 打开频道/用户搜索", "Open_days_of_the_week": "开放每周的日子", "Open_Livechats": "打开Livechats", - "Open_your_authentication_app_and_enter_the_code": "打开您的验证应用程序并输入代码。您也可以使用其中一个备份代码。", "Opened": "开业", "Opened_in_a_new_window": "在新窗口中打开。", "Opens_a_channel_group_or_direct_message": "打开频道,群组或直接信息", 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 29b4d7522504..a3d0b0e9cc7c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json @@ -3102,7 +3102,6 @@ "Open_days_of_the_week": "開放每週的日子", "Open_Livechats": "打開即時聊天", "Open_menu": "Open_menu", - "Open_your_authentication_app_and_enter_the_code": "打開您的驗證應用程式並輸入代碼。您也可以使用其中一個備用代碼。", "Opened": "已開啟", "Opened_in_a_new_window": "在新視窗中打開。", "Opens_a_channel_group_or_direct_message": "打開頻道,群組或直接訊息", @@ -4273,7 +4272,6 @@ "Verified": "已驗證", "Verify": "驗證", "Verify_your_email": "驗證您的電子郵件", - "Verify_your_email_with_the_code_we_sent": "檢查您的電子郵件以取得我們發送的代碼", "Version": "版本", "Version_version": "版本 {{version}}", "Video_Chat_Window": "視訊聊天", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json index e14fd08dd8d5..c860b2b37ef3 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json @@ -2793,7 +2793,6 @@ "Open_days_of_the_week": "每周开放日", "Open_Livechats": "正在进行的聊天", "Open_thread": "开启讨论串", - "Open_your_authentication_app_and_enter_the_code": "打开您的验证应用程序并输入代码。您也可以使用其中一个备份代码。", "Opened": "已开启", "Opened_in_a_new_window": "在新窗口中打开。", "Opens_a_channel_group_or_direct_message": "打开频道、组或私聊", @@ -3919,7 +3918,6 @@ "Verified": "已验证", "Verify": "验证", "Verify_your_email": "验证您的电子邮件", - "Verify_your_email_with_the_code_we_sent": "检查您的邮箱以获得我们发送的验证码", "Version": "版本", "Version_version": "版本 {{version}}", "Video_Chat_Window": "视频聊天", From 26c7418684ffec62ae7f41230a7273a20cd98fea Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 6 Feb 2024 22:42:31 -0300 Subject: [PATCH 019/207] chore: bump version to 6.7.0-develop --- apps/meteor/app/utils/rocketchat.info | 2 +- apps/meteor/package.json | 2 +- package.json | 2 +- packages/core-typings/package.json | 2 +- packages/gazzodown/package.json | 2 +- packages/rest-typings/package.json | 2 +- yarn.lock | 22 +++++++++++----------- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/meteor/app/utils/rocketchat.info b/apps/meteor/app/utils/rocketchat.info index 378fba14ca44..f2031f21f05d 100644 --- a/apps/meteor/app/utils/rocketchat.info +++ b/apps/meteor/app/utils/rocketchat.info @@ -1,3 +1,3 @@ { - "version": "6.6.0" + "version": "6.7.0-develop" } diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 7b85fef37fdf..e637783c4c8e 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.6.0", + "version": "6.7.0-develop", "private": true, "author": { "name": "Rocket.Chat", diff --git a/package.json b/package.json index 6f404b833de3..47e67f6b7ec3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rocket.chat", - "version": "6.6.0", + "version": "6.7.0-develop", "description": "Rocket.Chat Monorepo", "main": "index.js", "private": true, diff --git a/packages/core-typings/package.json b/packages/core-typings/package.json index 99c803f7e57d..462a4bb66eda 100644 --- a/packages/core-typings/package.json +++ b/packages/core-typings/package.json @@ -2,7 +2,7 @@ "$schema": "https://json.schemastore.org/package", "name": "@rocket.chat/core-typings", "private": true, - "version": "6.6.0", + "version": "6.7.0-develop", "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", "eslint": "~8.45.0", diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index 38f757a07e32..8ba6c1202395 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -65,7 +65,7 @@ "/dist" ], "peerDependencies": { - "@rocket.chat/core-typings": "6.6.0", + "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/css-in-js": "*", "@rocket.chat/fuselage": "*", "@rocket.chat/fuselage-tokens": "*", diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index b085dd7b4772..86c41231d618 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/rest-typings", "private": true, - "version": "6.6.0", + "version": "6.7.0-develop", "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", "@types/jest": "~29.5.7", diff --git a/yarn.lock b/yarn.lock index 6d568be3e647..5215936a1c3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9341,16 +9341,16 @@ __metadata: typescript: ~5.3.2 peerDependencies: "@rocket.chat/apps-engine": "*" - "@rocket.chat/eslint-config": 0.6.1-rc.0 + "@rocket.chat/eslint-config": 0.6.1 "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/fuselage-polyfills": "*" "@rocket.chat/icons": "*" "@rocket.chat/prettier-config": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-contexts": 4.0.0-rc.4 - "@rocket.chat/ui-kit": 0.33.0-rc.0 - "@rocket.chat/ui-video-conf": 4.0.0-rc.4 + "@rocket.chat/ui-contexts": 4.0.0 + "@rocket.chat/ui-kit": 0.33.0 + "@rocket.chat/ui-video-conf": 4.0.0 "@tanstack/react-query": "*" react: "*" react-dom: "*" @@ -9432,14 +9432,14 @@ __metadata: ts-jest: ~29.1.1 typescript: ~5.3.2 peerDependencies: - "@rocket.chat/core-typings": 6.6.0-rc.4 + "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/css-in-js": "*" "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-tokens": "*" "@rocket.chat/message-parser": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-client": 4.0.0-rc.4 - "@rocket.chat/ui-contexts": 4.0.0-rc.4 + "@rocket.chat/ui-client": 4.0.0 + "@rocket.chat/ui-contexts": 4.0.0 katex: "*" react: "*" languageName: unknown @@ -10621,7 +10621,7 @@ __metadata: "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/icons": "*" - "@rocket.chat/ui-contexts": 4.0.0-rc.4 + "@rocket.chat/ui-contexts": 4.0.0 react: ~17.0.2 languageName: unknown linkType: soft @@ -10796,7 +10796,7 @@ __metadata: "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/icons": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-contexts": 4.0.0-rc.4 + "@rocket.chat/ui-contexts": 4.0.0 react: ^17.0.2 react-dom: ^17.0.2 languageName: unknown @@ -10884,8 +10884,8 @@ __metadata: typescript: ~5.3.2 peerDependencies: "@rocket.chat/layout": "*" - "@rocket.chat/tools": 0.2.1-rc.0 - "@rocket.chat/ui-contexts": 4.0.0-rc.4 + "@rocket.chat/tools": 0.2.1 + "@rocket.chat/ui-contexts": 4.0.0 "@tanstack/react-query": "*" react: "*" react-hook-form: "*" From dbaf87fbcfda7253367899b4f342e0783d1157fb Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 6 Feb 2024 23:12:47 -0300 Subject: [PATCH 020/207] i18n: remove exceeding keys --- apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json index 89e3510be974..cd612458dec7 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json @@ -2905,7 +2905,6 @@ "Moderation_User_deactivated": "Bruker deaktivert", "Moderation_Delete_all_messages": "Slett alle meldinger", "Moderation_Duplicate_messages": "Dupliserte meldinger", - "Moderation_Report_reports": "Rapporter", "Moderation_Reported_message": "Rapportert melding", "Moderation_Message_already_deleted": "Meldingen er allerede slettet", "Moderation_Reset_user_avatar": "Tilbakestill brukeravatar", @@ -4562,4 +4561,4 @@ "free_per_month_user": "$0 per måned per bruker", "UpgradeToGetMore_engagement-dashboard_Title": "Analytics", "Buy_more": "Kjøp mer" -} \ No newline at end of file +} From 789544fa2f25f91316a00a76787bd66a780dfae3 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Wed, 7 Feb 2024 10:57:23 -0300 Subject: [PATCH 021/207] fix: Allow only numbers, if trigger's condition is 'visitor time on site' (#31666) --- .changeset/moody-cups-search.md | 5 +++++ .../client/views/omnichannel/triggers/EditTrigger.tsx | 9 ++++++++- .../tests/e2e/page-objects/omnichannel-triggers.ts | 6 +++--- 3 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 .changeset/moody-cups-search.md diff --git a/.changeset/moody-cups-search.md b/.changeset/moody-cups-search.md new file mode 100644 index 000000000000..a4846da652a4 --- /dev/null +++ b/.changeset/moody-cups-search.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes an issue allowing only numbers, if trigger's condition is 'visitor time on site' diff --git a/apps/meteor/client/views/omnichannel/triggers/EditTrigger.tsx b/apps/meteor/client/views/omnichannel/triggers/EditTrigger.tsx index 0739641535b0..58cf47058286 100644 --- a/apps/meteor/client/views/omnichannel/triggers/EditTrigger.tsx +++ b/apps/meteor/client/views/omnichannel/triggers/EditTrigger.tsx @@ -12,6 +12,7 @@ import { ToggleSwitch, Select, TextAreaInput, + NumberInput, } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useRouter, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; @@ -227,7 +228,13 @@ const EditTrigger = ({ triggerData }: { triggerData?: Serialized } + render={({ field }) => { + if (conditions[index].name === 'time-on-site') { + return ; + } + + return ; + }} /> )} diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-triggers.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-triggers.ts index 2ac39a4b52fd..407bd6a34997 100644 --- a/apps/meteor/tests/e2e/page-objects/omnichannel-triggers.ts +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-triggers.ts @@ -88,7 +88,7 @@ export class OmnichannelTriggers { name: triggersName, description: 'Creating a fresh trigger', condition: 'time-on-site', - conditionValue: '5s', + conditionValue: 5, triggerMessage, }); await this.btnSave.click(); @@ -110,7 +110,7 @@ export class OmnichannelTriggers { name: string; description: string; condition: 'time-on-site' | 'chat-opened-by-visitor' | 'after-guest-registration'; - conditionValue?: string; + conditionValue?: string | number; sender: 'queue' | 'custom'; agentName?: string; triggerMessage: string; @@ -121,7 +121,7 @@ export class OmnichannelTriggers { data.condition && (await this.selectCondition(data.condition)); if (data.conditionValue) { - await this.inputConditionValue.fill(data.conditionValue); + await this.inputConditionValue.fill(data.conditionValue.toString()); } data.sender && (await this.selectSender(data.sender)); From b0ec0a1e0536484fb56ef2791f9e4475753c2afb Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Wed, 7 Feb 2024 15:54:47 -0300 Subject: [PATCH 022/207] test: make stats and license tests fully independent (#31681) --- apps/meteor/tests/end-to-end/api/19-statistics.js | 4 +++- apps/meteor/tests/end-to-end/api/20-licenses.js | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/19-statistics.js b/apps/meteor/tests/end-to-end/api/19-statistics.js index 2b167e1ac771..4e1f0d8b0148 100644 --- a/apps/meteor/tests/end-to-end/api/19-statistics.js +++ b/apps/meteor/tests/end-to-end/api/19-statistics.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { before, describe, it, after } from 'mocha'; import { getCredentials, api, request, credentials } from '../../data/api-data.js'; import { updatePermission } from '../../data/permissions.helper'; @@ -9,6 +9,8 @@ describe('[Statistics]', function () { before((done) => getCredentials(done)); + after(() => updatePermission('view-statistics', ['admin'])); + describe('[/statistics]', () => { let lastUptime; it('should return an error when the user does not have the necessary permission', (done) => { diff --git a/apps/meteor/tests/end-to-end/api/20-licenses.js b/apps/meteor/tests/end-to-end/api/20-licenses.js index 9088e4e9e1d9..83867712a80d 100644 --- a/apps/meteor/tests/end-to-end/api/20-licenses.js +++ b/apps/meteor/tests/end-to-end/api/20-licenses.js @@ -1,21 +1,24 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { before, describe, it, after } from 'mocha'; import { getCredentials, api, request, credentials } from '../../data/api-data.js'; import { password } from '../../data/user'; -import { createUser, login } from '../../data/users.helper'; +import { createUser, deleteUser, login } from '../../data/users.helper'; describe('licenses', function () { + let createdUser; this.retries(0); before((done) => getCredentials(done)); let unauthorizedUserCredentials; before(async () => { - const createdUser = await createUser(); + createdUser = await createUser(); unauthorizedUserCredentials = await login(createdUser.username, password); }); + after(() => deleteUser(createdUser)); + describe('[/licenses.add]', () => { it('should fail if not logged in', (done) => { request From a4f193fea58aa43d1fedb1ba4b5ddf1a19e63af6 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Wed, 7 Feb 2024 17:04:59 -0300 Subject: [PATCH 023/207] test: make incoming integration fully independent (#31657) --- .../api/07-incoming-integrations.js | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/07-incoming-integrations.js b/apps/meteor/tests/end-to-end/api/07-incoming-integrations.js index b6eff60393e5..37ce98ade6c5 100644 --- a/apps/meteor/tests/end-to-end/api/07-incoming-integrations.js +++ b/apps/meteor/tests/end-to-end/api/07-incoming-integrations.js @@ -4,9 +4,9 @@ import { after, before, describe, it } from 'mocha'; import { getCredentials, api, request, credentials } from '../../data/api-data.js'; import { createIntegration, removeIntegration } from '../../data/integration.helper'; import { updatePermission } from '../../data/permissions.helper'; -import { createRoom } from '../../data/rooms.helper.js'; +import { createRoom, deleteRoom } from '../../data/rooms.helper.js'; import { password } from '../../data/user'; -import { createUser, login } from '../../data/users.helper'; +import { createUser, deleteUser, login } from '../../data/users.helper'; describe('[Incoming Integrations]', function () { this.retries(0); @@ -20,27 +20,28 @@ describe('[Incoming Integrations]', function () { before((done) => getCredentials(done)); - before((done) => { - updatePermission('manage-incoming-integrations', []) - .then(() => updatePermission('manage-own-incoming-integrations', [])) - .then(() => updatePermission('manage-own-outgoing-integrations', [])) - .then(() => updatePermission('manage-outgoing-integrations', [])); + before(async () => { + await Promise.all([ + updatePermission('manage-incoming-integrations', []), + updatePermission('manage-own-incoming-integrations', []), + updatePermission('manage-own-outgoing-integrations', []), + updatePermission('manage-outgoing-integrations', []), + ]); testChannelName = `channel.test.${Date.now()}-${Math.random()}`; - createRoom({ type: 'c', name: testChannelName }).end((err, res) => { - channel = res.body.channel; - - return done(); - }); + channel = (await createRoom({ type: 'c', name: testChannelName })).body.channel; }); - after((done) => { - updatePermission('manage-incoming-integrations', ['admin']) - .then(() => updatePermission('manage-own-incoming-integrations', ['admin'])) - .then(() => updatePermission('manage-own-outgoing-integrations', ['admin'])) - .then(() => updatePermission('manage-outgoing-integrations', ['admin'])) - .then(done); + after(async () => { + await Promise.all([ + updatePermission('manage-incoming-integrations', ['admin']), + updatePermission('manage-own-incoming-integrations', ['admin']), + updatePermission('manage-own-outgoing-integrations', ['admin']), + updatePermission('manage-outgoing-integrations', ['admin']), + deleteRoom({ type: 'c', roomId: channel._id }), + deleteUser(user), + ]); }); describe('[/integrations.create]', () => { From 4ea58de9910eacf2c82886c4cfa89c8597d5042d Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Wed, 7 Feb 2024 17:44:48 -0300 Subject: [PATCH 024/207] test: make settings fully independent (#31659) --- apps/meteor/tests/end-to-end/api/08-settings.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/08-settings.js b/apps/meteor/tests/end-to-end/api/08-settings.js index d517b60eea9d..4ea6a6ae778e 100644 --- a/apps/meteor/tests/end-to-end/api/08-settings.js +++ b/apps/meteor/tests/end-to-end/api/08-settings.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { before, describe, it, after } from 'mocha'; import { getCredentials, api, request, credentials } from '../../data/api-data.js'; import { updateSetting } from '../../data/permissions.helper'; @@ -87,9 +87,9 @@ describe('[Settings]', function () { }); describe('With OAuth enabled', () => { - before((done) => { - updateSetting('Accounts_OAuth_Google', true).then(done); - }); + before(() => updateSetting('Accounts_OAuth_Google', true)); + + after(() => updateSetting('Accounts_OAuth_Google', false)); it('should include the OAuth service in the response', (done) => { // wait 3 seconds before getting the service list so the server has had time to update it @@ -111,9 +111,9 @@ describe('[Settings]', function () { }); describe('With OAuth disabled', () => { - before((done) => { - updateSetting('Accounts_OAuth_Google', false).then(done); - }); + before(() => updateSetting('Accounts_OAuth_Google', false)); + + after(() => updateSetting('Accounts_OAuth_Google', false)); it('should not include the OAuth service in the response', (done) => { // wait 3 seconds before getting the service list so the server has had time to update it From 2114557dce42a7a8685db864d19aa1c4c3fc6c36 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Wed, 7 Feb 2024 18:18:42 -0300 Subject: [PATCH 025/207] test: make permission test fully independent (#31675) --- apps/meteor/tests/end-to-end/api/11-permissions.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/meteor/tests/end-to-end/api/11-permissions.js b/apps/meteor/tests/end-to-end/api/11-permissions.js index 2668b37ab1be..30db3b5b67e2 100644 --- a/apps/meteor/tests/end-to-end/api/11-permissions.js +++ b/apps/meteor/tests/end-to-end/api/11-permissions.js @@ -1,13 +1,16 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { before, describe, it, after } from 'mocha'; import { getCredentials, api, request, credentials } from '../../data/api-data.js'; +import { updatePermission } from '../../data/permissions.helper'; describe('[Permissions]', function () { this.retries(0); before((done) => getCredentials(done)); + after(() => updatePermission('add-oauth-service', ['admin'])); + describe('[/permissions.listAll]', () => { it('should return an array with update and remove properties', (done) => { request From a4f4cd5f687f3e35c1c929fce69e6b8905de9a3d Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Wed, 7 Feb 2024 18:43:28 -0300 Subject: [PATCH 026/207] chore: Introduce `UserCardProvider` (#31622) --- .../client/lib/messageActionDefault.ts | 8 +-- apps/meteor/app/ui/client/lib/ChatMessages.ts | 9 ++- apps/meteor/app/ui/client/lib/userCard.ts | 64 ----------------- .../client/components/GazzodownText.tsx | 6 +- .../components/UserCard/UserCard.stories.tsx | 12 ++-- .../client/components/UserCard/UserCard.tsx | 64 ++++++----------- .../components/UserCard/UserCardAction.tsx | 2 +- .../components/UserCard/UserCardActions.tsx | 15 ++++ .../components/UserCard/UserCardContainer.tsx | 16 ++++- .../components/UserCard/UserCardSkeleton.tsx | 34 +++++++++ .../client/components/UserCard/index.ts | 9 +-- .../client/components/UserInfo/UserInfo.tsx | 4 +- .../components/UserInfo/UserInfoUsername.tsx | 5 +- .../components/message/MessageHeader.tsx | 5 +- .../message/variants/RoomMessage.tsx | 2 +- .../message/variants/SystemMessage.tsx | 4 +- .../message/variants/ThreadMessage.tsx | 2 +- apps/meteor/client/lib/chats/ChatAPI.ts | 4 +- .../moderation/UserReports/UserReportInfo.tsx | 4 +- .../admin/users/AdminUserInfoWithData.tsx | 4 +- .../oauth/components/CurrentUserDisplay.tsx | 4 +- .../views/omnichannel/components/Info.tsx | 4 +- .../MessageList/ContactHistoryMessage.tsx | 4 +- .../views/room/UserCard/UserCardWithData.tsx | 20 +++--- .../client/views/room/UserCardHolder.tsx | 22 ------ .../client/views/room/body/RoomBody.tsx | 6 +- .../views/room/contexts/UserCardContext.ts | 14 ++++ .../UserInfo/UserInfoWithData.tsx | 4 +- .../actions/useCallAction.tsx | 17 ++++- .../ReactionListModal/ReactionListModal.tsx | 27 ++----- .../ReactionListModal/ReactionUserTag.tsx | 26 +++++-- .../modals/ReactionListModal/Reactions.tsx | 9 +-- .../views/room/providers/RoomProvider.tsx | 5 +- .../views/room/providers/UserCardProvider.tsx | 70 +++++++++++++++++++ .../hooks/useChatMessagesInstance.ts | 4 +- .../views/room/providers/hooks/useUserCard.ts | 56 --------------- 36 files changed, 279 insertions(+), 286 deletions(-) delete mode 100644 apps/meteor/app/ui/client/lib/userCard.ts create mode 100644 apps/meteor/client/components/UserCard/UserCardActions.tsx create mode 100644 apps/meteor/client/components/UserCard/UserCardSkeleton.tsx delete mode 100644 apps/meteor/client/views/room/UserCardHolder.tsx create mode 100644 apps/meteor/client/views/room/contexts/UserCardContext.ts create mode 100644 apps/meteor/client/views/room/providers/UserCardProvider.tsx delete mode 100644 apps/meteor/client/views/room/providers/hooks/useUserCard.ts diff --git a/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts b/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts index 5807673188e5..834fd0bb05a2 100644 --- a/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts +++ b/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts @@ -10,7 +10,7 @@ import { dispatchToastMessage } from '../../../../client/lib/toast'; import { messageArgs } from '../../../../client/lib/utils/messageArgs'; import { router } from '../../../../client/providers/RouterProvider'; import ForwardMessageModal from '../../../../client/views/room/modals/ForwardMessageModal/ForwardMessageModal'; -import ReactionList from '../../../../client/views/room/modals/ReactionListModal'; +import ReactionListModal from '../../../../client/views/room/modals/ReactionListModal'; import ReportMessageModal from '../../../../client/views/room/modals/ReportMessageModal'; import { hasAtLeastOnePermission, hasPermission } from '../../../authorization/client'; import { ChatRoom, Subscriptions } from '../../../models/client'; @@ -266,10 +266,10 @@ Meteor.startup(async () => { label: 'Reactions', context: ['message', 'message-mobile', 'threads', 'videoconf', 'videoconf-threads'], type: 'interaction', - action(this: unknown, _, { message: { reactions = {} } = messageArgs(this).msg }) { + action(this: unknown, _, { message: { reactions = {} } = messageArgs(this).msg, chat }) { imperativeModal.open({ - component: ReactionList, - props: { reactions, onClose: imperativeModal.close }, + component: ReactionListModal, + props: { reactions, onOpenUserCard: chat?.userCard.openUserCard, onClose: imperativeModal.close }, }); }, condition({ message: { reactions } }) { diff --git a/apps/meteor/app/ui/client/lib/ChatMessages.ts b/apps/meteor/app/ui/client/lib/ChatMessages.ts index 2e7c4a479e86..59efeff49af1 100644 --- a/apps/meteor/app/ui/client/lib/ChatMessages.ts +++ b/apps/meteor/app/ui/client/lib/ChatMessages.ts @@ -45,7 +45,10 @@ export class ChatMessages implements ChatAPI { public ActionManager: any; - public userCard: { open(username: string): (event: UIEvent) => void; close(): void }; + public userCard: { + openUserCard(event: UIEvent, username: string): void; + closeUserCard(): void; + }; public emojiPicker: { open(el: Element, cb: (emoji: string) => void): void; @@ -160,8 +163,8 @@ export class ChatMessages implements ChatAPI { this.readStateManager = new ReadStateManager(rid); this.userCard = { - open: unimplemented, - close: unimplemented, + openUserCard: unimplemented, + closeUserCard: unimplemented, }; this.emojiPicker = { diff --git a/apps/meteor/app/ui/client/lib/userCard.ts b/apps/meteor/app/ui/client/lib/userCard.ts deleted file mode 100644 index 90b3d4ec5201..000000000000 --- a/apps/meteor/app/ui/client/lib/userCard.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type { ComponentProps } from 'react'; -import { createElement } from 'react'; -import { createPortal } from 'react-dom'; - -import { registerPortal } from '../../../../client/lib/portals/portalsSubscription'; -import { queueMicrotask } from '../../../../client/lib/utils/queueMicrotask'; -import UserCardHolder from '../../../../client/views/room/UserCardHolder'; - -type UserCardProps = ReturnType['getProps']>; - -let props: UserCardProps; - -const subscribers = new Set<() => void>(); - -const updateProps = (newProps: Partial) => { - props = { ...props, ...newProps }; - subscribers.forEach((subscriber) => subscriber()); -}; - -const getProps = () => props; - -const subscribeToProps = (callback: () => void) => { - subscribers.add(callback); - - return () => { - subscribers.delete(callback); - }; -}; - -const createContainer = () => { - const container = document.createElement('div'); - container.id = 'react-user-card'; - document.body.appendChild(container); - - return container; -}; - -let container: HTMLDivElement | undefined; -let unregisterPortal: (() => void) | undefined; - -export const closeUserCard = () => { - queueMicrotask(() => { - if (unregisterPortal) { - unregisterPortal(); - unregisterPortal = undefined; - } - }); -}; - -export const openUserCard = (params: Omit) => { - updateProps({ ...params, onClose: closeUserCard }); - - if (!container) { - container = createContainer(); - } - - if (!unregisterPortal) { - const children = createElement(UserCardHolder, { getProps, subscribeToProps }); - const portal = createPortal(children, container); - unregisterPortal = registerPortal(container, portal); - } - - return closeUserCard; -}; diff --git a/apps/meteor/client/components/GazzodownText.tsx b/apps/meteor/client/components/GazzodownText.tsx index fe86cbd86fd1..76232984a594 100644 --- a/apps/meteor/client/components/GazzodownText.tsx +++ b/apps/meteor/client/components/GazzodownText.tsx @@ -25,7 +25,9 @@ type GazzodownTextProps = { }; const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTextProps) => { + const chat = useChat(); const highlights = useMessageListHighlights(); + const highlightRegex = useMemo(() => { if (!highlights?.length) { return; @@ -51,8 +53,6 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe const ownUserId = useUserId(); const showMentionSymbol = Boolean(useUserPreference('mentionsWithSymbol')); - const chat = useChat(); - const resolveUserMention = useCallback( (mention: string) => { if (mention === 'all' || mention === 'here') { @@ -75,7 +75,7 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe return (event: UIEvent): void => { event.stopPropagation(); - chat?.userCard.open(username)(event); + chat?.userCard.openUserCard(event, username); }; }, [chat?.userCard], diff --git a/apps/meteor/client/components/UserCard/UserCard.stories.tsx b/apps/meteor/client/components/UserCard/UserCard.stories.tsx index b07d4a09cc7d..e3174381d85d 100644 --- a/apps/meteor/client/components/UserCard/UserCard.stories.tsx +++ b/apps/meteor/client/components/UserCard/UserCard.stories.tsx @@ -1,7 +1,7 @@ import type { ComponentMeta, ComponentStory } from '@storybook/react'; import React from 'react'; -import UserCard from '.'; +import { UserCard, UserCardRole, UserCardAction } from '.'; export default { title: 'Components/UserCard', @@ -14,16 +14,16 @@ export default { customStatus: '🛴 currently working on User Card', roles: ( <> - Admin - Rocket.Chat - Team + Admin + Rocket.Chat + Team ), bio: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla tempus, eros convallis vulputate cursus, nisi neque eleifend libero, eget lacinia justo purus nec est. In at sodales ipsum. Sed lacinia quis purus eget pulvinar. Aenean eu pretium nunc, at aliquam magna. Praesent dignissim, tortor sed volutpat mattis, mauris diam pulvinar leo, porta commodo risus est non purus. Mauris in justo vel lorem ullamcorper hendrerit. Nam est metus, viverra a pellentesque vitae, ornare eget odio. Morbi tempor feugiat mattis. Morbi non felis tempor, aliquam justo sed, sagittis nibh. Mauris consequat ex metus. Praesent sodales sit amet nibh a vulputate. Integer commodo, mi vel bibendum sollicitudin, urna lectus accumsan ante, eget faucibus augue ex id neque. Aenean consectetur, orci a pellentesque mattis, tortor tellus fringilla elit, non ullamcorper risus nunc feugiat risus. Fusce sit amet nisi dapibus turpis commodo placerat. In tortor ante, vehicula sit amet augue et, imperdiet porta sem.', actions: ( <> - - + + ), localTime: 'Local Time: 7:44 AM', diff --git a/apps/meteor/client/components/UserCard/UserCard.tsx b/apps/meteor/client/components/UserCard/UserCard.tsx index 5678f360af38..78dd9a720ac1 100644 --- a/apps/meteor/client/components/UserCard/UserCard.tsx +++ b/apps/meteor/client/components/UserCard/UserCard.tsx @@ -1,13 +1,14 @@ import { css } from '@rocket.chat/css-in-js'; -import { Box, Button, IconButton, Skeleton } from '@rocket.chat/fuselage'; +import { Box, Button, IconButton } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactNode, ComponentProps, MouseEvent } from 'react'; +import type { ReactNode, ComponentProps } from 'react'; import React, { forwardRef } from 'react'; import { useEmbeddedLayout } from '../../hooks/useEmbeddedLayout'; import MarkdownText from '../MarkdownText'; import * as Status from '../UserStatus'; import UserAvatar from '../avatar/UserAvatar'; +import UserCardActions from './UserCardActions'; import UserCardContainer from './UserCardContainer'; import UserCardInfo from './UserCardInfo'; import UserCardRoles from './UserCardRoles'; @@ -22,9 +23,7 @@ const clampStyle = css` `; type UserCardProps = { - className?: string; - style?: ComponentProps['style']; - open?: (e: MouseEvent) => void; + onOpenUserInfo?: () => void; name?: string; username?: string; etag?: string; @@ -36,61 +35,40 @@ type UserCardProps = { localTime?: ReactNode; onClose?: () => void; nickname?: string; - isLoading?: boolean; -}; +} & ComponentProps; -const UserCard = forwardRef(function UserCard( +const UserCard = forwardRef(function UserCard( { - className, - style, - open, + onOpenUserInfo, name, username, etag, - customStatus = , - roles = ( - <> - - - - - ), - bio = , + customStatus, + roles, + bio, status = , actions, - localTime = , + localTime, onClose, nickname, - isLoading, - }: UserCardProps, + ...props + }, ref, ) { const t = useTranslation(); const isLayoutEmbedded = useEmbeddedLayout(); return ( - - - {!isLoading && username ? ( - - ) : ( - - )} + +
+ {username && } - {isLoading ? ( - <> - - - - - ) : ( - actions - )} + {actions} - +
- {isLoading ? : } + {nickname && ( ({nickname}) @@ -113,9 +91,9 @@ const UserCard = forwardRef(function UserCard( {typeof bio === 'string' ? : bio} )} - {!isLoading && open && !isLayoutEmbedded && ( + {onOpenUserInfo && !isLayoutEmbedded && (
-
diff --git a/apps/meteor/client/components/UserCard/UserCardAction.tsx b/apps/meteor/client/components/UserCard/UserCardAction.tsx index f13e71b601b5..c96e742be94e 100644 --- a/apps/meteor/client/components/UserCard/UserCardAction.tsx +++ b/apps/meteor/client/components/UserCard/UserCardAction.tsx @@ -5,7 +5,7 @@ import React from 'react'; type UserCardActionProps = ComponentProps; const UserCardAction = ({ label, icon, ...props }: UserCardActionProps): ReactElement => ( - + ); export default UserCardAction; diff --git a/apps/meteor/client/components/UserCard/UserCardActions.tsx b/apps/meteor/client/components/UserCard/UserCardActions.tsx new file mode 100644 index 000000000000..6a1258c02997 --- /dev/null +++ b/apps/meteor/client/components/UserCard/UserCardActions.tsx @@ -0,0 +1,15 @@ +import { useToolbar } from '@react-aria/toolbar'; +import { ButtonGroup } from '@rocket.chat/fuselage'; +import type { ReactElement, ComponentProps } from 'react'; +import React, { useRef } from 'react'; + +type UserCardActionsProps = ComponentProps; + +const UserCardActions = (props: UserCardActionsProps): ReactElement => { + const ref = useRef(null); + const { toolbarProps } = useToolbar(props, ref); + + return ; +}; + +export default UserCardActions; diff --git a/apps/meteor/client/components/UserCard/UserCardContainer.tsx b/apps/meteor/client/components/UserCard/UserCardContainer.tsx index c1279e4ff513..2434077922e3 100644 --- a/apps/meteor/client/components/UserCard/UserCardContainer.tsx +++ b/apps/meteor/client/components/UserCard/UserCardContainer.tsx @@ -3,7 +3,21 @@ import type { ComponentProps } from 'react'; import React, { forwardRef } from 'react'; const UserCardContainer = forwardRef(function UserCardContainer(props: ComponentProps, ref) { - return ; + return ( + + ); }); export default UserCardContainer; diff --git a/apps/meteor/client/components/UserCard/UserCardSkeleton.tsx b/apps/meteor/client/components/UserCard/UserCardSkeleton.tsx new file mode 100644 index 000000000000..9339eca8f665 --- /dev/null +++ b/apps/meteor/client/components/UserCard/UserCardSkeleton.tsx @@ -0,0 +1,34 @@ +import { Box, Skeleton } from '@rocket.chat/fuselage'; +import type { ComponentProps } from 'react'; +import React, { forwardRef } from 'react'; + +import UserCardContainer from './UserCardContainer'; + +const UserCardSkeleton = forwardRef>(function UserCardSkeleton(props, ref) { + return ( + + + + + {Array.from({ length: 3 }).map((_, i) => ( + + ))} + + + + + + + + {Array.from({ length: 3 }).map((_, i) => ( + + ))} + {Array.from({ length: 2 }).map((_, i) => ( + + ))} + + + ); +}); + +export default UserCardSkeleton; diff --git a/apps/meteor/client/components/UserCard/index.ts b/apps/meteor/client/components/UserCard/index.ts index defc7140be15..75a5ad84cd6c 100644 --- a/apps/meteor/client/components/UserCard/index.ts +++ b/apps/meteor/client/components/UserCard/index.ts @@ -3,12 +3,7 @@ import UserCardAction from './UserCardAction'; import UserCardInfo from './UserCardInfo'; import UserCardRole from './UserCardRole'; import UserCardRoles from './UserCardRoles'; +import UserCardSkeleton from './UserCardSkeleton'; import UserCardUsername from './UserCardUsername'; -export default Object.assign(UserCard, { - Action: UserCardAction, - Role: UserCardRole, - Roles: UserCardRoles, - Info: UserCardInfo, - Username: UserCardUsername, -}); +export { UserCard, UserCardAction, UserCardInfo, UserCardRole, UserCardRoles, UserCardUsername, UserCardSkeleton }; diff --git a/apps/meteor/client/components/UserInfo/UserInfo.tsx b/apps/meteor/client/components/UserInfo/UserInfo.tsx index bdce27d028ad..72722e8e9156 100644 --- a/apps/meteor/client/components/UserInfo/UserInfo.tsx +++ b/apps/meteor/client/components/UserInfo/UserInfo.tsx @@ -12,7 +12,7 @@ import { ContextualbarScrollableContent } from '../Contextualbar'; import InfoPanel from '../InfoPanel'; import MarkdownText from '../MarkdownText'; import UTCClock from '../UTCClock'; -import UserCard from '../UserCard'; +import { UserCardRoles } from '../UserCard'; import UserInfoAvatar from './UserInfoAvatar'; type UserInfoDataProps = Serialized< @@ -90,7 +90,7 @@ const UserInfo = ({ {roles.length !== 0 && ( {t('Roles')} - {roles} + {roles} )} diff --git a/apps/meteor/client/components/UserInfo/UserInfoUsername.tsx b/apps/meteor/client/components/UserInfo/UserInfoUsername.tsx index 75d2c87a82c0..171c5c68d8c4 100644 --- a/apps/meteor/client/components/UserInfo/UserInfoUsername.tsx +++ b/apps/meteor/client/components/UserInfo/UserInfoUsername.tsx @@ -3,16 +3,15 @@ import type { Box } from '@rocket.chat/fuselage'; import type { ReactElement, ComponentProps } from 'react'; import React from 'react'; -import UserCard from '../UserCard'; +import { UserCardUsername } from '../UserCard'; type UserInfoUsernameProps = { username: IUser['username']; status: ReactElement; } & ComponentProps; -// TODO: Remove UserCard.Username const UserInfoUsername = ({ username, status, ...props }: UserInfoUsernameProps): ReactElement => ( - + ); export default UserInfoUsername; diff --git a/apps/meteor/client/components/message/MessageHeader.tsx b/apps/meteor/client/components/message/MessageHeader.tsx index 3189aa2972bc..c12ee8c3ac20 100644 --- a/apps/meteor/client/components/message/MessageHeader.tsx +++ b/apps/meteor/client/components/message/MessageHeader.tsx @@ -52,7 +52,7 @@ const MessageHeader = ({ message }: MessageHeaderProps): ReactElement => { data-username={user.username} {...(user.username !== undefined && chat?.userCard && { - onClick: chat?.userCard.open(message.u.username), + onClick: (e) => chat?.userCard.openUserCard(e, message.u.username), style: { cursor: 'pointer' }, })} > @@ -66,7 +66,7 @@ const MessageHeader = ({ message }: MessageHeaderProps): ReactElement => { data-qa-type='username' {...(user.username !== undefined && chat?.userCard && { - onClick: chat?.userCard.open(message.u.username), + onClick: (e) => chat?.userCard.openUserCard(e, message.u.username), style: { cursor: 'pointer' }, })} > @@ -75,7 +75,6 @@ const MessageHeader = ({ message }: MessageHeaderProps): ReactElement => { )} - {shouldShowRolesList && } {formatTime(message.ts)} {message.private && {t('Only_you_can_see_this_message')}} diff --git a/apps/meteor/client/components/message/variants/RoomMessage.tsx b/apps/meteor/client/components/message/variants/RoomMessage.tsx index af8a6c08297c..b38b7a08cff5 100644 --- a/apps/meteor/client/components/message/variants/RoomMessage.tsx +++ b/apps/meteor/client/components/message/variants/RoomMessage.tsx @@ -86,7 +86,7 @@ const RoomMessage = ({ username={message.u.username} size='x36' {...(chat?.userCard && { - onClick: chat?.userCard.open(message.u.username), + onClick: (e) => chat?.userCard.openUserCard(e, message.u.username), style: { cursor: 'pointer' }, })} /> diff --git a/apps/meteor/client/components/message/variants/SystemMessage.tsx b/apps/meteor/client/components/message/variants/SystemMessage.tsx index bef39112bb11..2936abbd1c39 100644 --- a/apps/meteor/client/components/message/variants/SystemMessage.tsx +++ b/apps/meteor/client/components/message/variants/SystemMessage.tsx @@ -75,7 +75,7 @@ const SystemMessage = ({ message, showUserAvatar }: SystemMessageProps): ReactEl user.username && chat?.userCard.openUserCard(e, user.username), style: { cursor: 'pointer' }, })} > @@ -88,7 +88,7 @@ const SystemMessage = ({ message, showUserAvatar }: SystemMessageProps): ReactEl data-username={user.username} {...(user.username !== undefined && chat?.userCard && { - onClick: chat?.userCard.open(user.username), + onClick: (e) => user.username && chat?.userCard.openUserCard(e, user.username), style: { cursor: 'pointer' }, })} > diff --git a/apps/meteor/client/components/message/variants/ThreadMessage.tsx b/apps/meteor/client/components/message/variants/ThreadMessage.tsx index 7c2b219b546f..f9a4b385ac01 100644 --- a/apps/meteor/client/components/message/variants/ThreadMessage.tsx +++ b/apps/meteor/client/components/message/variants/ThreadMessage.tsx @@ -59,7 +59,7 @@ const ThreadMessage = ({ message, sequential, unread, showUserAvatar }: ThreadMe username={message.u.username} size='x36' {...(chat?.userCard && { - onClick: chat?.userCard.open(message.u.username), + onClick: (e) => chat?.userCard.openUserCard(e, message.u.username), style: { cursor: 'pointer' }, })} /> diff --git a/apps/meteor/client/lib/chats/ChatAPI.ts b/apps/meteor/client/lib/chats/ChatAPI.ts index 36f40f404a4f..d2364cfcd72c 100644 --- a/apps/meteor/client/lib/chats/ChatAPI.ts +++ b/apps/meteor/client/lib/chats/ChatAPI.ts @@ -127,8 +127,8 @@ export type ChatAPI = { | undefined; readonly userCard: { - open(username: string): (event: UIEvent) => void; - close(): void; + openUserCard(event: UIEvent, username: string): void; + closeUserCard(): void; }; readonly emojiPicker: { diff --git a/apps/meteor/client/views/admin/moderation/UserReports/UserReportInfo.tsx b/apps/meteor/client/views/admin/moderation/UserReports/UserReportInfo.tsx index 5d1d99ba8f86..7b410f5e6705 100644 --- a/apps/meteor/client/views/admin/moderation/UserReports/UserReportInfo.tsx +++ b/apps/meteor/client/views/admin/moderation/UserReports/UserReportInfo.tsx @@ -18,7 +18,7 @@ import React, { useMemo } from 'react'; import { ContextualbarScrollableContent } from '../../../../components/Contextualbar'; import GenericNoResults from '../../../../components/GenericNoResults'; -import UserCard from '../../../../components/UserCard'; +import { UserCardRole } from '../../../../components/UserCard'; import { useFormatDate } from '../../../../hooks/useFormatDate'; import ReportReason from '../helpers/ReportReason'; import UserColumn from '../helpers/UserColumn'; @@ -79,7 +79,7 @@ const UserReportInfo = ({ userId }: { userId: string }) => { {t('Roles')} {report.user.roles.map((role, index) => ( - {role} + {role} ))} diff --git a/apps/meteor/client/views/admin/users/AdminUserInfoWithData.tsx b/apps/meteor/client/views/admin/users/AdminUserInfoWithData.tsx index d0580b90c642..f9b8bb01e129 100644 --- a/apps/meteor/client/views/admin/users/AdminUserInfoWithData.tsx +++ b/apps/meteor/client/views/admin/users/AdminUserInfoWithData.tsx @@ -10,7 +10,7 @@ import React, { useMemo } from 'react'; import { getUserEmailAddress } from '../../../../lib/getUserEmailAddress'; import { ContextualbarContent } from '../../../components/Contextualbar'; import { FormSkeleton } from '../../../components/Skeleton'; -import UserCard from '../../../components/UserCard'; +import { UserCardRole } from '../../../components/UserCard'; import UserInfo from '../../../components/UserInfo'; import { UserStatus } from '../../../components/UserStatus'; import { getUserEmailVerified } from '../../../lib/utils/getUserEmailVerified'; @@ -76,7 +76,7 @@ const AdminUserInfoWithData = ({ uid, onReload }: AdminUserInfoWithDataProps): R name, username, lastLogin, - roles: getRoles(roles).map((role, index) => {role}), + roles: getRoles(roles).map((role, index) => {role}), bio, canViewAllInfo, phone, diff --git a/apps/meteor/client/views/oauth/components/CurrentUserDisplay.tsx b/apps/meteor/client/views/oauth/components/CurrentUserDisplay.tsx index e335dca52748..6e52570418de 100644 --- a/apps/meteor/client/views/oauth/components/CurrentUserDisplay.tsx +++ b/apps/meteor/client/views/oauth/components/CurrentUserDisplay.tsx @@ -7,9 +7,7 @@ import { useTranslation } from 'react-i18next'; import LocalTime from '../../../components/LocalTime'; import MarkdownText from '../../../components/MarkdownText'; -import UserCard from '../../../components/UserCard'; -import UserCardInfo from '../../../components/UserCard/UserCardInfo'; -import UserCardRole from '../../../components/UserCard/UserCardRole'; +import { UserCard, UserCardInfo, UserCardRole } from '../../../components/UserCard'; const clampStyle = css` display: -webkit-box; diff --git a/apps/meteor/client/views/omnichannel/components/Info.tsx b/apps/meteor/client/views/omnichannel/components/Info.tsx index c7925af9c80e..c8cdb9be4204 100644 --- a/apps/meteor/client/views/omnichannel/components/Info.tsx +++ b/apps/meteor/client/views/omnichannel/components/Info.tsx @@ -2,14 +2,14 @@ import { css } from '@rocket.chat/css-in-js'; import type { CSSProperties, FC } from 'react'; import React from 'react'; -import UserCard from '../../../components/UserCard'; +import { UserCardInfo } from '../../../components/UserCard'; const wordBreak = css` word-break: break-word; `; const Info: FC<{ className?: string; style?: CSSProperties }> = ({ className, ...props }) => ( - + ); export default Info; diff --git a/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx b/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx index c6ad907a14e1..f5dfa94adb2b 100644 --- a/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx +++ b/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx @@ -52,7 +52,7 @@ const ContactHistoryMessage: FC<{ url={message.avatar} username={message.u.username} size='x18' - onClick={chat?.userCard.open(message.u.username)} + onClick={(e) => chat?.userCard.openUserCard(e, message.u.username)} style={{ cursor: 'pointer' }} /> )} @@ -80,7 +80,7 @@ const ContactHistoryMessage: FC<{ url={message.avatar} username={message.u.username} size='x36' - onClick={chat?.userCard.open(message.u.username)} + onClick={(e) => chat?.userCard.openUserCard(e, message.u.username)} style={{ cursor: 'pointer' }} /> )} diff --git a/apps/meteor/client/views/room/UserCard/UserCardWithData.tsx b/apps/meteor/client/views/room/UserCard/UserCardWithData.tsx index 2c8668434985..0337c794f245 100644 --- a/apps/meteor/client/views/room/UserCard/UserCardWithData.tsx +++ b/apps/meteor/client/views/room/UserCard/UserCardWithData.tsx @@ -2,14 +2,14 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { PositionAnimated, AnimatedVisibility } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useSetting, useRolesDescription, useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactElement, UIEvent } from 'react'; +import type { ReactElement } from 'react'; import React, { useMemo, useRef } from 'react'; import { getUserDisplayName } from '../../../../lib/getUserDisplayName'; import { Backdrop } from '../../../components/Backdrop'; import GenericMenu from '../../../components/GenericMenu/GenericMenu'; import LocalTime from '../../../components/LocalTime'; -import UserCard from '../../../components/UserCard'; +import { UserCard, UserCardAction, UserCardRole, UserCardSkeleton } from '../../../components/UserCard'; import { ReactiveUserStatus } from '../../../components/UserStatus'; import { useUserInfoQuery } from '../../../hooks/useUserInfoQuery'; import { useUserInfoActions } from '../hooks/useUserInfoActions'; @@ -18,11 +18,11 @@ type UserCardWithDataProps = { username: string; target: Element; rid: IRoom['_id']; - open: (e: UIEvent) => void; + onOpenUserInfo: () => void; onClose: () => void; }; -const UserCardWithData = ({ username, target, rid, open, onClose }: UserCardWithDataProps): ReactElement => { +const UserCardWithData = ({ username, target, rid, onOpenUserInfo, onClose }: UserCardWithDataProps) => { const t = useTranslation(); const ref = useRef(target); const getRoles = useRolesDescription(); @@ -50,7 +50,7 @@ const UserCardWithData = ({ username, target, rid, open, onClose }: UserCardWith _id, name: getUserDisplayName(name, username, showRealNames), username, - roles: roles && getRoles(roles).map((role, index) => {role}), + roles: roles && getRoles(roles).map((role, index) => {role}), bio, etag: avatarETag, localTime: utcOffset && Number.isInteger(utcOffset) && , @@ -60,9 +60,9 @@ const UserCardWithData = ({ username, target, rid, open, onClose }: UserCardWith }; }, [data, username, showRealNames, isLoading, getRoles]); - const handleOpen = useMutableCallback((e: UIEvent) => { - open?.(e); - onClose?.(); + const handleOpenUserInfo = useMutableCallback(() => { + onOpenUserInfo(); + onClose(); }); const { actions: actionsDefinition, menuActions: menuOptions } = useUserInfoActions( @@ -80,7 +80,7 @@ const UserCardWithData = ({ username, target, rid, open, onClose }: UserCardWith const actions = useMemo(() => { const mapAction = ([key, { content, icon, onClick }]: any): ReactElement => ( - + ); return [...actionsDefinition.map(mapAction), menu].filter(Boolean); @@ -90,7 +90,7 @@ const UserCardWithData = ({ username, target, rid, open, onClose }: UserCardWith <> - + {isLoading ? : } ); diff --git a/apps/meteor/client/views/room/UserCardHolder.tsx b/apps/meteor/client/views/room/UserCardHolder.tsx deleted file mode 100644 index 0f9729217b4c..000000000000 --- a/apps/meteor/client/views/room/UserCardHolder.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import type { ComponentProps } from 'react'; -import React, { Suspense, lazy } from 'react'; -import { useSyncExternalStore } from 'use-sync-external-store/shim'; - -const UserCard = lazy(() => import('./UserCard')); - -type UserCardHolderProps = { - getProps: () => ComponentProps; - subscribeToProps: (callback: () => void) => () => void; -}; - -function UserCardHolder({ getProps, subscribeToProps }: UserCardHolderProps) { - const props = useSyncExternalStore(subscribeToProps, getProps); - - return ( - - - - ); -} - -export default UserCardHolder; diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index d2722200b2df..1a6a11c07f79 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -174,13 +174,13 @@ const RoomBody = (): ReactElement => { }; }); - const handleOpenUserCardButtonClick = useCallback( + const handleOpenUserCard = useCallback( (event: UIEvent, username: IUser['username']) => { if (!username) { return; } - chat?.userCard.open(username)(event); + chat?.userCard.openUserCard(event, username); }, [chat?.userCard], ); @@ -585,7 +585,7 @@ const RoomBody = (): ReactElement => { username={roomLeader.username} name={roomLeader.name} visible={!hideLeaderHeader} - onAvatarClick={handleOpenUserCardButtonClick} + onAvatarClick={handleOpenUserCard} /> ) : null}
void; + closeUserCard: () => void; +}; + +export const UserCardContext = createContext({ + openUserCard: () => undefined, + closeUserCard: () => undefined, +}); + +export const useUserCard = () => useContext(UserCardContext); diff --git a/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoWithData.tsx b/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoWithData.tsx index 47383259a0c4..d5e2f36625f4 100644 --- a/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoWithData.tsx +++ b/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoWithData.tsx @@ -14,7 +14,7 @@ import { ContextualbarContent, } from '../../../../components/Contextualbar'; import { FormSkeleton } from '../../../../components/Skeleton'; -import UserCard from '../../../../components/UserCard'; +import { UserCardRole } from '../../../../components/UserCard'; import UserInfo from '../../../../components/UserInfo'; import { ReactiveUserStatus } from '../../../../components/UserStatus'; import { AsyncStatePhase } from '../../../../hooks/useAsyncState'; @@ -68,7 +68,7 @@ const UserInfoWithData = ({ uid, username, rid, onClose, onClickBack }: UserInfo name, username, lastLogin, - roles: roles && getRoles(roles).map((role, index) => {role}), + roles: roles && getRoles(roles).map((role, index) => {role}), bio, canViewAllInfo, phone, diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useCallAction.tsx b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useCallAction.tsx index fa0fc46fe804..27fd6de58520 100644 --- a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useCallAction.tsx +++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useCallAction.tsx @@ -3,9 +3,9 @@ import { isRoomFederated } from '@rocket.chat/core-typings'; import { useTranslation, useUserRoom, useUserId, useUserSubscriptionByName, useSetting, usePermission } from '@rocket.chat/ui-contexts'; import { useMemo } from 'react'; -import { closeUserCard } from '../../../../../../app/ui/client/lib/userCard'; import { useVideoConfDispatchOutgoing, useVideoConfIsCalling, useVideoConfIsRinging } from '../../../../../contexts/VideoConfContext'; import { VideoConfManager } from '../../../../../lib/VideoConfManager'; +import { useUserCard } from '../../../contexts/UserCardContext'; import { useVideoConfWarning } from '../../../contextualBar/VideoConference/hooks/useVideoConfWarning'; import type { UserInfoAction, UserInfoActionType } from '../useUserInfoActions'; @@ -13,6 +13,7 @@ export const useCallAction = (user: Pick): UserInfoAc const t = useTranslation(); const usernameSubscription = useUserSubscriptionByName(user.username ?? ''); const room = useUserRoom(usernameSubscription?.rid || ''); + const { closeUserCard } = useUserCard(); const dispatchWarning = useVideoConfWarning(); const dispatchPopup = useVideoConfDispatchOutgoing(); @@ -49,7 +50,19 @@ export const useCallAction = (user: Pick): UserInfoAc type: 'communication' as UserInfoActionType, } : undefined; - }, [room, user._id, ownUserId, enabledForDMs, permittedToCallManagement, isCalling, isRinging, t, dispatchPopup, dispatchWarning]); + }, [ + room, + user._id, + ownUserId, + enabledForDMs, + permittedToCallManagement, + isCalling, + isRinging, + t, + dispatchPopup, + dispatchWarning, + closeUserCard, + ]); return videoCallOption; }; diff --git a/apps/meteor/client/views/room/modals/ReactionListModal/ReactionListModal.tsx b/apps/meteor/client/views/room/modals/ReactionListModal/ReactionListModal.tsx index 162c8dd3071b..596d74977b3d 100644 --- a/apps/meteor/client/views/room/modals/ReactionListModal/ReactionListModal.tsx +++ b/apps/meteor/client/views/room/modals/ReactionListModal/ReactionListModal.tsx @@ -1,38 +1,25 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactElement } from 'react'; +import type { ReactElement, UIEvent } from 'react'; import React from 'react'; import GenericModal from '../../../../components/GenericModal'; -import { useChat } from '../../contexts/ChatContext'; import Reactions from './Reactions'; -type ReactionListProps = { +type ReactionListModalProps = { reactions: Required['reactions']; + onOpenUserCard?: (e: UIEvent, username: string) => void; onClose: () => void; }; -const ReactionList = ({ reactions, onClose }: ReactionListProps): ReactElement => { +const ReactionListModal = ({ reactions, onOpenUserCard, onClose }: ReactionListModalProps): ReactElement => { const t = useTranslation(); - const chat = useChat(); - - const onClick = useMutableCallback((e) => { - const { username } = e.currentTarget.dataset; - - if (!username) { - return; - } - - chat?.userCard.open(username)(e); - }); - return ( - - + + ); }; -export default ReactionList; +export default ReactionListModal; diff --git a/apps/meteor/client/views/room/modals/ReactionListModal/ReactionUserTag.tsx b/apps/meteor/client/views/room/modals/ReactionListModal/ReactionUserTag.tsx index 258751d18095..4c13ac8af532 100644 --- a/apps/meteor/client/views/room/modals/ReactionListModal/ReactionUserTag.tsx +++ b/apps/meteor/client/views/room/modals/ReactionListModal/ReactionUserTag.tsx @@ -1,18 +1,30 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Box, Tag } from '@rocket.chat/fuselage'; -import type { ReactElement } from 'react'; +import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import type { ReactElement, UIEvent } from 'react'; import React from 'react'; type ReactionUserTagProps = { username: IUser['username']; - onClick: (e: React.MouseEvent) => void; displayName: string; + onOpenUserCard?: (e: UIEvent, username: string) => void; }; -const ReactionUserTag = ({ username, onClick, displayName }: ReactionUserTagProps): ReactElement => ( - - {displayName} - -); +const ReactionUserTag = ({ username, displayName, onOpenUserCard }: ReactionUserTagProps): ReactElement => { + const handleOpenCard = useEffectEvent((e: UIEvent) => { + if (!username) { + return; + } + onOpenUserCard?.(e, username); + }); + + return ( + + + {displayName} + + + ); +}; export default ReactionUserTag; diff --git a/apps/meteor/client/views/room/modals/ReactionListModal/Reactions.tsx b/apps/meteor/client/views/room/modals/ReactionListModal/Reactions.tsx index 235cdfc0a269..d766661de3e5 100644 --- a/apps/meteor/client/views/room/modals/ReactionListModal/Reactions.tsx +++ b/apps/meteor/client/views/room/modals/ReactionListModal/Reactions.tsx @@ -1,7 +1,7 @@ import type { IMessage } from '@rocket.chat/core-typings'; import { Box } from '@rocket.chat/fuselage'; import { useSetting } from '@rocket.chat/ui-contexts'; -import type { ReactElement } from 'react'; +import type { ReactElement, UIEvent } from 'react'; import React from 'react'; import Emoji from '../../../../components/Emoji'; @@ -9,11 +9,12 @@ import ReactionUserTag from './ReactionUserTag'; type ReactionsProps = { reactions: Required['reactions']; - onClick: (e: React.MouseEvent) => void; + onOpenUserCard?: (e: UIEvent, username: string) => void; }; -const Reactions = ({ reactions, onClick }: ReactionsProps): ReactElement => { +const Reactions = ({ reactions, onOpenUserCard }: ReactionsProps): ReactElement => { const useRealName = useSetting('UI_Use_Real_Name'); + return ( {Object.entries(reactions).map(([reaction, { names = [], usernames }]) => ( @@ -25,7 +26,7 @@ const Reactions = ({ reactions, onClick }: ReactionsProps): ReactElement => { key={username} displayName={useRealName ? names[i] || username : username} username={username} - onClick={onClick} + onOpenUserCard={onOpenUserCard} /> ))} diff --git a/apps/meteor/client/views/room/providers/RoomProvider.tsx b/apps/meteor/client/views/room/providers/RoomProvider.tsx index 88884d01439d..65c83100b1c0 100644 --- a/apps/meteor/client/views/room/providers/RoomProvider.tsx +++ b/apps/meteor/client/views/room/providers/RoomProvider.tsx @@ -17,6 +17,7 @@ import { useRoomRolesManagement } from '../body/hooks/useRoomRolesManagement'; import { RoomContext } from '../contexts/RoomContext'; import ComposerPopupProvider from './ComposerPopupProvider'; import RoomToolboxProvider from './RoomToolboxProvider'; +import UserCardProvider from './UserCardProvider'; import { useRedirectOnSettingsChanged } from './hooks/useRedirectOnSettingsChanged'; import { useRoomQuery } from './hooks/useRoomQuery'; import { useUsersNameChanged } from './hooks/useUsersNameChanged'; @@ -110,7 +111,9 @@ const RoomProvider = ({ rid, children }: RoomProviderProps): ReactElement => { - {children} + + {children} + diff --git a/apps/meteor/client/views/room/providers/UserCardProvider.tsx b/apps/meteor/client/views/room/providers/UserCardProvider.tsx new file mode 100644 index 000000000000..47a9f1cd744e --- /dev/null +++ b/apps/meteor/client/views/room/providers/UserCardProvider.tsx @@ -0,0 +1,70 @@ +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import type { ComponentProps, ReactNode } from 'react'; +import React, { Suspense, lazy, useCallback, useMemo, useState } from 'react'; + +import { useRoom } from '../contexts/RoomContext'; +import { useRoomToolbox } from '../contexts/RoomToolboxContext'; +import { UserCardContext } from '../contexts/UserCardContext'; + +const UserCard = lazy(() => import('../UserCard')); + +const UserCardProvider = ({ children }: { children: ReactNode }) => { + const room = useRoom(); + const [userCardData, setUserCardData] = useState | null>(null); + + const { openTab } = useRoomToolbox(); + + const openUserInfo = useMutableCallback((username?: string) => { + switch (room.t) { + case 'l': + openTab('room-info', username); + break; + + case 'v': + openTab('voip-room-info', username); + break; + + case 'd': + (room.uids?.length ?? 0) > 2 ? openTab('user-info-group', username) : openTab('user-info', username); + break; + + default: + openTab('members-list', username); + break; + } + }); + + const handleSetUserCard = useCallback( + (e, username) => { + setUserCardData({ + username, + rid: room._id, + target: e.target, + onOpenUserInfo: () => openUserInfo(username), + onClose: () => setUserCardData(null), + }); + }, + [openUserInfo, room._id], + ); + + const contextValue = useMemo( + () => ({ + openUserCard: handleSetUserCard, + closeUserCard: () => setUserCardData(null), + }), + [handleSetUserCard], + ); + + return ( + + {children} + {userCardData && ( + + + + )} + + ); +}; + +export default UserCardProvider; diff --git a/apps/meteor/client/views/room/providers/hooks/useChatMessagesInstance.ts b/apps/meteor/client/views/room/providers/hooks/useChatMessagesInstance.ts index a468efd68fc6..023e99a99dd9 100644 --- a/apps/meteor/client/views/room/providers/hooks/useChatMessagesInstance.ts +++ b/apps/meteor/client/views/room/providers/hooks/useChatMessagesInstance.ts @@ -7,8 +7,8 @@ import { useEmojiPicker } from '../../../../contexts/EmojiPickerContext'; import type { ChatAPI } from '../../../../lib/chats/ChatAPI'; import { useUiKitActionManager } from '../../../../uikit/hooks/useUiKitActionManager'; import { useRoomSubscription } from '../../contexts/RoomContext'; +import { useUserCard } from '../../contexts/UserCardContext'; import { useInstance } from './useInstance'; -import { useUserCard } from './useUserCard'; export function useChatMessagesInstance({ rid, tmid }: { rid: IRoom['_id']; tmid?: IMessage['_id'] }): ChatAPI { const uid = useUserId(); @@ -26,8 +26,8 @@ export function useChatMessagesInstance({ rid, tmid }: { rid: IRoom['_id']; tmid } }, [subscription, chatMessages?.readStateManager]); - chatMessages.userCard = useUserCard(); chatMessages.emojiPicker = useEmojiPicker(); + chatMessages.userCard = useUserCard(); return chatMessages; } diff --git a/apps/meteor/client/views/room/providers/hooks/useUserCard.ts b/apps/meteor/client/views/room/providers/hooks/useUserCard.ts deleted file mode 100644 index f04774df3da5..000000000000 --- a/apps/meteor/client/views/room/providers/hooks/useUserCard.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import type { UIEvent } from 'react'; -import { useCallback, useEffect } from 'react'; - -import { openUserCard, closeUserCard } from '../../../../../app/ui/client/lib/userCard'; -import { useRoom } from '../../contexts/RoomContext'; -import { useRoomToolbox } from '../../contexts/RoomToolboxContext'; - -export const useUserCard = () => { - useEffect(() => { - return () => { - closeUserCard(); - }; - }, []); - - const room = useRoom(); - const { openTab } = useRoomToolbox(); - - const openUserInfo = useMutableCallback((username?: string) => { - switch (room.t) { - case 'l': - openTab('room-info', username); - break; - - case 'v': - openTab('voip-room-info', username); - break; - - case 'd': - (room.uids?.length ?? 0) > 2 ? openTab('user-info-group', username) : openTab('user-info', username); - break; - - default: - openTab('members-list', username); - break; - } - }); - - const open = useCallback( - (username: string) => (event: UIEvent) => { - event.preventDefault(); - openUserCard({ - username, - target: event.currentTarget, - rid: room._id, - open: (event: UIEvent) => { - event.preventDefault(); - openUserInfo(username); - }, - }); - }, - [openUserInfo, room._id], - ); - - return { open, close: closeUserCard }; -}; From 0f7554ad1916d2de4f51a6462d4905a7306f85f1 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Wed, 7 Feb 2024 19:23:39 -0300 Subject: [PATCH 027/207] test: make emoji custom test fully independent (#31674) --- apps/meteor/tests/end-to-end/api/12-emoji-custom.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/meteor/tests/end-to-end/api/12-emoji-custom.js b/apps/meteor/tests/end-to-end/api/12-emoji-custom.js index ba9c6756b470..488be47852ca 100644 --- a/apps/meteor/tests/end-to-end/api/12-emoji-custom.js +++ b/apps/meteor/tests/end-to-end/api/12-emoji-custom.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { before, describe, it, after } from 'mocha'; import { getCredentials, api, request, credentials } from '../../data/api-data.js'; import { imgURL } from '../../data/interactions'; @@ -8,10 +8,18 @@ const customEmojiName = `my-custom-emoji-${Date.now()}`; let createdCustomEmoji; describe('[EmojiCustom]', function () { + let withoutAliases; + this.retries(0); before((done) => getCredentials(done)); + after(() => + request.post(api('emoji-custom.delete')).set(credentials).send({ + emojiId: withoutAliases._id, + }), + ); + describe('[/emoji-custom.create]', () => { it('should create new custom emoji', (done) => { request @@ -75,6 +83,7 @@ describe('[EmojiCustom]', function () { expect(res.body.emojis).to.have.property('remove').and.to.be.a('array').and.to.have.lengthOf(0); createdCustomEmoji = res.body.emojis.update.find((emoji) => emoji.name === customEmojiName); + withoutAliases = res.body.emojis.update.find((emoji) => emoji.name === `${customEmojiName}-without-aliases`); }) .end(done); }); From 4f3947ca21039e7edf5ea081b1e44c023a603895 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Wed, 7 Feb 2024 19:59:42 -0300 Subject: [PATCH 028/207] test: make commands test fully independent (#31677) --- .../tests/end-to-end/api/16-commands.js | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/16-commands.js b/apps/meteor/tests/end-to-end/api/16-commands.js index 5022feea1cd7..74e9255524c4 100644 --- a/apps/meteor/tests/end-to-end/api/16-commands.js +++ b/apps/meteor/tests/end-to-end/api/16-commands.js @@ -1,9 +1,9 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { before, describe, it, after } from 'mocha'; import { getCredentials, api, request, credentials } from '../../data/api-data.js'; import { sendSimpleMessage } from '../../data/chat.helper.js'; -import { createRoom } from '../../data/rooms.helper.js'; +import { createRoom, deleteRoom } from '../../data/rooms.helper.js'; import { password } from '../../data/user'; import { createUser, deleteUser, login } from '../../data/users.helper.js'; @@ -97,25 +97,24 @@ describe('[Commands]', function () { let testChannel; let threadMessage; - before((done) => { - createRoom({ type: 'c', name: `channel.test.commands.${Date.now()}` }).end((err, res) => { - testChannel = res.body.channel; - sendSimpleMessage({ - roomId: testChannel._id, - text: 'Message to create thread', - }).end((err, message) => { - sendSimpleMessage({ - roomId: testChannel._id, - text: 'Thread Message', - tmid: message.body.message._id, - }).end((err, res) => { - threadMessage = res.body.message; - done(); - }); - }); + before(async () => { + testChannel = (await createRoom({ type: 'c', name: `channel.test.commands.${Date.now()}` })).body.channel; + const { body: { message } = {} } = await sendSimpleMessage({ + roomId: testChannel._id, + text: 'Message to create thread', }); + + threadMessage = ( + await sendSimpleMessage({ + roomId: testChannel._id, + text: 'Thread Message', + tmid: message._id, + }) + ).body.message; }); + after(() => deleteRoom({ type: 'c', roomId: testChannel._id })); + it('should return an error when call the endpoint without "command" required parameter', (done) => { request .post(api('commands.run')) From a1bc9f68df85c689b8d6373a709ce8581a953028 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Wed, 7 Feb 2024 20:35:42 -0300 Subject: [PATCH 029/207] test: make custom sound tests fully independent (#31678) --- .../tests/end-to-end/api/17-custom-sounds.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/meteor/tests/end-to-end/api/17-custom-sounds.js b/apps/meteor/tests/end-to-end/api/17-custom-sounds.js index 6afefe30eb23..006129a31d7c 100644 --- a/apps/meteor/tests/end-to-end/api/17-custom-sounds.js +++ b/apps/meteor/tests/end-to-end/api/17-custom-sounds.js @@ -3,7 +3,7 @@ import { readFileSync } from 'fs'; import path from 'path'; import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { before, describe, it, after } from 'mocha'; import { getCredentials, api, request, credentials } from '../../data/api-data.js'; @@ -82,6 +82,20 @@ describe('[CustomSounds]', function () { .expect(200); }); + after(() => + request + .post(api('method.call/deleteCustomSound')) + .set(credentials) + .send({ + message: JSON.stringify({ + msg: 'method', + id: '33', + method: 'deleteCustomSound', + params: [fileId], + }), + }), + ); + it('should return forbidden if the there is no fileId on the url', (done) => { request .get('/custom-sounds/') From 6ada4862d10f5efa71a5f81fbc2310fcb0003bc9 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Wed, 7 Feb 2024 21:13:30 -0300 Subject: [PATCH 030/207] test: make subscription test fully independent (#31673) --- .../tests/end-to-end/api/10-subscriptions.js | 72 ++++++++----------- 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/10-subscriptions.js b/apps/meteor/tests/end-to-end/api/10-subscriptions.js index 531291a99216..e54102621690 100644 --- a/apps/meteor/tests/end-to-end/api/10-subscriptions.js +++ b/apps/meteor/tests/end-to-end/api/10-subscriptions.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { after, before, describe, it } from 'mocha'; import { getCredentials, api, request, credentials } from '../../data/api-data.js'; -import { createRoom } from '../../data/rooms.helper'; +import { createRoom, deleteRoom } from '../../data/rooms.helper'; import { adminUsername } from '../../data/user'; import { createUser, deleteUser, login } from '../../data/users.helper.js'; @@ -11,6 +11,14 @@ describe('[Subscriptions]', function () { before((done) => getCredentials(done)); + let testChannel; + + before(async () => { + testChannel = (await createRoom({ type: 'c', name: `channel.test.${Date.now()}` })).body.channel; + }); + + after(() => deleteRoom({ type: 'c', roomId: testChannel._id })); + it('/subscriptions.get', (done) => { request .get(api('subscriptions.get')) @@ -42,19 +50,6 @@ describe('[Subscriptions]', function () { }); it('/subscriptions.getOne:', () => { - let testChannel; - it('create an channel', (done) => { - request - .post(api('channels.create')) - .set(credentials) - .send({ - name: `channel.test.${Date.now()}`, - }) - .end((err, res) => { - testChannel = res.body.channel; - done(); - }); - }); it('subscriptions.getOne', (done) => { request .get(api('subscriptions.getOne')) @@ -74,29 +69,23 @@ describe('[Subscriptions]', function () { describe('[/subscriptions.read]', () => { let testChannel; - it('create a channel', (done) => { - createRoom({ type: 'c', name: `channel.test.${Date.now()}` }).end((err, res) => { - testChannel = res.body.channel; - done(); - }); - }); - let testGroup; - it('create a group', (done) => { - createRoom({ type: 'p', name: `channel.test.${Date.now()}` }).end((err, res) => { - testGroup = res.body.group; - done(); - }); - }); - let testDM; - it('create a DM', (done) => { - createRoom({ type: 'd', username: 'rocket.cat' }).end((err, res) => { - testDM = res.body.room; - done(); - }); + + before(async () => { + testChannel = (await createRoom({ type: 'c', name: `channel.test.${Date.now()}` })).body.channel; + testGroup = (await createRoom({ type: 'p', name: `group.test.${Date.now()}` })).body.group; + testDM = (await createRoom({ type: 'd', username: 'rocket.cat' })).body.room; }); + after(() => + Promise.all([ + deleteRoom({ type: 'd', roomId: testDM._id }), + deleteRoom({ type: 'c', roomId: testChannel._id }), + deleteRoom({ type: 'p', roomId: testGroup._id }), + ]), + ); + it('should mark public channels as read', (done) => { request .post(api('subscriptions.read')) @@ -332,18 +321,13 @@ describe('[Subscriptions]', function () { describe('[/subscriptions.unread]', () => { let testChannel; - it('create an channel', (done) => { - request - .post(api('channels.create')) - .set(credentials) - .send({ - name: `channel.test.${Date.now()}`, - }) - .end((err, res) => { - testChannel = res.body.channel; - done(); - }); + + before(async () => { + testChannel = (await createRoom({ type: 'c', name: `channel.test.${Date.now()}` })).body.channel; }); + + after(() => deleteRoom({ type: 'c', roomId: testChannel._id })); + it('should fail when there are no messages on an channel', (done) => { request .post(api('subscriptions.unread')) From 6bb6f7b6ec3a0ad63d76760556af7460f651d6d6 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Wed, 7 Feb 2024 21:51:53 -0300 Subject: [PATCH 031/207] test: make assets api tests fully independent (#31676) --- apps/meteor/tests/end-to-end/api/14-assets.js | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/14-assets.js b/apps/meteor/tests/end-to-end/api/14-assets.js index 8248e8c04f09..3bcc968e7ee7 100644 --- a/apps/meteor/tests/end-to-end/api/14-assets.js +++ b/apps/meteor/tests/end-to-end/api/14-assets.js @@ -1,26 +1,18 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { before, describe, it, after } from 'mocha'; import { getCredentials, api, request, credentials } from '../../data/api-data.js'; import { imgURL } from '../../data/interactions'; +import { updatePermission } from '../../data/permissions.helper'; describe('[Assets]', function () { this.retries(0); before((done) => getCredentials(done)); - it('giving "manage-assets" permission to user', (done) => { - request - .post(api('permissions.update')) - .set(credentials) - .send({ permissions: [{ _id: 'manage-assets', roles: ['admin'] }] }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); - }); + before(() => updatePermission('manage-assets', ['admin'])); + + after(() => updatePermission('manage-assets', ['admin'])); describe('[/assets.setAsset]', () => { it('should set the "logo" asset', (done) => { @@ -28,7 +20,6 @@ describe('[Assets]', function () { .post(api('assets.setAsset')) .set(credentials) .attach('asset', imgURL) - .field({ assetName: 'logo', }) From 1635b1d5190b52d9c5a67263e77affb0fe7dd0c8 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Thu, 8 Feb 2024 11:47:43 -0300 Subject: [PATCH 032/207] test: make oauthapps test fully independent (#31680) --- apps/meteor/tests/end-to-end/api/18-oauthapps.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/meteor/tests/end-to-end/api/18-oauthapps.js b/apps/meteor/tests/end-to-end/api/18-oauthapps.js index 83017a811047..4566dab47967 100644 --- a/apps/meteor/tests/end-to-end/api/18-oauthapps.js +++ b/apps/meteor/tests/end-to-end/api/18-oauthapps.js @@ -1,14 +1,26 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { before, describe, it, after } from 'mocha'; import { getCredentials, api, request, credentials } from '../../data/api-data.js'; import { updatePermission } from '../../data/permissions.helper'; describe('[OAuthApps]', function () { + const createdAppsIds = []; this.retries(0); before((done) => getCredentials(done)); + after(() => + Promise.all([ + updatePermission('manage-oauth-apps', ['admin']), + ...createdAppsIds.map((appId) => + request.post(api(`oauth-apps.delete`)).set(credentials).send({ + appId, + }), + ), + ]), + ); + describe('[/oauth-apps.list]', () => { it('should return an error when the user does not have the necessary permission', (done) => { updatePermission('manage-oauth-apps', []).then(() => { @@ -179,6 +191,7 @@ describe('[OAuthApps]', function () { expect(res.body).to.have.nested.property('application.name', name); expect(res.body).to.have.nested.property('application.redirectUri', redirectUri); expect(res.body).to.have.nested.property('application.active', active); + createdAppsIds.push(res.body.application._id); }); }); }); @@ -202,6 +215,7 @@ describe('[OAuthApps]', function () { .expect(200) .end((err, res) => { appId = res.body.application._id; + createdAppsIds.push(appId); done(); }); }); From b254c5e26c8bc94065f326449dba0239c4b7bdd7 Mon Sep 17 00:00:00 2001 From: Debdut Chakraborty Date: Fri, 9 Feb 2024 00:33:27 +0530 Subject: [PATCH 033/207] fix: add group and section to homeserver domain setting (#31700) --- .changeset/cyan-countries-impress.md | 5 +++++ .gitignore | 2 ++ .../infrastructure/rocket-chat/adapters/Settings.ts | 2 ++ 3 files changed, 9 insertions(+) create mode 100644 .changeset/cyan-countries-impress.md diff --git a/.changeset/cyan-countries-impress.md b/.changeset/cyan-countries-impress.md new file mode 100644 index 000000000000..6bd78b6f62f8 --- /dev/null +++ b/.changeset/cyan-countries-impress.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed matrix homeserver domain setting not being visible in admin panel diff --git a/.gitignore b/.gitignore index 13ee265952bb..fcf2b8cd07c7 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ yarn-error.log* .envrc *.sublime-workspace + +**/.vim/ diff --git a/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts b/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts index 92278549b6fc..952c2a338440 100644 --- a/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts +++ b/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts @@ -237,6 +237,8 @@ export class RocketChatSettingsAdapter { i18nLabel: 'Federation_Matrix_homeserver_domain', i18nDescription: 'Federation_Matrix_homeserver_domain_desc', alert: 'Federation_Matrix_homeserver_domain_alert', + group: 'Federation', + section: 'Matrix Bridge', }, ); From 1e833d5b7c55d9ecd13c4eaa7988a275b2d0a85b Mon Sep 17 00:00:00 2001 From: Tiago Evangelista Pinto Date: Thu, 8 Feb 2024 16:57:22 -0300 Subject: [PATCH 034/207] chore: Introduce `ui-avatar` package to monorepo (#31482) Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com> --- .../components/RoomAutoComplete/Avatar.tsx | 3 +- .../RoomAutoComplete/RoomAutoComplete.tsx | 2 +- .../RoomAutoCompleteMultiple.tsx | 3 +- .../UserAndRoomAutoCompleteMultiple.tsx | 3 +- .../UserAutoComplete/UserAutoComplete.tsx | 3 +- .../UserAutoCompleteMultiple.tsx | 3 +- .../UserAutoCompleteMultipleFederated.tsx | 2 +- .../UserAutoCompleteMultipleOption.tsx | 3 +- .../client/components/UserCard/UserCard.tsx | 2 +- .../components/UserInfo/UserInfoAvatar.tsx | 3 +- .../client/components/avatar/AppAvatar.tsx | 19 ------ .../client/components/avatar/BaseAvatar.tsx | 27 -------- .../client/components/avatar/RoomAvatar.tsx | 29 --------- .../components/avatar/RoomAvatarEditor.tsx | 2 +- .../UserAvatarEditor/UserAvatarEditor.tsx | 8 +-- .../structure/AttachmentAuthorAvatar.tsx | 3 +- .../message/variants/RoomMessage.tsx | 5 +- .../message/variants/SystemMessage.tsx | 2 +- .../message/variants/ThreadMessage.tsx | 5 +- .../message/variants/ThreadMessagePreview.tsx | 11 +++- .../client/sidebar/Item/Condensed.stories.tsx | 2 +- .../client/sidebar/Item/Extended.stories.tsx | 2 +- .../client/sidebar/Item/Medium.stories.tsx | 2 +- .../sidebar/header/UserAvatarWithStatus.tsx | 2 +- .../client/sidebar/header/UserMenuHeader.tsx | 2 +- .../sidebar/hooks/useAvatarTemplate.tsx | 3 +- .../moderation/helpers/ContextMessage.tsx | 2 +- .../admin/moderation/helpers/UserColumn.tsx | 8 +-- .../UsersInRoleTable/UsersInRoleTableRow.tsx | 2 +- .../client/views/admin/rooms/RoomRow.tsx | 2 +- .../groups/voip/VoipExtensionsPage.tsx | 2 +- .../admin/users/UsersTable/UsersTableRow.tsx | 2 +- .../tabs/users/UsersTable/UsersTableRow.tsx | 2 +- .../AppDetailsPage/AppDetailsPageHeader.tsx | 6 +- .../tabs/AppRequests/AppRequestItem.tsx | 2 +- .../views/marketplace/AppsList/AppRow.tsx | 2 +- apps/meteor/client/views/meet/CallPage.tsx | 39 +++++------ apps/meteor/client/views/meet/MeetPage.tsx | 20 +++--- .../agents/AgentsTable/AgentsTableRow.tsx | 2 +- .../contactHistory/ContactHistoryItem.tsx | 6 +- .../MessageList/ContactHistoryMessage.tsx | 2 +- .../DepartmentAgentsTable/AgentAvatar.tsx | 3 +- .../calls/contextualBar/VoipInfo.tsx | 2 +- .../directory/components/AgentField.tsx | 2 +- .../contacts/contextualBar/ContactInfo.tsx | 2 +- .../omnichannel/managers/ManagersTable.tsx | 2 +- .../omnichannel/queueList/QueueListTable.tsx | 2 +- .../client/views/room/Header/RoomHeader.tsx | 2 +- .../client/views/room/body/LeaderBar.tsx | 2 +- .../room/body/RoomForeword/RoomForeword.tsx | 2 +- .../room/composer/ComposerBoxPopupUser.tsx | 2 +- .../components/DiscussionsListItem.tsx | 5 +- .../contextualBar/Info/RoomInfo/RoomInfo.tsx | 2 +- .../RoomMembers/RoomMembersItem.tsx | 2 +- .../Threads/components/ThreadListMessage.tsx | 5 +- .../UserInfo/ReportUserModal.tsx | 2 +- .../VideoConfList/VideoConfListItem.tsx | 6 +- .../VideoConfPopup/VideoConfPopupRoomInfo.tsx | 2 +- .../ReadReceiptsModal/ReadReceiptRow.tsx | 2 +- .../contextualBar/TeamAutocomplete/Avatar.js | 10 --- .../TeamAutocomplete/TeamAutocomplete.tsx | 11 ++-- .../RoomsAvailableForTeamsAutoComplete.tsx | 2 +- .../channels/TeamsChannelItem.js | 2 +- .../teams/contextualBar/info/TeamsInfo.tsx | 2 +- .../client/omnichannel/ContactManagerInfo.js | 2 +- .../cannedResponses/CannedResponsesTable.tsx | 2 +- .../DeviceManagementInfo.tsx | 2 +- .../views/audit/components/AuditLogEntry.tsx | 2 +- .../components/forms/RoomAutoComplete.tsx | 3 +- apps/meteor/package.json | 1 + packages/fuselage-ui-kit/package.json | 2 + packages/ui-avatar/.eslintignore | 1 + packages/ui-avatar/.eslintrc.json | 65 +++++++++++++++++++ packages/ui-avatar/package.json | 40 ++++++++++++ .../ui-avatar/src/components/AppAvatar.tsx | 12 ++++ .../ui-avatar/src/components/BaseAvatar.tsx | 30 +++++++++ .../src/components}/MessageAvatar.tsx | 17 ++--- .../ui-avatar/src/components/RoomAvatar.tsx | 24 +++++++ .../ui-avatar/src/components}/UserAvatar.tsx | 2 +- packages/ui-avatar/src/index.ts | 5 ++ packages/ui-avatar/tsconfig-build.json | 4 ++ packages/ui-avatar/tsconfig.json | 8 +++ packages/ui-video-conf/package.json | 2 + .../VideoConfMessage.stories.tsx | 2 +- packages/uikit-playground/package.json | 1 + yarn.lock | 54 +++++++++++++++ 86 files changed, 380 insertions(+), 225 deletions(-) delete mode 100644 apps/meteor/client/components/avatar/AppAvatar.tsx delete mode 100644 apps/meteor/client/components/avatar/BaseAvatar.tsx delete mode 100644 apps/meteor/client/components/avatar/RoomAvatar.tsx delete mode 100644 apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/Avatar.js create mode 100644 packages/ui-avatar/.eslintignore create mode 100644 packages/ui-avatar/.eslintrc.json create mode 100644 packages/ui-avatar/package.json create mode 100644 packages/ui-avatar/src/components/AppAvatar.tsx create mode 100644 packages/ui-avatar/src/components/BaseAvatar.tsx rename {apps/meteor/client/components/message/header => packages/ui-avatar/src/components}/MessageAvatar.tsx (54%) create mode 100644 packages/ui-avatar/src/components/RoomAvatar.tsx rename {apps/meteor/client/components/avatar => packages/ui-avatar/src/components}/UserAvatar.tsx (94%) create mode 100644 packages/ui-avatar/src/index.ts create mode 100644 packages/ui-avatar/tsconfig-build.json create mode 100644 packages/ui-avatar/tsconfig.json diff --git a/apps/meteor/client/components/RoomAutoComplete/Avatar.tsx b/apps/meteor/client/components/RoomAutoComplete/Avatar.tsx index d28546428513..1437c4ebbe89 100644 --- a/apps/meteor/client/components/RoomAutoComplete/Avatar.tsx +++ b/apps/meteor/client/components/RoomAutoComplete/Avatar.tsx @@ -1,9 +1,8 @@ import { Options } from '@rocket.chat/fuselage'; +import { RoomAvatar } from '@rocket.chat/ui-avatar'; import type { FC } from 'react'; import React from 'react'; -import RoomAvatar from '../avatar/RoomAvatar'; - type AvatarProps = { value: string; type: string; diff --git a/apps/meteor/client/components/RoomAutoComplete/RoomAutoComplete.tsx b/apps/meteor/client/components/RoomAutoComplete/RoomAutoComplete.tsx index 93509618827c..39fbf9577776 100644 --- a/apps/meteor/client/components/RoomAutoComplete/RoomAutoComplete.tsx +++ b/apps/meteor/client/components/RoomAutoComplete/RoomAutoComplete.tsx @@ -1,11 +1,11 @@ import { AutoComplete, Option, Box } from '@rocket.chat/fuselage'; import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; +import { RoomAvatar } from '@rocket.chat/ui-avatar'; import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import type { ReactElement, ComponentProps } from 'react'; import React, { memo, useMemo, useState } from 'react'; -import RoomAvatar from '../avatar/RoomAvatar'; import Avatar from './Avatar'; const generateQuery = ( diff --git a/apps/meteor/client/components/RoomAutoCompleteMultiple/RoomAutoCompleteMultiple.tsx b/apps/meteor/client/components/RoomAutoCompleteMultiple/RoomAutoCompleteMultiple.tsx index eb0414e7da14..49006e025111 100644 --- a/apps/meteor/client/components/RoomAutoCompleteMultiple/RoomAutoCompleteMultiple.tsx +++ b/apps/meteor/client/components/RoomAutoCompleteMultiple/RoomAutoCompleteMultiple.tsx @@ -1,12 +1,11 @@ import { AutoComplete, Option, Chip, Box, Skeleton } from '@rocket.chat/fuselage'; import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; +import { RoomAvatar } from '@rocket.chat/ui-avatar'; import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import type { ReactElement, ComponentProps } from 'react'; import React, { memo, useMemo, useState } from 'react'; -import RoomAvatar from '../avatar/RoomAvatar'; - const generateQuery = ( term = '', ): { diff --git a/apps/meteor/client/components/UserAndRoomAutoCompleteMultiple/UserAndRoomAutoCompleteMultiple.tsx b/apps/meteor/client/components/UserAndRoomAutoCompleteMultiple/UserAndRoomAutoCompleteMultiple.tsx index f96c198865cc..b8ff0b472fa4 100644 --- a/apps/meteor/client/components/UserAndRoomAutoCompleteMultiple/UserAndRoomAutoCompleteMultiple.tsx +++ b/apps/meteor/client/components/UserAndRoomAutoCompleteMultiple/UserAndRoomAutoCompleteMultiple.tsx @@ -2,13 +2,12 @@ import { isDirectMessageRoom } from '@rocket.chat/core-typings'; import { AutoComplete, Box, Option, OptionAvatar, OptionContent, Chip } from '@rocket.chat/fuselage'; import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; import { escapeRegExp } from '@rocket.chat/string-helpers'; +import { RoomAvatar, UserAvatar } from '@rocket.chat/ui-avatar'; import { useUser, useUserSubscriptions } from '@rocket.chat/ui-contexts'; import type { ComponentProps, ReactElement } from 'react'; import React, { memo, useMemo, useState } from 'react'; import { roomCoordinator } from '../../lib/rooms/roomCoordinator'; -import RoomAvatar from '../avatar/RoomAvatar'; -import UserAvatar from '../avatar/UserAvatar'; type UserAndRoomAutoCompleteMultipleProps = Omit, 'filter'>; diff --git a/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx b/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx index ebe3ecc5ee72..1f12f29ee13b 100644 --- a/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx +++ b/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx @@ -1,12 +1,11 @@ import { AutoComplete, Option, Box, Chip, Options } from '@rocket.chat/fuselage'; import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import type { ComponentProps, ReactElement } from 'react'; import React, { memo, useMemo, useState } from 'react'; -import UserAvatar from '../avatar/UserAvatar'; - const query = ( term = '', conditions = {}, diff --git a/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.tsx b/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.tsx index 857af5e9c43f..f9887d693ca0 100644 --- a/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.tsx +++ b/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.tsx @@ -1,12 +1,11 @@ import { AutoComplete, Box, OptionAvatar, Option, OptionContent, Chip, OptionDescription } from '@rocket.chat/fuselage'; import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import type { ComponentProps, ReactElement } from 'react'; import React, { memo, useMemo, useState } from 'react'; -import UserAvatar from '../avatar/UserAvatar'; - const query = ( term = '', ): { diff --git a/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleFederated.tsx b/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleFederated.tsx index 457593e2c5db..5a372ee39dd2 100644 --- a/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleFederated.tsx +++ b/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleFederated.tsx @@ -1,11 +1,11 @@ import { MultiSelectFiltered, Icon, Box, Chip } from '@rocket.chat/fuselage'; import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import type { ReactElement, AllHTMLAttributes } from 'react'; import React, { memo, useState, useCallback, useMemo } from 'react'; -import UserAvatar from '../avatar/UserAvatar'; import AutocompleteOptions, { OptionsContext } from './UserAutoCompleteMultipleOptions'; type UserAutoCompleteMultipleFederatedProps = { diff --git a/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleOption.tsx b/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleOption.tsx index e6762c188abd..0dc773af9596 100644 --- a/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleOption.tsx +++ b/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleOption.tsx @@ -1,10 +1,9 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Option, OptionDescription } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import type { ReactElement } from 'react'; import React from 'react'; -import UserAvatar from '../avatar/UserAvatar'; - type UserAutoCompleteMultipleOptionProps = { label: { _federated?: boolean; diff --git a/apps/meteor/client/components/UserCard/UserCard.tsx b/apps/meteor/client/components/UserCard/UserCard.tsx index 78dd9a720ac1..2d440427ccbd 100644 --- a/apps/meteor/client/components/UserCard/UserCard.tsx +++ b/apps/meteor/client/components/UserCard/UserCard.tsx @@ -1,5 +1,6 @@ import { css } from '@rocket.chat/css-in-js'; import { Box, Button, IconButton } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactNode, ComponentProps } from 'react'; import React, { forwardRef } from 'react'; @@ -7,7 +8,6 @@ import React, { forwardRef } from 'react'; import { useEmbeddedLayout } from '../../hooks/useEmbeddedLayout'; import MarkdownText from '../MarkdownText'; import * as Status from '../UserStatus'; -import UserAvatar from '../avatar/UserAvatar'; import UserCardActions from './UserCardActions'; import UserCardContainer from './UserCardContainer'; import UserCardInfo from './UserCardInfo'; diff --git a/apps/meteor/client/components/UserInfo/UserInfoAvatar.tsx b/apps/meteor/client/components/UserInfo/UserInfoAvatar.tsx index 2fbe4a05ed55..c15992489264 100644 --- a/apps/meteor/client/components/UserInfo/UserInfoAvatar.tsx +++ b/apps/meteor/client/components/UserInfo/UserInfoAvatar.tsx @@ -1,8 +1,7 @@ +import { UserAvatar } from '@rocket.chat/ui-avatar'; import type { ComponentProps, ReactElement } from 'react'; import React from 'react'; -import UserAvatar from '../avatar/UserAvatar'; - const UserInfoAvatar = ({ username, ...props }: ComponentProps): ReactElement => ( ); diff --git a/apps/meteor/client/components/avatar/AppAvatar.tsx b/apps/meteor/client/components/avatar/AppAvatar.tsx deleted file mode 100644 index c146eb4b10fc..000000000000 --- a/apps/meteor/client/components/avatar/AppAvatar.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Box } from '@rocket.chat/fuselage'; -import type { ComponentProps, ReactElement } from 'react'; -import React from 'react'; - -import BaseAvatar from './BaseAvatar'; - -type AppAvatarProps = { - iconFileContent: string; - iconFileData: string; - size: ComponentProps['size']; -} & ComponentProps; - -export default function AppAvatar({ iconFileContent, size, iconFileData, ...props }: AppAvatarProps): ReactElement { - return ( - - - - ); -} diff --git a/apps/meteor/client/components/avatar/BaseAvatar.tsx b/apps/meteor/client/components/avatar/BaseAvatar.tsx deleted file mode 100644 index d32f96f57881..000000000000 --- a/apps/meteor/client/components/avatar/BaseAvatar.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import type { AvatarProps } from '@rocket.chat/fuselage'; -import { Avatar, Skeleton } from '@rocket.chat/fuselage'; -import type { FC } from 'react'; -import React, { useState } from 'react'; - -export type BaseAvatarProps = Omit; - -const BaseAvatar: FC = ({ onError, ...props }) => { - const [isLoading, setIsLoading] = useState(false); - - if (isLoading) { - return ; - } - - return ( - { - setIsLoading(true); - onError?.(event); - }} - {...props} - /> - ); -}; - -export default BaseAvatar; diff --git a/apps/meteor/client/components/avatar/RoomAvatar.tsx b/apps/meteor/client/components/avatar/RoomAvatar.tsx deleted file mode 100644 index bf3c96509046..000000000000 --- a/apps/meteor/client/components/avatar/RoomAvatar.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { useRoomAvatarPath } from '@rocket.chat/ui-contexts'; -import type { ReactElement } from 'react'; -import React, { memo } from 'react'; - -import BaseAvatar from './BaseAvatar'; - -// TODO: frontend chapter day - Remove inline Styling - -type RoomAvatarProps = { - /* @deprecated */ - size?: 'x16' | 'x20' | 'x28' | 'x36' | 'x40' | 'x124' | 'x332'; - /* @deprecated */ - url?: string; - - room: { - _id: string; - type?: string; - t?: string; - avatarETag?: string; - }; -}; - -const RoomAvatar = function RoomAvatar({ room, ...rest }: RoomAvatarProps): ReactElement { - const getRoomPathAvatar = useRoomAvatarPath(); - const { url = getRoomPathAvatar(room), ...props } = rest; - return ; -}; - -export default memo(RoomAvatar); diff --git a/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx b/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx index 4a780e4ab173..de4108d8f866 100644 --- a/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx +++ b/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx @@ -3,6 +3,7 @@ import type { IRoom, RoomAdminFieldsType } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; import { Box, Button, ButtonGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { RoomAvatar } from '@rocket.chat/ui-avatar'; import { useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useEffect } from 'react'; @@ -10,7 +11,6 @@ import React, { useEffect } from 'react'; import { getAvatarURL } from '../../../app/utils/client/getAvatarURL'; import { useSingleFileInput } from '../../hooks/useSingleFileInput'; import { isValidImageFormat } from '../../lib/utils/isValidImageFormat'; -import RoomAvatar from './RoomAvatar'; type RoomAvatarEditorProps = { room: Pick; diff --git a/apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarEditor.tsx b/apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarEditor.tsx index eda44e98d87f..05d96d9536be 100644 --- a/apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarEditor.tsx +++ b/apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarEditor.tsx @@ -1,13 +1,13 @@ import type { IUser, AvatarObject } from '@rocket.chat/core-typings'; -import { Box, Button, TextInput, Avatar, IconButton, Label } from '@rocket.chat/fuselage'; +import { Box, Button, Avatar, TextInput, IconButton, Label } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useToastMessageDispatch, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement, ChangeEvent } from 'react'; import React, { useState, useCallback } from 'react'; import { useSingleFileInput } from '../../../hooks/useSingleFileInput'; import { isValidImageFormat } from '../../../lib/utils/isValidImageFormat'; -import UserAvatar from '../UserAvatar'; import type { UserAvatarSuggestion } from './UserAvatarSuggestion'; import UserAvatarSuggestions from './UserAvatarSuggestions'; import { readFileAsDataURL } from './readFileAsDataURL'; @@ -81,11 +81,11 @@ function UserAvatarEditor({ currentUsername, username, setAvatarObj, disabled, e data-qa-id='UserAvatarEditor' username={currentUsername || ''} etag={etag} - onError={() => dispatchToastMessage({ type: 'error', message: t('error-invalid-image-url') })} style={{ - objectFit: 'contain', imageOrientation: rotateImages ? 'from-image' : 'none', + objectFit: 'contain', }} + onError={() => dispatchToastMessage({ type: 'error', message: t('error-invalid-image-url') })} /> diff --git a/apps/meteor/client/components/message/content/attachments/structure/AttachmentAuthorAvatar.tsx b/apps/meteor/client/components/message/content/attachments/structure/AttachmentAuthorAvatar.tsx index 22e1f107b027..fb0b3e448f12 100644 --- a/apps/meteor/client/components/message/content/attachments/structure/AttachmentAuthorAvatar.tsx +++ b/apps/meteor/client/components/message/content/attachments/structure/AttachmentAuthorAvatar.tsx @@ -1,8 +1,7 @@ +import { BaseAvatar } from '@rocket.chat/ui-avatar'; import type { ReactElement } from 'react'; import React from 'react'; -import BaseAvatar from '../../../../avatar/BaseAvatar'; - const AttachmentAuthorAvatar = ({ url }: { url: string }): ReactElement => ; export default AttachmentAuthorAvatar; diff --git a/apps/meteor/client/components/message/variants/RoomMessage.tsx b/apps/meteor/client/components/message/variants/RoomMessage.tsx index b38b7a08cff5..ff4b082ae1ee 100644 --- a/apps/meteor/client/components/message/variants/RoomMessage.tsx +++ b/apps/meteor/client/components/message/variants/RoomMessage.tsx @@ -1,6 +1,7 @@ import type { IMessage } from '@rocket.chat/core-typings'; import { Message, MessageLeftContainer, MessageContainer, CheckBox } from '@rocket.chat/fuselage'; import { useToggle } from '@rocket.chat/fuselage-hooks'; +import { MessageAvatar } from '@rocket.chat/ui-avatar'; import { useUserId } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useRef, memo } from 'react'; @@ -15,11 +16,11 @@ import { } from '../../../views/room/MessageList/contexts/SelectedMessagesContext'; import { useJumpToMessage } from '../../../views/room/MessageList/hooks/useJumpToMessage'; import { useChat } from '../../../views/room/contexts/ChatContext'; +import Emoji from '../../Emoji'; import IgnoredContent from '../IgnoredContent'; import MessageHeader from '../MessageHeader'; import MessageToolbarHolder from '../MessageToolbarHolder'; import StatusIndicators from '../StatusIndicators'; -import MessageAvatar from '../header/MessageAvatar'; import RoomMessageContent from './room/RoomMessageContent'; type RoomMessageProps = { @@ -81,7 +82,7 @@ const RoomMessage = ({ {!sequential && message.u.username && !selecting && showUserAvatar && ( : undefined} avatarUrl={message.avatar} username={message.u.username} size='x36' diff --git a/apps/meteor/client/components/message/variants/SystemMessage.tsx b/apps/meteor/client/components/message/variants/SystemMessage.tsx index 2936abbd1c39..bb0a198061fc 100644 --- a/apps/meteor/client/components/message/variants/SystemMessage.tsx +++ b/apps/meteor/client/components/message/variants/SystemMessage.tsx @@ -11,6 +11,7 @@ import { MessageUsername, MessageNameContainer, } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -29,7 +30,6 @@ import { useCountSelected, } from '../../../views/room/MessageList/contexts/SelectedMessagesContext'; import { useChat } from '../../../views/room/contexts/ChatContext'; -import UserAvatar from '../../avatar/UserAvatar'; import Attachments from '../content/Attachments'; import MessageActions from '../content/MessageActions'; import { useMessageListShowRealName, useMessageListShowUsername } from '../list/MessageListContext'; diff --git a/apps/meteor/client/components/message/variants/ThreadMessage.tsx b/apps/meteor/client/components/message/variants/ThreadMessage.tsx index f9a4b385ac01..343b56dc54a8 100644 --- a/apps/meteor/client/components/message/variants/ThreadMessage.tsx +++ b/apps/meteor/client/components/message/variants/ThreadMessage.tsx @@ -1,6 +1,7 @@ import { type IThreadMessage, type IThreadMainMessage, isVideoConfMessage } from '@rocket.chat/core-typings'; import { Message, MessageLeftContainer, MessageContainer } from '@rocket.chat/fuselage'; import { useToggle } from '@rocket.chat/fuselage-hooks'; +import { MessageAvatar } from '@rocket.chat/ui-avatar'; import { useUserId } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { memo, useRef } from 'react'; @@ -9,11 +10,11 @@ import type { MessageActionContext } from '../../../../app/ui-utils/client/lib/M import { useIsMessageHighlight } from '../../../views/room/MessageList/contexts/MessageHighlightContext'; import { useJumpToMessage } from '../../../views/room/MessageList/hooks/useJumpToMessage'; import { useChat } from '../../../views/room/contexts/ChatContext'; +import Emoji from '../../Emoji'; import IgnoredContent from '../IgnoredContent'; import MessageHeader from '../MessageHeader'; import MessageToolbarHolder from '../MessageToolbarHolder'; import StatusIndicators from '../StatusIndicators'; -import MessageAvatar from '../header/MessageAvatar'; import ThreadMessageContent from './thread/ThreadMessageContent'; type ThreadMessageProps = { @@ -54,7 +55,7 @@ const ThreadMessage = ({ message, sequential, unread, showUserAvatar }: ThreadMe {!sequential && message.u.username && showUserAvatar && ( : undefined} avatarUrl={message.avatar} username={message.u.username} size='x36' diff --git a/apps/meteor/client/components/message/variants/ThreadMessagePreview.tsx b/apps/meteor/client/components/message/variants/ThreadMessagePreview.tsx index eb6fe03d9d0f..0aa8efc76402 100644 --- a/apps/meteor/client/components/message/variants/ThreadMessagePreview.tsx +++ b/apps/meteor/client/components/message/variants/ThreadMessagePreview.tsx @@ -12,6 +12,7 @@ import { CheckBox, MessageStatusIndicatorItem, } from '@rocket.chat/fuselage'; +import { MessageAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { memo } from 'react'; @@ -27,7 +28,7 @@ import { useMessageBody } from '../../../views/room/MessageList/hooks/useMessage import { useParentMessage } from '../../../views/room/MessageList/hooks/useParentMessage'; import { isParsedMessage } from '../../../views/room/MessageList/lib/isParsedMessage'; import { useGoToThread } from '../../../views/room/hooks/useGoToThread'; -import MessageAvatar from '../header/MessageAvatar'; +import Emoji from '../../Emoji'; import { useShowTranslated } from '../list/MessageListContext'; import ThreadMessagePreviewBody from './threadPreview/ThreadMessagePreviewBody'; @@ -101,7 +102,13 @@ const ThreadMessagePreview = ({ message, showUserAvatar, sequential, ...props }: )} goToThread({ rid: message.rid, tmid: message.tmid, msg: message._id }) : undefined}> - {!isSelecting && showUserAvatar && } + {!isSelecting && showUserAvatar && ( + : undefined} + username={message.u.username} + size='x18' + /> + )} {isSelecting && } diff --git a/apps/meteor/client/sidebar/Item/Condensed.stories.tsx b/apps/meteor/client/sidebar/Item/Condensed.stories.tsx index 61e392e77e2e..f63893a30a81 100644 --- a/apps/meteor/client/sidebar/Item/Condensed.stories.tsx +++ b/apps/meteor/client/sidebar/Item/Condensed.stories.tsx @@ -1,10 +1,10 @@ import { Box, IconButton } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { action } from '@storybook/addon-actions'; import type { ComponentMeta, ComponentStory } from '@storybook/react'; import React from 'react'; import * as Status from '../../components/UserStatus'; -import UserAvatar from '../../components/avatar/UserAvatar'; import Condensed from './Condensed'; export default { diff --git a/apps/meteor/client/sidebar/Item/Extended.stories.tsx b/apps/meteor/client/sidebar/Item/Extended.stories.tsx index 7e2b79ad1d6f..a6392eae5d61 100644 --- a/apps/meteor/client/sidebar/Item/Extended.stories.tsx +++ b/apps/meteor/client/sidebar/Item/Extended.stories.tsx @@ -1,10 +1,10 @@ import { Box, IconButton, Badge } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { action } from '@storybook/addon-actions'; import type { ComponentMeta, ComponentStory } from '@storybook/react'; import React from 'react'; import * as Status from '../../components/UserStatus'; -import UserAvatar from '../../components/avatar/UserAvatar'; import Extended from './Extended'; export default { diff --git a/apps/meteor/client/sidebar/Item/Medium.stories.tsx b/apps/meteor/client/sidebar/Item/Medium.stories.tsx index 823244047c51..0c03cf33c500 100644 --- a/apps/meteor/client/sidebar/Item/Medium.stories.tsx +++ b/apps/meteor/client/sidebar/Item/Medium.stories.tsx @@ -1,10 +1,10 @@ import { Box, IconButton } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { action } from '@storybook/addon-actions'; import type { ComponentMeta, ComponentStory } from '@storybook/react'; import React from 'react'; import * as Status from '../../components/UserStatus'; -import UserAvatar from '../../components/avatar/UserAvatar'; import Medium from './Medium'; export default { diff --git a/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx b/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx index f962125f681c..6fa08f5e0253 100644 --- a/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx +++ b/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx @@ -1,10 +1,10 @@ import { css } from '@rocket.chat/css-in-js'; import { Box } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useSetting, useUser, useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; import { UserStatus } from '../../components/UserStatus'; -import UserAvatar from '../../components/avatar/UserAvatar'; const anon = { _id: '', diff --git a/apps/meteor/client/sidebar/header/UserMenuHeader.tsx b/apps/meteor/client/sidebar/header/UserMenuHeader.tsx index 515d4153576d..511b2c2a1e45 100644 --- a/apps/meteor/client/sidebar/header/UserMenuHeader.tsx +++ b/apps/meteor/client/sidebar/header/UserMenuHeader.tsx @@ -1,11 +1,11 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Box, Margins } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; 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 }) => { diff --git a/apps/meteor/client/sidebar/hooks/useAvatarTemplate.tsx b/apps/meteor/client/sidebar/hooks/useAvatarTemplate.tsx index 5bb49da37418..9fd1023a32e7 100644 --- a/apps/meteor/client/sidebar/hooks/useAvatarTemplate.tsx +++ b/apps/meteor/client/sidebar/hooks/useAvatarTemplate.tsx @@ -1,10 +1,9 @@ import type { IRoom } from '@rocket.chat/core-typings'; +import { RoomAvatar } from '@rocket.chat/ui-avatar'; import { useUserPreference } from '@rocket.chat/ui-contexts'; import type { ComponentType } from 'react'; import React, { useMemo } from 'react'; -import RoomAvatar from '../../components/avatar/RoomAvatar'; - export const useAvatarTemplate = ( sidebarViewMode?: 'extended' | 'medium' | 'condensed', sidebarDisplayAvatar?: boolean, diff --git a/apps/meteor/client/views/admin/moderation/helpers/ContextMessage.tsx b/apps/meteor/client/views/admin/moderation/helpers/ContextMessage.tsx index c835bb0ad22e..51a2be087276 100644 --- a/apps/meteor/client/views/admin/moderation/helpers/ContextMessage.tsx +++ b/apps/meteor/client/views/admin/moderation/helpers/ContextMessage.tsx @@ -1,10 +1,10 @@ import type { IMessage, MessageReport } from '@rocket.chat/core-typings'; import { isE2EEMessage } from '@rocket.chat/core-typings'; import { Message, MessageName, MessageToolbarItem, MessageToolbarWrapper, MessageUsername } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; -import UserAvatar from '../../../../components/avatar/UserAvatar'; import MessageContentBody from '../../../../components/message/MessageContentBody'; import Attachments from '../../../../components/message/content/Attachments'; import UiKitMessageBlock from '../../../../components/message/uikit/UiKitMessageBlock'; diff --git a/apps/meteor/client/views/admin/moderation/helpers/UserColumn.tsx b/apps/meteor/client/views/admin/moderation/helpers/UserColumn.tsx index b6289fdff9b8..baaddc1cb6c2 100644 --- a/apps/meteor/client/views/admin/moderation/helpers/UserColumn.tsx +++ b/apps/meteor/client/views/admin/moderation/helpers/UserColumn.tsx @@ -1,14 +1,14 @@ import { Box } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; +import type { ComponentProps } from 'react'; import React from 'react'; -import UserAvatar from '../../../../components/avatar/UserAvatar'; - type UserColumnProps = { name?: string; username?: string; isDesktopOrLarger?: boolean; isProfile?: boolean; - size?: string; + size: ComponentProps['size']; fontSize?: string; }; @@ -17,7 +17,7 @@ const UserColumn = ({ name, username, fontSize, size }: UserColumnProps) => { {username && ( - + )} 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 1fbce402a21b..ea4b999d694e 100644 --- a/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTableRow.tsx +++ b/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTableRow.tsx @@ -1,12 +1,12 @@ import type { IUserInRole } from '@rocket.chat/core-typings'; import { Box, Button, Icon } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import type { ReactElement } from 'react'; import React, { memo } from 'react'; import { getUserEmailAddress } from '../../../../../../lib/getUserEmailAddress'; import { GenericTableRow, GenericTableCell } from '../../../../../components/GenericTable'; -import UserAvatar from '../../../../../components/avatar/UserAvatar'; type UsersInRoleTableRowProps = { user: IUserInRole; diff --git a/apps/meteor/client/views/admin/rooms/RoomRow.tsx b/apps/meteor/client/views/admin/rooms/RoomRow.tsx index 2c0dbb8c31a6..73a30e647764 100644 --- a/apps/meteor/client/views/admin/rooms/RoomRow.tsx +++ b/apps/meteor/client/views/admin/rooms/RoomRow.tsx @@ -2,11 +2,11 @@ import { isDiscussion } from '@rocket.chat/core-typings'; import type { IRoom, RoomAdminFieldsType } from '@rocket.chat/core-typings'; import { Box, Icon } from '@rocket.chat/fuselage'; import { useMediaQuery } from '@rocket.chat/fuselage-hooks'; +import { RoomAvatar } from '@rocket.chat/ui-avatar'; import { useRouter, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useCallback } from 'react'; import { GenericTableCell, GenericTableRow } from '../../../components/GenericTable'; -import RoomAvatar from '../../../components/avatar/RoomAvatar'; import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; const roomTypeI18nMap = { 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 857fd034c91e..7494a249818c 100644 --- a/apps/meteor/client/views/admin/settings/groups/voip/VoipExtensionsPage.tsx +++ b/apps/meteor/client/views/admin/settings/groups/voip/VoipExtensionsPage.tsx @@ -1,4 +1,5 @@ import { Box, Chip, Button, Pagination } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useSetModal, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import React, { useMemo } from 'react'; @@ -15,7 +16,6 @@ import { } from '../../../../../components/GenericTable'; import { usePagination } from '../../../../../components/GenericTable/hooks/usePagination'; import { PageContent } from '../../../../../components/Page'; -import UserAvatar from '../../../../../components/avatar/UserAvatar'; import AssignAgentButton from './AssignAgentButton'; import AssignAgentModal from './AssignAgentModal'; import RemoveAgentButton from './RemoveAgentButton'; diff --git a/apps/meteor/client/views/admin/users/UsersTable/UsersTableRow.tsx b/apps/meteor/client/views/admin/users/UsersTable/UsersTableRow.tsx index 2572b7446c42..4010b118e57d 100644 --- a/apps/meteor/client/views/admin/users/UsersTable/UsersTableRow.tsx +++ b/apps/meteor/client/views/admin/users/UsersTable/UsersTableRow.tsx @@ -1,6 +1,7 @@ import type { IRole, IUser } from '@rocket.chat/core-typings'; import { Box } from '@rocket.chat/fuselage'; import { capitalize } from '@rocket.chat/string-helpers'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -8,7 +9,6 @@ import React from 'react'; import { Roles } from '../../../../../app/models/client'; import { GenericTableRow, GenericTableCell } from '../../../../components/GenericTable'; -import UserAvatar from '../../../../components/avatar/UserAvatar'; type UsersTableRowProps = { user: Pick; diff --git a/apps/meteor/client/views/directory/tabs/users/UsersTable/UsersTableRow.tsx b/apps/meteor/client/views/directory/tabs/users/UsersTable/UsersTableRow.tsx index 976bb95ba926..864b0657b6e4 100644 --- a/apps/meteor/client/views/directory/tabs/users/UsersTable/UsersTableRow.tsx +++ b/apps/meteor/client/views/directory/tabs/users/UsersTable/UsersTableRow.tsx @@ -1,10 +1,10 @@ import type { IUser, Serialized } from '@rocket.chat/core-typings'; import { Box, Flex } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import React from 'react'; import { GenericTableRow, GenericTableCell } from '../../../../../components/GenericTable'; import MarkdownText from '../../../../../components/MarkdownText'; -import UserAvatar from '../../../../../components/avatar/UserAvatar'; import { useFormatDate } from '../../../../../hooks/useFormatDate'; type UsersTableRowProps = { diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPageHeader.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPageHeader.tsx index ef0c7259fddd..a4a65f061252 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPageHeader.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPageHeader.tsx @@ -1,11 +1,11 @@ import type { App } from '@rocket.chat/core-typings'; import { Box, Tag } from '@rocket.chat/fuselage'; +import { AppAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation } from '@rocket.chat/ui-contexts'; import moment from 'moment'; import type { ReactElement } from 'react'; import React from 'react'; -import AppAvatar from '../../../components/avatar/AppAvatar'; import AppMenu from '../AppMenu'; import BundleChips from '../BundleChips'; import { appIncompatibleStatusProps } from '../helpers'; @@ -37,7 +37,9 @@ const AppDetailsPageHeader = ({ app }: { app: App }): ReactElement => { return ( - + + + diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequestItem.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequestItem.tsx index 4cdba9f22360..78be4577940c 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequestItem.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequestItem.tsx @@ -1,9 +1,9 @@ import { Badge, Box } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { usePermission } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; -import UserAvatar from '../../../../../components/avatar/UserAvatar'; import { useFormatDateAndTime } from '../../../../../hooks/useFormatDateAndTime'; type AppRequestItemProps = { diff --git a/apps/meteor/client/views/marketplace/AppsList/AppRow.tsx b/apps/meteor/client/views/marketplace/AppsList/AppRow.tsx index dffa23d7a2c8..33e54edd90bb 100644 --- a/apps/meteor/client/views/marketplace/AppsList/AppRow.tsx +++ b/apps/meteor/client/views/marketplace/AppsList/AppRow.tsx @@ -1,11 +1,11 @@ import type { App } from '@rocket.chat/core-typings'; import { Badge, Card, CardBody, CardCol, CardControls, CardHeader, CardRow, CardTitle } from '@rocket.chat/fuselage'; +import { AppAvatar } from '@rocket.chat/ui-avatar'; import { useRouteParameter, useRouter } from '@rocket.chat/ui-contexts'; import type { KeyboardEvent, MouseEvent, ReactElement } from 'react'; import React, { memo } from 'react'; import semver from 'semver'; -import AppAvatar from '../../../components/avatar/AppAvatar'; import AppStatus from '../AppDetailsPage/tabs/AppStatus/AppStatus'; import AppMenu from '../AppMenu'; import BundleChips from '../BundleChips'; diff --git a/apps/meteor/client/views/meet/CallPage.tsx b/apps/meteor/client/views/meet/CallPage.tsx index 01c4bdc0de0c..87188221c091 100644 --- a/apps/meteor/client/views/meet/CallPage.tsx +++ b/apps/meteor/client/views/meet/CallPage.tsx @@ -1,4 +1,5 @@ import { Box, Flex, ButtonGroup, Button, Icon } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation, useStream } from '@rocket.chat/ui-contexts'; import moment from 'moment'; import type { FC } from 'react'; @@ -7,7 +8,6 @@ import React, { useEffect, useState } from 'react'; import { sdk } from '../../../app/utils/client/lib/SDKClient'; import { WebRTC } from '../../../app/webrtc/client'; import { WEB_RTC_EVENTS } from '../../../app/webrtc/lib/constants'; -import UserAvatar from '../../components/avatar/UserAvatar'; import OngoingCallDuration from './OngoingCallDuration'; import './styles.css'; @@ -217,15 +217,14 @@ const CallPage: FC = ({ display: isCameraOn ? 'block' : 'none', }} > - + > + + = ({ top: isRemoteMobileDevice || isLocalMobileDevice ? '10%' : '30%', }} > - + > + + + @@ -336,15 +335,14 @@ const CallPage: FC = ({ backgroundColor='dark' alignItems='center' > - + > + + = ({ }} alignItems='center' > - + > + + Calling... diff --git a/apps/meteor/client/views/meet/MeetPage.tsx b/apps/meteor/client/views/meet/MeetPage.tsx index 8dc27d5ef84e..e7a34b78fd1d 100644 --- a/apps/meteor/client/views/meet/MeetPage.tsx +++ b/apps/meteor/client/views/meet/MeetPage.tsx @@ -1,10 +1,10 @@ import { Button, Box, Flex } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useRouteParameter, useSearchParameter, useTranslation } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import React, { useEffect, useState, useCallback } from 'react'; import { sdk } from '../../../app/utils/client/lib/SDKClient'; -import UserAvatar from '../../components/avatar/UserAvatar'; import { useEmbeddedLayout } from '../../hooks/useEmbeddedLayout'; import NotFoundPage from '../notFound/NotFoundPage'; import PageLoading from '../root/PageLoading'; @@ -90,15 +90,14 @@ const MeetPage = () => { backgroundColor='dark' alignItems='center' > - + > + + { }} alignItems='center' > - + > + +

Call Ended!

- - {username && } - + {username && } {username} diff --git a/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx b/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx index f5dfa94adb2b..a1b9ba1ca3af 100644 --- a/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx +++ b/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx @@ -17,12 +17,12 @@ import { MessageSystemBody, MessageSystemTimestamp, } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { FC } from 'react'; import React, { memo } from 'react'; import { getUserDisplayName } from '../../../../../lib/getUserDisplayName'; -import UserAvatar from '../../../../components/avatar/UserAvatar'; import MessageContentBody from '../../../../components/message/MessageContentBody'; import StatusIndicators from '../../../../components/message/StatusIndicators'; import Attachments from '../../../../components/message/content/Attachments'; diff --git a/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/AgentAvatar.tsx b/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/AgentAvatar.tsx index 168b7c007ced..681c3556b845 100644 --- a/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/AgentAvatar.tsx +++ b/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/AgentAvatar.tsx @@ -1,9 +1,8 @@ import { Box } from '@rocket.chat/fuselage'; import { useMediaQuery } from '@rocket.chat/fuselage-hooks'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import React, { memo } from 'react'; -import UserAvatar from '../../../../components/avatar/UserAvatar'; - const AgentAvatar = ({ name, username, eTag }: { name: string; username: string; eTag?: string }) => { const mediaQuery = useMediaQuery('(min-width: 1024px)'); diff --git a/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfo.tsx b/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfo.tsx index 2aff3776f84c..65792b6d73e2 100644 --- a/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfo.tsx +++ b/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfo.tsx @@ -1,5 +1,6 @@ import type { IVoipRoom } from '@rocket.chat/core-typings'; import { Box, Icon, Chip, ButtonGroup } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation } from '@rocket.chat/ui-contexts'; import moment from 'moment'; import type { ReactElement } from 'react'; @@ -16,7 +17,6 @@ import { } from '../../../../../components/Contextualbar'; import InfoPanel from '../../../../../components/InfoPanel'; import { UserStatus } from '../../../../../components/UserStatus'; -import UserAvatar from '../../../../../components/avatar/UserAvatar'; import { useIsCallReady } from '../../../../../contexts/CallContext'; import AgentInfoDetails from '../../../components/AgentInfoDetails'; import AgentField from '../../components/AgentField'; diff --git a/apps/meteor/client/views/omnichannel/directory/components/AgentField.tsx b/apps/meteor/client/views/omnichannel/directory/components/AgentField.tsx index d0e0b91ae4e9..91b21d219ffe 100644 --- a/apps/meteor/client/views/omnichannel/directory/components/AgentField.tsx +++ b/apps/meteor/client/views/omnichannel/directory/components/AgentField.tsx @@ -1,10 +1,10 @@ import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import React from 'react'; import { UserStatus } from '../../../../components/UserStatus'; -import UserAvatar from '../../../../components/avatar/UserAvatar'; import AgentInfoDetails from '../../components/AgentInfoDetails'; import Field from '../../components/Field'; import Info from '../../components/Info'; 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 30d9aeee33da..9578c6a07b83 100644 --- a/apps/meteor/client/views/omnichannel/directory/contacts/contextualBar/ContactInfo.tsx +++ b/apps/meteor/client/views/omnichannel/directory/contacts/contextualBar/ContactInfo.tsx @@ -1,5 +1,6 @@ import { Box, Margins, ButtonGroup, Button, Divider } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; 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'; @@ -10,7 +11,6 @@ import { parseOutboundPhoneNumber } from '../../../../../../ee/client/lib/voip/p import ContactManagerInfo from '../../../../../../ee/client/omnichannel/ContactManagerInfo'; import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../../../components/Contextualbar'; import { UserStatus } from '../../../../../components/UserStatus'; -import UserAvatar from '../../../../../components/avatar/UserAvatar'; import { useIsCallReady } from '../../../../../contexts/CallContext'; import { useFormatDate } from '../../../../../hooks/useFormatDate'; import AgentInfoDetails from '../../../components/AgentInfoDetails'; diff --git a/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx b/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx index 49ecf27d14d3..7662f740c7d3 100644 --- a/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx +++ b/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx @@ -1,5 +1,6 @@ import { Box, Pagination } from '@rocket.chat/fuselage'; import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; import { hashQueryKey, useQuery } from '@tanstack/react-query'; import React, { useMemo, useState } from 'react'; @@ -17,7 +18,6 @@ import { } from '../../../components/GenericTable'; import { usePagination } from '../../../components/GenericTable/hooks/usePagination'; import { useSort } from '../../../components/GenericTable/hooks/useSort'; -import UserAvatar from '../../../components/avatar/UserAvatar'; import AddManager from './AddManager'; import RemoveManagerButton from './RemoveManagerButton'; diff --git a/apps/meteor/client/views/omnichannel/queueList/QueueListTable.tsx b/apps/meteor/client/views/omnichannel/queueList/QueueListTable.tsx index 525611c8237e..b6508658e520 100644 --- a/apps/meteor/client/views/omnichannel/queueList/QueueListTable.tsx +++ b/apps/meteor/client/views/omnichannel/queueList/QueueListTable.tsx @@ -1,6 +1,7 @@ import { UserStatus } from '@rocket.chat/core-typings'; import { Box, Pagination } from '@rocket.chat/fuselage'; import { useMediaQuery } from '@rocket.chat/fuselage-hooks'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import type { ReactElement } from 'react'; @@ -18,7 +19,6 @@ import { } from '../../../components/GenericTable'; import { usePagination } from '../../../components/GenericTable/hooks/usePagination'; import { useSort } from '../../../components/GenericTable/hooks/useSort'; -import UserAvatar from '../../../components/avatar/UserAvatar'; import { QueueListFilter } from './QueueListFilter'; const QueueListTable = (): ReactElement => { diff --git a/apps/meteor/client/views/room/Header/RoomHeader.tsx b/apps/meteor/client/views/room/Header/RoomHeader.tsx index 951305d63f4f..05f80a984982 100644 --- a/apps/meteor/client/views/room/Header/RoomHeader.tsx +++ b/apps/meteor/client/views/room/Header/RoomHeader.tsx @@ -1,11 +1,11 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; +import { RoomAvatar } from '@rocket.chat/ui-avatar'; import { Header, HeaderAvatar, HeaderContent, HeaderContentRow, HeaderSubtitle, HeaderToolbar } from '@rocket.chat/ui-client'; import { useTranslation } from '@rocket.chat/ui-contexts'; import React, { Suspense } from 'react'; import MarkdownText from '../../../components/MarkdownText'; -import RoomAvatar from '../../../components/avatar/RoomAvatar'; import FederatedRoomOriginServer from './FederatedRoomOriginServer'; import ParentRoomWithData from './ParentRoomWithData'; import ParentTeam from './ParentTeam'; diff --git a/apps/meteor/client/views/room/body/LeaderBar.tsx b/apps/meteor/client/views/room/body/LeaderBar.tsx index ff0503971369..6de7c3d52e8e 100644 --- a/apps/meteor/client/views/room/body/LeaderBar.tsx +++ b/apps/meteor/client/views/room/body/LeaderBar.tsx @@ -1,13 +1,13 @@ import type { IUser } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; import { Box, Button } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement, UIEvent } from 'react'; import React, { memo, useCallback, useMemo } from 'react'; import { isTruthy } from '../../../../lib/isTruthy'; import { ReactiveUserStatus } from '../../../components/UserStatus'; -import UserAvatar from '../../../components/avatar/UserAvatar'; import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; type LeaderBarProps = { diff --git a/apps/meteor/client/views/room/body/RoomForeword/RoomForeword.tsx b/apps/meteor/client/views/room/body/RoomForeword/RoomForeword.tsx index b02bbcb48815..63ba8f895e6c 100644 --- a/apps/meteor/client/views/room/body/RoomForeword/RoomForeword.tsx +++ b/apps/meteor/client/views/room/body/RoomForeword/RoomForeword.tsx @@ -1,11 +1,11 @@ import type { IRoom, IUser } from '@rocket.chat/core-typings'; import { isVoipRoom, isDirectMessageRoom } from '@rocket.chat/core-typings'; import { Flex, Box } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; -import UserAvatar from '../../../../components/avatar/UserAvatar'; import { VoipRoomForeword } from '../../../../components/voip/room/VoipRoomForeword'; import RoomForewordUsernameList from './RoomForewordUsernameList'; diff --git a/apps/meteor/client/views/room/composer/ComposerBoxPopupUser.tsx b/apps/meteor/client/views/room/composer/ComposerBoxPopupUser.tsx index 9a9e5f1bfda0..a32168799158 100644 --- a/apps/meteor/client/views/room/composer/ComposerBoxPopupUser.tsx +++ b/apps/meteor/client/views/room/composer/ComposerBoxPopupUser.tsx @@ -1,9 +1,9 @@ import { OptionAvatar, OptionColumn, OptionContent, OptionInput } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; import ReactiveUserStatus from '../../../components/UserStatus/ReactiveUserStatus'; -import UserAvatar from '../../../components/avatar/UserAvatar'; export type ComposerBoxPopupUserProps = { _id: string; diff --git a/apps/meteor/client/views/room/contextualBar/Discussions/components/DiscussionsListItem.tsx b/apps/meteor/client/views/room/contextualBar/Discussions/components/DiscussionsListItem.tsx index 6253ab9e3fac..6fabbaff4a1a 100644 --- a/apps/meteor/client/views/room/contextualBar/Discussions/components/DiscussionsListItem.tsx +++ b/apps/meteor/client/views/room/contextualBar/Discussions/components/DiscussionsListItem.tsx @@ -1,10 +1,11 @@ import type { IDiscussionMessage } from '@rocket.chat/core-typings'; import { Box, Message } from '@rocket.chat/fuselage'; +import { MessageAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ComponentProps, ReactElement, ReactNode } from 'react'; import React, { memo } from 'react'; -import MessageAvatar from '../../../../../components/message/header/MessageAvatar'; +import Emoji from '../../../../../components/Emoji'; import { clickableItem } from '../../../../../lib/clickableItem'; type DiscussionListItemProps = { @@ -36,7 +37,7 @@ const DiscussionListItem = ({ return ( - + : undefined} username={username} size='x36' /> 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 7828363227ac..38a7593937ff 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx +++ b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx @@ -1,5 +1,6 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { Box, Callout, Menu, Option } from '@rocket.chat/fuselage'; +import { RoomAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation } from '@rocket.chat/ui-contexts'; import React, { useMemo } from 'react'; @@ -14,7 +15,6 @@ import { import InfoPanel from '../../../../../components/InfoPanel'; import RetentionPolicyCallout from '../../../../../components/InfoPanel/RetentionPolicyCallout'; import MarkdownText from '../../../../../components/MarkdownText'; -import RoomAvatar from '../../../../../components/avatar/RoomAvatar'; import type { Action } from '../../../../hooks/useActionSpread'; import { useActionSpread } from '../../../../hooks/useActionSpread'; import { useRetentionPolicy } from '../../../body/hooks/useRetentionPolicy'; diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx index 4fa3c48039e4..8b4d5ad8cb52 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx @@ -11,12 +11,12 @@ import { OptionSkeleton, } from '@rocket.chat/fuselage'; import { usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import type { ReactElement, MouseEvent } from 'react'; import React, { useState } from 'react'; import { getUserDisplayNames } from '../../../../../lib/getUserDisplayNames'; import { ReactiveUserStatus } from '../../../../components/UserStatus'; -import UserAvatar from '../../../../components/avatar/UserAvatar'; import { usePreventPropagation } from '../../../../hooks/usePreventPropagation'; import UserActions from './RoomMembersActions'; diff --git a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadListMessage.tsx b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadListMessage.tsx index d7d7469f2adb..03a80c496028 100644 --- a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadListMessage.tsx +++ b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadListMessage.tsx @@ -1,10 +1,11 @@ import type { IMessage } from '@rocket.chat/core-typings'; import { Message, Box, IconButton } from '@rocket.chat/fuselage'; +import { MessageAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ComponentProps, MouseEventHandler, ReactElement, ReactNode } from 'react'; import React, { memo } from 'react'; -import MessageAvatar from '../../../../../components/message/header/MessageAvatar'; +import Emoji from '../../../../../components/Emoji'; import { followStyle, anchor } from '../../../../../components/message/helpers/followSyle'; import AllMentionNotification from '../../../../../components/message/notification/AllMentionNotification'; import MeMentionNotification from '../../../../../components/message/notification/MeMentionNotification'; @@ -55,7 +56,7 @@ const ThreadListMessage = ({ - + : undefined} username={username} size='x36' /> diff --git a/apps/meteor/client/views/room/contextualBar/UserInfo/ReportUserModal.tsx b/apps/meteor/client/views/room/contextualBar/UserInfo/ReportUserModal.tsx index cb28cf2b7ba8..d62ebb96befa 100644 --- a/apps/meteor/client/views/room/contextualBar/UserInfo/ReportUserModal.tsx +++ b/apps/meteor/client/views/room/contextualBar/UserInfo/ReportUserModal.tsx @@ -1,11 +1,11 @@ import { Box, FieldGroup, Field, FieldLabel, FieldRow, FieldError, TextAreaInput } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import type { ComponentProps } from 'react'; import React from 'react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import GenericModal from '../../../../components/GenericModal/GenericModal'; -import UserAvatar from '../../../../components/avatar/UserAvatar'; type ReportUserModalProps = { onConfirm: (description: string) => void; diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListItem.tsx b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListItem.tsx index 41cabe61bf90..0e155bc2b0b6 100644 --- a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListItem.tsx +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListItem.tsx @@ -2,11 +2,11 @@ import type { IGroupVideoConference } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; import { Button, Message, Box, Avatar, Palette } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation, useSetting } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; -import UserAvatar from '../../../../../components/avatar/UserAvatar'; import { useVideoConfJoinCall } from '../../../../../contexts/VideoConfContext'; import { useTimeAgo } from '../../../../../hooks/useTimeAgo'; import { VIDEOCONF_STACK_MAX_USERS } from '../../../../../lib/constants'; @@ -61,9 +61,7 @@ const VideoConfListItem = ({ pb={8} > - - {username && } - + {username && } {showRealName ? name : username} diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/VideoConfPopupRoomInfo.tsx b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/VideoConfPopupRoomInfo.tsx index e8a7160e07e5..3d1f52746510 100644 --- a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/VideoConfPopupRoomInfo.tsx +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/VideoConfPopupRoomInfo.tsx @@ -1,5 +1,6 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { isDirectMessageRoom, isMultipleDirectMessageRoom } from '@rocket.chat/core-typings'; +import { RoomAvatar } from '@rocket.chat/ui-avatar'; import { useUser, useUserSubscription } from '@rocket.chat/ui-contexts'; import { VideoConfPopupInfo } from '@rocket.chat/ui-video-conf'; import type { ReactElement } from 'react'; @@ -7,7 +8,6 @@ import React from 'react'; import { RoomIcon } from '../../../../../../components/RoomIcon'; import ReactiveUserStatus from '../../../../../../components/UserStatus/ReactiveUserStatus'; -import RoomAvatar from '../../../../../../components/avatar/RoomAvatar'; import { useUserDisplayName } from '../../../../../../hooks/useUserDisplayName'; const VideoConfPopupRoomInfo = ({ room }: { room: IRoom }): ReactElement => { diff --git a/apps/meteor/client/views/room/modals/ReadReceiptsModal/ReadReceiptRow.tsx b/apps/meteor/client/views/room/modals/ReadReceiptsModal/ReadReceiptRow.tsx index 81df1d8adf7c..81748dab2dcd 100644 --- a/apps/meteor/client/views/room/modals/ReadReceiptsModal/ReadReceiptRow.tsx +++ b/apps/meteor/client/views/room/modals/ReadReceiptsModal/ReadReceiptRow.tsx @@ -1,10 +1,10 @@ import type { ReadReceipt } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; import { Box, Palette } from '@rocket.chat/fuselage'; +import { UserAvatar } from '@rocket.chat/ui-avatar'; import type { ReactElement } from 'react'; import React from 'react'; -import UserAvatar from '../../../../components/avatar/UserAvatar'; import { useFormatDateAndTime } from '../../../../hooks/useFormatDateAndTime'; import { useUserDisplayName } from '../../../../hooks/useUserDisplayName'; diff --git a/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/Avatar.js b/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/Avatar.js deleted file mode 100644 index a547a15f0c30..000000000000 --- a/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/Avatar.js +++ /dev/null @@ -1,10 +0,0 @@ -import { Options } from '@rocket.chat/fuselage'; -import React from 'react'; - -import RoomAvatar from '../../../../components/avatar/RoomAvatar'; - -const Avatar = ({ _id, type, avatarETag, ...props }) => ( - -); - -export default Avatar; diff --git a/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/TeamAutocomplete.tsx b/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/TeamAutocomplete.tsx index 8084336a57ab..0c3a03fdfe68 100644 --- a/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/TeamAutocomplete.tsx +++ b/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/TeamAutocomplete.tsx @@ -1,11 +1,10 @@ import { AutoComplete, Option, Box } from '@rocket.chat/fuselage'; +import { RoomAvatar } from '@rocket.chat/ui-avatar'; import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import type { ComponentProps } from 'react'; import React, { memo, useMemo, useState } from 'react'; -import Avatar from './Avatar'; - type TeamAutocompleteProps = Omit, 'filter'>; const TeamAutocomplete = ({ value, onChange, ...props }: TeamAutocompleteProps) => { @@ -32,12 +31,14 @@ const TeamAutocomplete = ({ value, onChange, ...props }: TeamAutocompleteProps) onChange={onChange} filter={filter} setFilter={setFilter} - renderSelected={({ selected: { value, label } }) => ( + renderSelected={({ selected: { value, label: room } }) => ( - {label.name} + {room.name} )} - renderItem={({ value, label, ...props }) =>

@@ -600,7 +602,13 @@ const RoomBody = (): ReactElement => { > -
    +
      {canPreview ? ( <> {hasMorePreviousMessages ? ( diff --git a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx index 496b234cc9b6..b1277b9c7603 100644 --- a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx +++ b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx @@ -16,6 +16,7 @@ import { isMessageNewDay } from '../../../MessageList/lib/isMessageNewDay'; import MessageListProvider from '../../../MessageList/providers/MessageListProvider'; import LoadingMessagesIndicator from '../../../body/LoadingMessagesIndicator'; import { useFirstUnreadMessageId } from '../../../hooks/useFirstUnreadMessageId'; +import { useMessageListNavigation } from '../../../hooks/useMessageListNavigation'; import { useScrollMessageList } from '../../../hooks/useScrollMessageList'; import { useLegacyThreadMessageJump } from '../hooks/useLegacyThreadMessageJump'; import { useLegacyThreadMessageListScrolling } from '../hooks/useLegacyThreadMessageListScrolling'; @@ -57,7 +58,6 @@ const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElemen } = useLegacyThreadMessageListScrolling(mainMessage); const { parentRef: listJumpRef } = useLegacyThreadMessageJump({ enabled: !loading }); - const listRef = useMergedRefs(listScrollRef, listJumpRef); const hideUsernames = useUserPreference('hideUsernames'); const showUserAvatar = !!useUserPreference('displayAvatars'); @@ -68,6 +68,9 @@ const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElemen const scrollMessageList = useScrollMessageList(listWrapperScrollRef); const firstUnreadMessageId = useFirstUnreadMessageId(); + const { messageListRef, messageListProps } = useMessageListNavigation(); + + const listRef = useMergedRefs(listScrollRef, listJumpRef, messageListRef); return (
      @@ -76,7 +79,7 @@ const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElemen onScroll={handleScroll} style={{ scrollBehavior: 'smooth', overflowX: 'hidden' }} > -
        +
          {loading ? (
        • diff --git a/apps/meteor/client/views/room/hooks/useMessageListNavigation.ts b/apps/meteor/client/views/room/hooks/useMessageListNavigation.ts new file mode 100644 index 000000000000..f446e867531d --- /dev/null +++ b/apps/meteor/client/views/room/hooks/useMessageListNavigation.ts @@ -0,0 +1,112 @@ +import { createFocusManager } from '@react-aria/focus'; +import type { RefCallback } from 'react'; +import { useCallback } from 'react'; +import { useFocusManager } from 'react-aria'; + +type MessageListProps = { + 'aria-orientation': 'vertical' | 'horizontal'; +}; + +const isListItem = (node: EventTarget) => + (node as HTMLElement).getAttribute('role') === 'listitem' || (node as HTMLElement).getAttribute('role') === 'link'; +const isMessageToolbarAction = (node: EventTarget) => (node as HTMLElement).parentElement?.getAttribute('role') === 'toolbar'; +const isSystemMessage = (node: EventTarget) => (node as HTMLElement).classList.contains('rcx-message-system'); +const isThreadMessage = (node: EventTarget) => (node as HTMLElement).classList.contains('rcx-message-thread'); + +/** + * Custom hook to provide the room navigation by keyboard. + * @param ref - A ref to the message list DOM element. + */ +export const useMessageListNavigation = (): { messageListRef: RefCallback; messageListProps: MessageListProps } => { + const roomFocusManager = useFocusManager(); + + const messageListRef = useCallback( + (node: HTMLElement | null) => { + let lastMessageFocused: HTMLElement | null = null; + + if (!node) { + return; + } + + const massageListFocusManager = createFocusManager({ + current: node, + }); + + node.addEventListener('keydown', (e) => { + if (!e.target) { + return; + } + + if (!isListItem(e.target)) { + return; + } + + if (e.key === 'Tab') { + if (e.shiftKey) { + e.preventDefault(); + e.stopPropagation(); + + roomFocusManager.focusFirst({ + from: document.getElementsByClassName('rcx-room-header')[0], + }); + } else if (isThreadMessage(e.target) || isSystemMessage(e.target) || isMessageToolbarAction(e.target)) { + e.preventDefault(); + e.stopPropagation(); + + roomFocusManager.focusNext({ + accept: (node) => node.tagName === 'TEXTAREA', + }); + } + } + + if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { + if (e.key === 'ArrowUp') { + massageListFocusManager.focusPrevious({ wrap: true, accept: (node) => isListItem(node) }); + } + + if (e.key === 'ArrowDown') { + massageListFocusManager.focusNext({ wrap: true, accept: (node) => isListItem(node) }); + } + + lastMessageFocused = document.activeElement as HTMLElement; + } + }); + + node.addEventListener( + 'blur', + (e) => { + if (!(e.currentTarget instanceof HTMLElement && e.relatedTarget instanceof HTMLElement)) { + return; + } + + if (!e.currentTarget.contains(e.relatedTarget) && !lastMessageFocused) { + lastMessageFocused = e.target as HTMLElement; + } + }, + { capture: true }, + ); + + node.addEventListener( + 'focus', + (e) => { + if (!(e.currentTarget instanceof HTMLElement && e.relatedTarget instanceof HTMLElement)) { + return; + } + if (lastMessageFocused && !e.currentTarget.contains(e.relatedTarget) && node.contains(e.target as HTMLElement)) { + lastMessageFocused?.focus(); + lastMessageFocused = null; + } + }, + { capture: true }, + ); + }, + [roomFocusManager], + ); + + return { + messageListRef, + messageListProps: { + 'aria-orientation': 'vertical', + }, + }; +}; diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 35430565ce10..6e1b6a0b3836 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -240,7 +240,7 @@ "@rocket.chat/favicon": "workspace:^", "@rocket.chat/forked-matrix-appservice-bridge": "^4.0.2", "@rocket.chat/forked-matrix-bot-sdk": "^0.6.0-beta.3", - "@rocket.chat/fuselage": "^0.45.0", + "@rocket.chat/fuselage": "^0.47.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/fuselage-toastbar": "~0.31.25", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index d9faf22baf4c..60ffa84825f0 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -967,6 +967,7 @@ "Channel_Name_Placeholder": "Please enter channel name...", "Channel_to_listen_on": "Channel to listen on", "Channel_Unarchived": "Channel with name `#%s` has been Unarchived successfully", + "Channel__roomName__": "Channel {{roomName}}.", "Channels": "Channels", "Channels_added": "Channels added sucessfully", "Channels_are_where_your_team_communicate": "Channels are where your team communicate", @@ -1167,6 +1168,7 @@ "Conversation_finished_message": "Conversation Finished Message", "Conversation_finished_text": "Conversation Finished Text", "conversation_with_s": "the conversation with %s", + "Conversation_with__roomName__": "Conversation with {{roomName}}.", "Conversations": "Conversations", "Conversations_per_day": "Conversations per Day", "Convert": "Convert", @@ -6276,6 +6278,7 @@ "Remove_RocketChat_Watermark": "Remove Rocket.Chat watermark", "High_scalabaility": "High scalabaility", "Premium_and_unlimited_apps": "Premium and unlimited apps", + "Message_actions": "Message actions", "Message_audit": "Message auditing", "Premium_omnichannel_capabilities": "Premium omnichannel capabilities", "Video_call_manager": "Video call manager", diff --git a/apps/meteor/tests/e2e/messaging.spec.ts b/apps/meteor/tests/e2e/messaging.spec.ts index c5099c52b9f1..f71c1dd04b8c 100644 --- a/apps/meteor/tests/e2e/messaging.spec.ts +++ b/apps/meteor/tests/e2e/messaging.spec.ts @@ -20,6 +20,45 @@ test.describe.serial('Messaging', () => { await page.goto('/home'); }); + test('should navigate on messages using keyboard', async ({ page }) => { + await poHomeChannel.sidenav.openChat(targetChannel); + await poHomeChannel.content.sendMessage('msg1'); + await poHomeChannel.content.sendMessage('msg2'); + + // move focus to the second message + await page.keyboard.press('Shift+Tab'); + await expect(page.locator('[data-qa-type="message"]').last()).toBeFocused(); + + // move focus to the first typed message + await page.keyboard.press('ArrowUp'); + await expect(page.locator('[data-qa-type="message"]:has-text("msg1")')).toBeFocused(); + + // move focus to the favorite icon + await page.keyboard.press('Shift+Tab'); + await expect(poHomeChannel.roomHeaderFavoriteBtn).toBeFocused(); + + // refocus on the first typed message + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await expect(page.locator('[data-qa-type="message"]:has-text("msg1")')).toBeFocused(); + + // move focus to the message toolbar + await page.locator('[data-qa-type="message"]:has-text("msg1")').locator('[role=toolbar][aria-label="Message actions"]').getByRole('button', { name: 'Add reaction' }).waitFor(); + + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await expect(page.locator('[data-qa-type="message"]:has-text("msg1")').locator('[role=toolbar][aria-label="Message actions"]').getByRole('button', { name: 'Add reaction' })).toBeFocused(); + + // move focus to the first system message + await page.keyboard.press('Tab'); + await page.keyboard.press('ArrowDown'); + await expect(page.locator('[data-qa="system-message"]').first()).toBeFocused(); + + // move focus to the composer + await page.keyboard.press('Tab'); + await expect(poHomeChannel.composer).toBeFocused(); + }); + test('expect show "hello word" in both contexts (targetChannel)', async ({ browser }) => { await poHomeChannel.sidenav.openChat(targetChannel); const { page } = await createAuxContext(browser, Users.user2); diff --git a/apps/meteor/tests/e2e/page-objects/home-channel.ts b/apps/meteor/tests/e2e/page-objects/home-channel.ts index 278256b7b0b1..01dcc302d3e0 100644 --- a/apps/meteor/tests/e2e/page-objects/home-channel.ts +++ b/apps/meteor/tests/e2e/page-objects/home-channel.ts @@ -41,6 +41,10 @@ export class HomeChannel { await this.page.mouse.move(0, 0); } + get composer(): Locator { + return this.page.locator('textarea[name="msg"]'); + } + get composerToolbar(): Locator { return this.page.locator('[role=toolbar][aria-label="Composer Primary Actions"]'); } diff --git a/ee/packages/ui-theming/package.json b/ee/packages/ui-theming/package.json index abd9bdd62a78..87cab03460df 100644 --- a/ee/packages/ui-theming/package.json +++ b/ee/packages/ui-theming/package.json @@ -4,7 +4,7 @@ "private": true, "devDependencies": { "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.45.0", + "@rocket.chat/fuselage": "^0.47.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/ui-contexts": "workspace:~", diff --git a/packages/fuselage-ui-kit/package.json b/packages/fuselage-ui-kit/package.json index 72f61f197aab..599e8476175b 100644 --- a/packages/fuselage-ui-kit/package.json +++ b/packages/fuselage-ui-kit/package.json @@ -63,7 +63,7 @@ "@babel/preset-typescript": "~7.22.15", "@rocket.chat/apps-engine": "1.41.0", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.45.0", + "@rocket.chat/fuselage": "^0.47.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/icons": "^0.33.0", diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index 8ba6c1202395..eafeb069d0d3 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.45.0", + "@rocket.chat/fuselage": "^0.47.0", "@rocket.chat/fuselage-tokens": "~0.32.0", "@rocket.chat/message-parser": "~0.31.28", "@rocket.chat/styled": "~0.31.25", diff --git a/packages/ui-avatar/package.json b/packages/ui-avatar/package.json index 2eaa0106ce3c..853178aae3a9 100644 --- a/packages/ui-avatar/package.json +++ b/packages/ui-avatar/package.json @@ -4,7 +4,7 @@ "private": true, "devDependencies": { "@babel/core": "~7.22.20", - "@rocket.chat/fuselage": "^0.41.0", + "@rocket.chat/fuselage": "^0.47.0", "@rocket.chat/ui-contexts": "workspace:^", "@types/babel__core": "~7.20.3", "@types/react": "~17.0.69", diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index c931427a0723..b7cbe0932812 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.45.0", + "@rocket.chat/fuselage": "^0.47.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/mock-providers": "workspace:^", diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json index 10da988ad59f..a1fbe39a6142 100644 --- a/packages/ui-composer/package.json +++ b/packages/ui-composer/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.45.0", + "@rocket.chat/fuselage": "^0.47.0", "@rocket.chat/icons": "^0.33.0", "@storybook/addon-actions": "~6.5.16", "@storybook/addon-docs": "~6.5.16", diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index 838b66764152..cd0327d6a051 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.45.0", + "@rocket.chat/fuselage": "^0.47.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/styled": "~0.31.25", diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index a7ba568e7367..9b34b125df9b 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -15,7 +15,7 @@ "@codemirror/tooltip": "^0.19.16", "@lezer/highlight": "^1.1.6", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.45.0", + "@rocket.chat/fuselage": "^0.47.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/fuselage-toastbar": "^0.31.25", diff --git a/yarn.lock b/yarn.lock index 7c88ca377d17..c1d3600c0f65 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9304,7 +9304,7 @@ __metadata: "@babel/preset-typescript": ~7.22.15 "@rocket.chat/apps-engine": 1.41.0 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.45.0 + "@rocket.chat/fuselage": ^0.47.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/gazzodown": "workspace:^" @@ -9359,9 +9359,9 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/fuselage@npm:^0.41.0": - version: 0.41.0 - resolution: "@rocket.chat/fuselage@npm:0.41.0" +"@rocket.chat/fuselage@npm:^0.47.0": + version: 0.47.0 + resolution: "@rocket.chat/fuselage@npm:0.47.0" dependencies: "@rocket.chat/css-in-js": ^0.31.25 "@rocket.chat/css-supports": ^0.31.25 @@ -9379,31 +9379,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 8ee33c5626ff7fb8970714696332efc723ca0793470d0b4da9a169aa6b2528ec75068d775e8f149dd3a1ea401ff0240be892022b64bd80e5bd4fc04349e0c0aa - languageName: node - linkType: hard - -"@rocket.chat/fuselage@npm:^0.45.0": - version: 0.45.0 - resolution: "@rocket.chat/fuselage@npm:0.45.0" - dependencies: - "@rocket.chat/css-in-js": ^0.31.25 - "@rocket.chat/css-supports": ^0.31.25 - "@rocket.chat/fuselage-tokens": ^0.32.0 - "@rocket.chat/memo": ^0.31.25 - "@rocket.chat/styled": ^0.31.25 - invariant: ^2.2.4 - react-aria: ~3.23.1 - react-keyed-flatten-children: ^1.3.0 - react-stately: ~3.17.0 - peerDependencies: - "@rocket.chat/fuselage-hooks": "*" - "@rocket.chat/fuselage-polyfills": "*" - "@rocket.chat/icons": "*" - react: ^17.0.2 - react-dom: ^17.0.2 - react-virtuoso: 1.2.4 - checksum: 84611983e263c0016687100c342c4256d6aff2a221c9d7ce86ecd7d49fd1c5fb5cb4d7d79aedd53fd4a1b29ad89efb4bb50357a8e24997822fb249fa78a5ac70 + checksum: 4ca06bd467db88b5c4c3eed70bbd8879923cfbe7f5a92bd934ed0f800b37825592d84753e124a3f2c70d4e9d82bf5dac6bc2735ea93ccfa26fddc6ab49ea8094 languageName: node linkType: hard @@ -9414,7 +9390,7 @@ __metadata: "@babel/core": ~7.22.20 "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.45.0 + "@rocket.chat/fuselage": ^0.47.0 "@rocket.chat/fuselage-tokens": ~0.32.0 "@rocket.chat/message-parser": ~0.31.28 "@rocket.chat/styled": ~0.31.25 @@ -9747,7 +9723,7 @@ __metadata: "@rocket.chat/favicon": "workspace:^" "@rocket.chat/forked-matrix-appservice-bridge": ^4.0.2 "@rocket.chat/forked-matrix-bot-sdk": ^0.6.0-beta.3 - "@rocket.chat/fuselage": ^0.45.0 + "@rocket.chat/fuselage": ^0.47.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/fuselage-toastbar": ~0.31.25 @@ -10605,7 +10581,7 @@ __metadata: resolution: "@rocket.chat/ui-avatar@workspace:packages/ui-avatar" dependencies: "@babel/core": ~7.22.20 - "@rocket.chat/fuselage": ^0.41.0 + "@rocket.chat/fuselage": ^0.47.0 "@rocket.chat/ui-contexts": "workspace:^" "@types/babel__core": ~7.20.3 "@types/react": ~17.0.69 @@ -10631,7 +10607,7 @@ __metadata: "@babel/core": ~7.22.20 "@react-aria/toolbar": ^3.0.0-beta.1 "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.45.0 + "@rocket.chat/fuselage": ^0.47.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/mock-providers": "workspace:^" @@ -10684,7 +10660,7 @@ __metadata: "@babel/core": ~7.22.20 "@react-aria/toolbar": ^3.0.0-beta.1 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.45.0 + "@rocket.chat/fuselage": ^0.47.0 "@rocket.chat/icons": ^0.33.0 "@storybook/addon-actions": ~6.5.16 "@storybook/addon-docs": ~6.5.16 @@ -10776,7 +10752,7 @@ __metadata: resolution: "@rocket.chat/ui-theming@workspace:ee/packages/ui-theming" dependencies: "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.45.0 + "@rocket.chat/fuselage": ^0.47.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/ui-contexts": "workspace:~" @@ -10819,7 +10795,7 @@ __metadata: "@rocket.chat/css-in-js": ~0.31.25 "@rocket.chat/emitter": ~0.31.25 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.45.0 + "@rocket.chat/fuselage": ^0.47.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/styled": ~0.31.25 @@ -10864,7 +10840,7 @@ __metadata: "@codemirror/tooltip": ^0.19.16 "@lezer/highlight": ^1.1.6 "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.45.0 + "@rocket.chat/fuselage": ^0.47.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/fuselage-toastbar": ^0.31.25 From 1fb0bc19893af65263beae40267a3c5e0f3e9a56 Mon Sep 17 00:00:00 2001 From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Tue, 20 Feb 2024 14:42:57 -0300 Subject: [PATCH 050/207] fix: some apps-engine bridges are receiving incompatible data (#31788) --- .changeset/dirty-islands-mate.md | 5 +++++ .../meteor/app/apps/server/bridges/messages.ts | 2 +- .../app/apps/server/bridges/persistence.ts | 18 ++++++++++++++---- .../app/apps/server/converters/threads.ts | 2 +- 4 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 .changeset/dirty-islands-mate.md diff --git a/.changeset/dirty-islands-mate.md b/.changeset/dirty-islands-mate.md new file mode 100644 index 000000000000..22e4eaf85d78 --- /dev/null +++ b/.changeset/dirty-islands-mate.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +fixed some apps-engine bridges receiving data in a wrong format diff --git a/apps/meteor/app/apps/server/bridges/messages.ts b/apps/meteor/app/apps/server/bridges/messages.ts index e4d09018176d..311e3aaca1e1 100644 --- a/apps/meteor/app/apps/server/bridges/messages.ts +++ b/apps/meteor/app/apps/server/bridges/messages.ts @@ -62,7 +62,7 @@ export class AppMessageBridge extends MessageBridge { } const convertedMsg = await this.orch.getConverters()?.get('messages').convertAppMessage(message); - const convertedUser = await this.orch.getConverters()?.get('users').convertById(user.id); + const convertedUser = (await Users.findOneById(user.id)) || this.orch.getConverters()?.get('users').convertToRocketChat(user); await deleteMessage(convertedMsg, convertedUser); } diff --git a/apps/meteor/app/apps/server/bridges/persistence.ts b/apps/meteor/app/apps/server/bridges/persistence.ts index 6876a9bcc79f..3810ed367e99 100644 --- a/apps/meteor/app/apps/server/bridges/persistence.ts +++ b/apps/meteor/app/apps/server/bridges/persistence.ts @@ -1,5 +1,6 @@ import type { RocketChatAssociationRecord } from '@rocket.chat/apps-engine/definition/metadata'; import { PersistenceBridge } from '@rocket.chat/apps-engine/server/bridges/PersistenceBridge'; +import type { InsertOneResult, UpdateResult } from 'mongodb'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; @@ -21,7 +22,10 @@ export class AppPersistenceBridge extends PersistenceBridge { throw new Error('Attempted to store an invalid data type, it must be an object.'); } - return this.orch.getPersistenceModel().insertOne({ appId, data }); + return this.orch + .getPersistenceModel() + .insertOne({ appId, data }) + .then(({ insertedId }: InsertOneResult) => insertedId || ''); } protected async createWithAssociations(data: object, associations: Array, appId: string): Promise { @@ -35,7 +39,10 @@ export class AppPersistenceBridge extends PersistenceBridge { throw new Error('Attempted to store an invalid data type, it must be an object.'); } - return this.orch.getPersistenceModel().insertOne({ appId, associations, data }); + return this.orch + .getPersistenceModel() + .insertOne({ appId, associations, data }) + .then(({ insertedId }: InsertOneResult) => insertedId || ''); } protected async readById(id: string, appId: string): Promise { @@ -89,7 +96,7 @@ export class AppPersistenceBridge extends PersistenceBridge { const records = await this.orch.getPersistenceModel().find(query).toArray(); - if (!records || !records.length) { + if (!records?.length) { return undefined; } @@ -125,6 +132,9 @@ export class AppPersistenceBridge extends PersistenceBridge { associations, }; - return this.orch.getPersistenceModel().update(query, { $set: { data } }, { upsert }); + return this.orch + .getPersistenceModel() + .update(query, { $set: { data } }, { upsert }) + .then(({ upsertedId }: UpdateResult) => upsertedId || ''); } } diff --git a/apps/meteor/app/apps/server/converters/threads.ts b/apps/meteor/app/apps/server/converters/threads.ts index 1fa52d29cb16..19d3b4aeae6d 100644 --- a/apps/meteor/app/apps/server/converters/threads.ts +++ b/apps/meteor/app/apps/server/converters/threads.ts @@ -101,7 +101,7 @@ export class AppThreadsConverter { groupable: 'groupable', token: 'token', blocks: 'blocks', - room, + room: () => room, editor: async (message: IMessage) => { if (!isEditedMessage(message)) { return undefined; From 21575cf4d52ef29036c34bab967e9fa221c41c78 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 20 Feb 2024 18:39:46 -0300 Subject: [PATCH 051/207] chore: bump turbo (#31797) --- package.json | 2 +- yarn.lock | 60 ++++++++++++++++++++++++++-------------------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index 47e67f6b7ec3..1498a54ec200 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "@changesets/cli": "^2.26.2", "@types/chart.js": "^2.9.39", "@types/js-yaml": "^4.0.8", - "turbo": "~1.11.2" + "turbo": "^1.12.4" }, "workspaces": [ "apps/*", diff --git a/yarn.lock b/yarn.lock index c1d3600c0f65..307f3e2a787e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -36590,7 +36590,7 @@ __metadata: "@types/chart.js": ^2.9.39 "@types/js-yaml": ^4.0.8 node-gyp: ^9.4.1 - turbo: ~1.11.2 + turbo: ^1.12.4 languageName: unknown linkType: soft @@ -40088,58 +40088,58 @@ __metadata: languageName: node linkType: hard -"turbo-darwin-64@npm:1.11.2": - version: 1.11.2 - resolution: "turbo-darwin-64@npm:1.11.2" +"turbo-darwin-64@npm:1.12.4": + version: 1.12.4 + resolution: "turbo-darwin-64@npm:1.12.4" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"turbo-darwin-arm64@npm:1.11.2": - version: 1.11.2 - resolution: "turbo-darwin-arm64@npm:1.11.2" +"turbo-darwin-arm64@npm:1.12.4": + version: 1.12.4 + resolution: "turbo-darwin-arm64@npm:1.12.4" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"turbo-linux-64@npm:1.11.2": - version: 1.11.2 - resolution: "turbo-linux-64@npm:1.11.2" +"turbo-linux-64@npm:1.12.4": + version: 1.12.4 + resolution: "turbo-linux-64@npm:1.12.4" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"turbo-linux-arm64@npm:1.11.2": - version: 1.11.2 - resolution: "turbo-linux-arm64@npm:1.11.2" +"turbo-linux-arm64@npm:1.12.4": + version: 1.12.4 + resolution: "turbo-linux-arm64@npm:1.12.4" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"turbo-windows-64@npm:1.11.2": - version: 1.11.2 - resolution: "turbo-windows-64@npm:1.11.2" +"turbo-windows-64@npm:1.12.4": + version: 1.12.4 + resolution: "turbo-windows-64@npm:1.12.4" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"turbo-windows-arm64@npm:1.11.2": - version: 1.11.2 - resolution: "turbo-windows-arm64@npm:1.11.2" +"turbo-windows-arm64@npm:1.12.4": + version: 1.12.4 + resolution: "turbo-windows-arm64@npm:1.12.4" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"turbo@npm:~1.11.2": - version: 1.11.2 - resolution: "turbo@npm:1.11.2" - dependencies: - turbo-darwin-64: 1.11.2 - turbo-darwin-arm64: 1.11.2 - turbo-linux-64: 1.11.2 - turbo-linux-arm64: 1.11.2 - turbo-windows-64: 1.11.2 - turbo-windows-arm64: 1.11.2 +"turbo@npm:^1.12.4": + version: 1.12.4 + resolution: "turbo@npm:1.12.4" + dependencies: + turbo-darwin-64: 1.12.4 + turbo-darwin-arm64: 1.12.4 + turbo-linux-64: 1.12.4 + turbo-linux-arm64: 1.12.4 + turbo-windows-64: 1.12.4 + turbo-windows-arm64: 1.12.4 dependenciesMeta: turbo-darwin-64: optional: true @@ -40155,7 +40155,7 @@ __metadata: optional: true bin: turbo: bin/turbo - checksum: 427b31807fe39d83ce002fd8da92029ac7ff93f4891e1da7c3c1a2c9b15257e3f4a8d9688186bd0228bda70965c4bf7db15dfd1f5de2c1208e058efd5401c352 + checksum: d387fb91af6ed0ea925201d3858180353c5d93be564829de2e22f48fe57124d1347d2abb8b99215901a305d4c6da4a0daf4c28afeec20fa1bc1ae2762c3b8d3d languageName: node linkType: hard From 475d69ddc5121720217f57078d1c0ef029ae8868 Mon Sep 17 00:00:00 2001 From: Guilherme Jun Grillo <48109548+guijun13@users.noreply.github.com> Date: Tue, 20 Feb 2024 20:35:12 -0300 Subject: [PATCH 052/207] chore: Replace dropdown in favor of `MenuV2` on message toolbar menu (#29469) Co-authored-by: dougfabris --- apps/meteor/app/reactions/client/init.ts | 4 +- .../client/messageAction/replyInThread.ts | 2 +- .../app/ui-utils/client/lib/MessageAction.ts | 2 +- .../message/MessageToolbarHolder.tsx | 17 +-- .../toolbar/DesktopToolbarDropdown.tsx | 23 --- .../message/toolbar/MessageActionMenu.tsx | 138 +++++------------- .../message/toolbar/MessageToolbar.tsx | 8 +- .../message/toolbar/MobileToolbarDropdown.tsx | 38 ----- .../message/toolbar/ToolbarDropdown.tsx | 38 ----- apps/meteor/tests/e2e/message-actions.spec.ts | 26 ++-- .../page-objects/fragments/home-content.ts | 4 +- apps/meteor/tests/e2e/threads.spec.ts | 26 ++-- 12 files changed, 90 insertions(+), 236 deletions(-) delete mode 100644 apps/meteor/client/components/message/toolbar/DesktopToolbarDropdown.tsx delete mode 100644 apps/meteor/client/components/message/toolbar/MobileToolbarDropdown.tsx delete mode 100644 apps/meteor/client/components/message/toolbar/ToolbarDropdown.tsx diff --git a/apps/meteor/app/reactions/client/init.ts b/apps/meteor/app/reactions/client/init.ts index 24840b9de7cf..1943d7262939 100644 --- a/apps/meteor/app/reactions/client/init.ts +++ b/apps/meteor/app/reactions/client/init.ts @@ -13,8 +13,8 @@ Meteor.startup(() => { context: ['message', 'message-mobile', 'threads', 'federated', 'videoconf', 'videoconf-threads'], action(event, props) { const { message = messageArgs(this).msg, chat } = props; - event.stopPropagation(); - chat?.emojiPicker.open(event.currentTarget! as Element, (emoji) => sdk.call('setReaction', `:${emoji}:`, message._id)); + event?.stopPropagation(); + chat?.emojiPicker.open(event?.currentTarget as Element, (emoji) => sdk.call('setReaction', `:${emoji}:`, message._id)); }, condition({ message, user, room, subscription }) { if (!room) { diff --git a/apps/meteor/app/threads/client/messageAction/replyInThread.ts b/apps/meteor/app/threads/client/messageAction/replyInThread.ts index 03f6606a2073..01d007e0d953 100644 --- a/apps/meteor/app/threads/client/messageAction/replyInThread.ts +++ b/apps/meteor/app/threads/client/messageAction/replyInThread.ts @@ -19,7 +19,7 @@ Meteor.startup(() => { context: ['message', 'message-mobile', 'federated', 'videoconf'], action(e, props) { const { message = messageArgs(this).msg } = props; - e.stopPropagation(); + e?.stopPropagation(); router.navigate({ name: router.getRouteName()!, params: { diff --git a/apps/meteor/app/ui-utils/client/lib/MessageAction.ts b/apps/meteor/app/ui-utils/client/lib/MessageAction.ts index 043dfe87b606..6a3ddd45ca66 100644 --- a/apps/meteor/app/ui-utils/client/lib/MessageAction.ts +++ b/apps/meteor/app/ui-utils/client/lib/MessageAction.ts @@ -48,7 +48,7 @@ export type MessageActionConfig = { context?: MessageActionContext[]; action: ( this: any, - e: Pick, + e: Pick | undefined, { message, tabbar, diff --git a/apps/meteor/client/components/message/MessageToolbarHolder.tsx b/apps/meteor/client/components/message/MessageToolbarHolder.tsx index 4251660d0f9f..cd0b8d6574bb 100644 --- a/apps/meteor/client/components/message/MessageToolbarHolder.tsx +++ b/apps/meteor/client/components/message/MessageToolbarHolder.tsx @@ -2,11 +2,10 @@ import type { IMessage } from '@rocket.chat/core-typings'; import { MessageToolbarWrapper } from '@rocket.chat/fuselage'; import { useQuery } from '@tanstack/react-query'; import type { ReactElement } from 'react'; -import React, { Suspense, lazy, memo, useRef, useState } from 'react'; +import React, { Suspense, lazy, memo, useState } from 'react'; import type { MessageActionContext } from '../../../app/ui-utils/client/lib/MessageAction'; import { useChat } from '../../views/room/contexts/ChatContext'; -import { useIsVisible } from '../../views/room/hooks/useIsVisible'; type MessageToolbarHolderProps = { message: IMessage; @@ -16,14 +15,8 @@ type MessageToolbarHolderProps = { const MessageToolbar = lazy(() => import('./toolbar/MessageToolbar')); const MessageToolbarHolder = ({ message, context }: MessageToolbarHolderProps): ReactElement => { - const ref = useRef(null); - - const [isVisible] = useIsVisible(ref); - const [kebabOpen, setKebabOpen] = useState(false); - - const showToolbox = isVisible || kebabOpen; - const chat = useChat(); + const [showToolbar, setShowToolbar] = useState(false); const depsQueryResult = useQuery(['toolbox', message._id, context], async () => { const room = await chat?.data.findRoom(); @@ -35,15 +28,15 @@ const MessageToolbarHolder = ({ message, context }: MessageToolbarHolderProps): }); return ( - - {showToolbox && depsQueryResult.isSuccess && depsQueryResult.data.room && ( + + {depsQueryResult.isSuccess && depsQueryResult.data.room && ( )} diff --git a/apps/meteor/client/components/message/toolbar/DesktopToolbarDropdown.tsx b/apps/meteor/client/components/message/toolbar/DesktopToolbarDropdown.tsx deleted file mode 100644 index 8c80bc9005bf..000000000000 --- a/apps/meteor/client/components/message/toolbar/DesktopToolbarDropdown.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Tile, PositionAnimated } from '@rocket.chat/fuselage'; -import type { ReactNode, Ref, RefObject } from 'react'; -import React, { forwardRef } from 'react'; - -type DesktopToolbarDropdownProps = { - children: ReactNode; - reference: RefObject; -}; - -const DesktopToolbarDropdown = forwardRef(function DesktopToolbarDropdown( - { reference, children }: DesktopToolbarDropdownProps, - ref: Ref, -) { - return ( - - - {children} - - - ); -}); - -export default DesktopToolbarDropdown; diff --git a/apps/meteor/client/components/message/toolbar/MessageActionMenu.tsx b/apps/meteor/client/components/message/toolbar/MessageActionMenu.tsx index a88d9beb1d99..8d266fe35d0e 100644 --- a/apps/meteor/client/components/message/toolbar/MessageActionMenu.tsx +++ b/apps/meteor/client/components/message/toolbar/MessageActionMenu.tsx @@ -1,14 +1,19 @@ -import { MessageToolbarItem, Option, OptionDivider, OptionTitle } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ComponentProps, MouseEvent, MouseEventHandler, ReactElement } from 'react'; -import React, { Fragment, useCallback, useRef, useState } from 'react'; +import type { MouseEvent, ReactElement } from 'react'; +import React from 'react'; import type { MessageActionConfig } from '../../../../app/ui-utils/client/lib/MessageAction'; -import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout'; -import ToolbarDropdown from './ToolbarDropdown'; +import GenericMenu from '../../GenericMenu/GenericMenu'; +import type { GenericMenuItemProps } from '../../GenericMenu/GenericMenuItem'; type MessageActionConfigOption = Omit & { - action: ((event: MouseEvent) => void) & MouseEventHandler; + action: (e?: MouseEvent) => void; +}; + +type MessageActionSection = { + id: string; + title: string; + items: GenericMenuItemProps[]; }; type MessageActionMenuProps = { @@ -16,104 +21,41 @@ type MessageActionMenuProps = { options: MessageActionConfigOption[]; }; -const getSectionOrder = (section: string): number => { - switch (section) { - case 'communication': - return 0; - case 'interaction': - return 1; - case 'duplication': - return 2; - case 'apps': - return 3; - case 'management': - return 4; - default: - return 5; - } -}; - -const MessageActionMenu = ({ options, onChangeMenuVisibility, ...props }: MessageActionMenuProps): ReactElement => { - const buttonRef = useRef(null); +const MessageActionMenu = ({ options, onChangeMenuVisibility }: MessageActionMenuProps): ReactElement => { const t = useTranslation(); - const [visible, setVisible] = useState(false); - const isLayoutEmbedded = useEmbeddedLayout(); - - const handleChangeMenuVisibility = useCallback( - (visible: boolean): void => { - setVisible(visible); - onChangeMenuVisibility(visible); - }, - [onChangeMenuVisibility], - ); - const groupOptions = options.reduce((acc, option) => { - const { type = '' } = option; + const groupOptions = options + .map((option) => ({ + variant: option.color === 'alert' ? 'danger' : '', + id: option.id, + icon: option.icon, + content: t(option.label), + onClick: option.action, + type: option.type, + })) + .reduce((acc, option) => { + const group = option.type ? option.type : ''; + const section = acc.find((section: { id: string }) => section.id === group); + if (section) { + section.items.push(option); + return acc; + } + const newSection = { id: group, title: group === 'apps' ? t('Apps') : '', items: [option] }; + acc.push(newSection); - if (option.color === 'alert') { - option.variant = 'danger' as const; - } - - const order = getSectionOrder(type); - - const [sectionType, options] = acc[getSectionOrder(type)] ?? [type, []]; - - if (!(isLayoutEmbedded && option.id === 'reply-directly')) { - options.push(option); - } - - if (options.length === 0) { return acc; - } - - acc[order] = [sectionType, options]; - - return acc; - }, [] as unknown as [section: string, options: Array][]); + }, [] as unknown as MessageActionSection[]); - const handleClose = useCallback(() => { - handleChangeMenuVisibility(false); - }, [handleChangeMenuVisibility]); return ( - <> - handleChangeMenuVisibility(!visible)} - data-qa-id='menu' - data-qa-type='message-action-menu' - title={t('More')} - /> - {visible && ( - <> - - {groupOptions.map(([section, options], index, arr) => ( - - {section === 'apps' && Apps} - {options.map((option) => ( - - ))} - - - )} - + ); }; diff --git a/apps/meteor/client/components/message/toolbar/MessageToolbar.tsx b/apps/meteor/client/components/message/toolbar/MessageToolbar.tsx index dd26dde17ecf..a51ca4f3b3d4 100644 --- a/apps/meteor/client/components/message/toolbar/MessageToolbar.tsx +++ b/apps/meteor/client/components/message/toolbar/MessageToolbar.tsx @@ -12,6 +12,7 @@ import type { MessageActionContext } from '../../../../app/ui-utils/client/lib/M import { MessageAction } from '../../../../app/ui-utils/client/lib/MessageAction'; import { useEmojiPickerData } from '../../../contexts/EmojiPickerContext'; import { useMessageActionAppsActionButtons } from '../../../hooks/useAppActionButtons'; +import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout'; import EmojiElement from '../../../views/composer/EmojiPicker/EmojiElement'; import { useIsSelecting } from '../../../views/room/MessageList/contexts/SelectedMessagesContext'; import { useAutoTranslate } from '../../../views/room/MessageList/hooks/useAutoTranslate'; @@ -59,6 +60,7 @@ const MessageToolbar = ({ const t = useTranslation(); const user = useUser() ?? undefined; const settings = useSettings(); + const isLayoutEmbedded = useEmbeddedLayout(); const toolbarRef = useRef(null); const { toolbarProps } = useToolbar(props, toolbarRef); @@ -89,7 +91,7 @@ const MessageToolbar = ({ return { message: toolboxItems.filter((action) => !hiddenActions.includes(action.id)), - menu: menuItems.filter((action) => !hiddenActions.includes(action.id)), + menu: menuItems.filter((action) => !(isLayoutEmbedded && action.id === 'reply-directly') && !hiddenActions.includes(action.id)), }; }); @@ -130,11 +132,11 @@ const MessageToolbar = ({ ))} {actionsQueryResult.isSuccess && actionsQueryResult.data.menu.length > 0 && ( ({ ...action, - action: (e): void => action.action(e, { message, tabbar: toolbox, room, chat, autoTranslateOptions }), + action: (e) => action.action(e, { message, tabbar: toolbox, room, chat, autoTranslateOptions }), }))} + onChangeMenuVisibility={onChangeMenuVisibility} data-qa-type='message-action-menu-options' /> )} diff --git a/apps/meteor/client/components/message/toolbar/MobileToolbarDropdown.tsx b/apps/meteor/client/components/message/toolbar/MobileToolbarDropdown.tsx deleted file mode 100644 index 0d62a0a8e9de..000000000000 --- a/apps/meteor/client/components/message/toolbar/MobileToolbarDropdown.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Tile } from '@rocket.chat/fuselage'; -import type { ReactNode, Ref } from 'react'; -import React, { forwardRef } from 'react'; - -import ScrollableContentWrapper from '../../ScrollableContentWrapper'; - -type MobileToolbarDropdownProps = { - children: ReactNode; -}; - -const MobileToolbarDropdown = forwardRef(function MobileToolbarDropdown( - { children, ...props }: MobileToolbarDropdownProps, - ref: Ref, -) { - return ( - - {children} - - ); -}); - -export default MobileToolbarDropdown; diff --git a/apps/meteor/client/components/message/toolbar/ToolbarDropdown.tsx b/apps/meteor/client/components/message/toolbar/ToolbarDropdown.tsx deleted file mode 100644 index 411441c0d1f6..000000000000 --- a/apps/meteor/client/components/message/toolbar/ToolbarDropdown.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Box } from '@rocket.chat/fuselage'; -import { useOutsideClick } from '@rocket.chat/fuselage-hooks'; -import { useLayout } from '@rocket.chat/ui-contexts'; -import type { ReactNode, ReactElement } from 'react'; -import React, { useRef } from 'react'; - -import DesktopToolbarDropdown from './DesktopToolbarDropdown'; -import MobileToolbarDropdown from './MobileToolbarDropdown'; - -type ToolbarDropdownProps = { - children: ReactNode; - reference: React.RefObject; - handleClose: () => void; -}; - -const ToolbarDropdown = ({ - children, - handleClose, - reference, -}: ToolbarDropdownProps): ReactElement => { - const { isMobile } = useLayout(); - const target = useRef(null); - const boxRef = useRef(null); - - const Dropdown = isMobile ? MobileToolbarDropdown : DesktopToolbarDropdown; - - useOutsideClick([boxRef], handleClose); - - return ( - - - {children} - - - ); -}; - -export default ToolbarDropdown; diff --git a/apps/meteor/tests/e2e/message-actions.spec.ts b/apps/meteor/tests/e2e/message-actions.spec.ts index 97fbc2c238b7..79737400cc0f 100644 --- a/apps/meteor/tests/e2e/message-actions.spec.ts +++ b/apps/meteor/tests/e2e/message-actions.spec.ts @@ -19,7 +19,7 @@ test.describe.serial('message-actions', () => { test('expect reply the message in direct', async ({ page }) => { await poHomeChannel.content.sendMessage('this is a message for reply in direct'); await poHomeChannel.content.openLastMessageMenu(); - await page.locator('li', { hasText: 'Reply in direct message' }).click(); + await page.locator('role=menuitem[name="Reply in direct message"]').click(); await expect(page).toHaveURL(/.*reply/); }); @@ -36,7 +36,7 @@ test.describe.serial('message-actions', () => { test('expect edit the message', async ({ page }) => { await poHomeChannel.content.sendMessage('This is a message to edit'); await poHomeChannel.content.openLastMessageMenu(); - await page.locator('[data-qa-id="edit-message"]').click(); + await page.locator('role=menuitem[name="Edit"]').click(); await page.locator('[name="msg"]').fill('this message was edited'); await page.keyboard.press('Enter'); @@ -46,7 +46,7 @@ test.describe.serial('message-actions', () => { test('expect message is deleted', async ({ page }) => { await poHomeChannel.content.sendMessage('Message to delete'); await poHomeChannel.content.openLastMessageMenu(); - await page.locator('[data-qa-id="delete-message"]').click(); + await page.locator('role=menuitem[name="Delete"]').click(); await page.locator('#modal-root .rcx-button-group--align-end .rcx-button--danger').click(); await expect(poHomeChannel.content.lastUserMessage.locator('[data-qa-type="message-body"]:has-text("Message to delete")')).toHaveCount( 0, @@ -68,23 +68,31 @@ test.describe.serial('message-actions', () => { test('expect star the message', async ({ page }) => { await poHomeChannel.content.sendMessage('Message to star'); await poHomeChannel.content.openLastMessageMenu(); - await page.locator('[data-qa-id="star-message"]').click(); + await page.locator('role=menuitem[name="Star"]').click(); await poHomeChannel.dismissToast(); await page.locator('role=button[name="Options"]').click(); await page.locator('[data-key="starred-messages"]').click(); await expect(poHomeChannel.content.lastUserMessageBody).toHaveText('Message to star'); }); - test('expect copy the message', async ({ page }) => { + test('expect copy the message content to clipboard', async ({ page, context }) => { + await context.grantPermissions(['clipboard-read', 'clipboard-write']); await poHomeChannel.content.sendMessage('Message to copy'); await poHomeChannel.content.openLastMessageMenu(); - await page.locator('[data-qa-id="copy"]').click(); + await page.locator('role=menuitem[name="Copy text"]').click(); + + const clipboardText = await page.evaluate("navigator.clipboard.readText()"); + expect(clipboardText).toBe('Message to copy'); }); - test('expect permalink the message', async ({ page }) => { + test('expect copy the message link to clipboard', async ({ page, context }) => { + await context.grantPermissions(['clipboard-read', 'clipboard-write']); await poHomeChannel.content.sendMessage('Message to permalink'); await poHomeChannel.content.openLastMessageMenu(); - await page.locator('[data-qa-id="permalink"]').click(); + await page.locator('role=menuitem[name="Copy link"]').click(); + + const clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(clipboardText).toContain('http'); }); test.describe('Preference Hide Contextual Bar by clicking outside of it Enabled', () => { @@ -102,7 +110,7 @@ test.describe.serial('message-actions', () => { test('expect reply the message in direct', async ({ page }) => { await poHomeChannel.content.sendMessage('this is a message for reply in direct'); await poHomeChannel.content.openLastMessageMenu(); - await page.locator('li', { hasText: 'Reply in Direct Message' }).click(); + await page.locator('role=menuitem[name="Reply in direct message"]').click(); await expect(page).toHaveURL(/.*reply/); }); diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts index 79c9617355e9..2c739ae6667d 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts @@ -223,8 +223,8 @@ export class HomeContent { async openLastMessageMenu(): Promise { await this.page.locator('[data-qa-type="message"]').last().hover(); - await this.page.locator('[data-qa-type="message"]').last().locator('[data-qa-type="message-action-menu"][data-qa-id="menu"]').waitFor(); - await this.page.locator('[data-qa-type="message"]').last().locator('[data-qa-type="message-action-menu"][data-qa-id="menu"]').click(); + await this.page.locator('[data-qa-type="message"]').last().locator('role=button[name="More"]').waitFor(); + await this.page.locator('[data-qa-type="message"]').last().locator('role=button[name="More"]').click(); } async openLastThreadMessageMenu(): Promise { diff --git a/apps/meteor/tests/e2e/threads.spec.ts b/apps/meteor/tests/e2e/threads.spec.ts index d9e181d2706b..0fcaff6fd037 100644 --- a/apps/meteor/tests/e2e/threads.spec.ts +++ b/apps/meteor/tests/e2e/threads.spec.ts @@ -62,7 +62,7 @@ test.describe.serial('Threads', () => { await expect(poHomeChannel.content.lastThreadMessageText).toContainText('This is a thread message also sent in channel'); await poHomeChannel.content.openLastMessageMenu(); - await page.locator('[data-qa-id="copy"]').click(); + await page.locator('role=menuitem[name="Copy text"]').click(); await expect(page).toHaveURL(/.*thread/); await expect(poHomeChannel.content.lastThreadMessageText).toContainText('This is a thread message also sent in channel'); @@ -82,7 +82,7 @@ test.describe.serial('Threads', () => { await poHomeChannel.content.openLastThreadMessageMenu(); await expect(page).toHaveURL(/.*thread/); - await page.locator('[data-qa-id="delete-message"]').click(); + await page.locator('role=menuitem[name="Delete"]').click(); await page.locator('#modal-root .rcx-button-group--align-end .rcx-button--danger').click(); await expect(page).not.toHaveURL(/.*thread/); @@ -93,7 +93,7 @@ test.describe.serial('Threads', () => { await poHomeChannel.content.openLastThreadMessageMenu(); await expect(page).toHaveURL(/.*thread/); - await page.locator('[data-qa-id="delete-message"]').click(); + await page.locator('role=menuitem[name="Delete"]').click(); await page.locator('#modal-root .rcx-button-group--align-end .rcx-button--danger').click(); await expect(page).toHaveURL(/.*thread/); @@ -101,7 +101,7 @@ test.describe.serial('Threads', () => { test('expect edit the thread message', async ({ page }) => { await poHomeChannel.content.openLastThreadMessageMenu(); - await page.locator('[data-qa-id="edit-message"]').click(); + await page.locator('role=menuitem[name="Edit"]').click(); await page.locator('[name="msg"]').last().fill('this message was edited'); await page.keyboard.press('Enter'); await expect(poHomeChannel.content.lastUserMessageBody).toHaveText('this message was edited'); @@ -118,20 +118,28 @@ test.describe.serial('Threads', () => { test('expect star the thread message', async ({ page }) => { await poHomeChannel.content.openLastThreadMessageMenu(); - await page.locator('[data-qa-id="star-message"]').click(); + await page.locator('role=menuitem[name="Star"]').click(); await page.getByRole('button').and(page.getByTitle('Options')).click(); await page.locator('[data-key="starred-messages"]').click(); await expect(poHomeChannel.content.lastUserMessageBody).toHaveText('this is a message for reply'); }); - test('expect copy the message', async ({ page }) => { + test('expect copy the thread message content to clipboard', async ({ page, context }) => { + await context.grantPermissions(['clipboard-read', 'clipboard-write']); await poHomeChannel.content.openLastThreadMessageMenu(); - await page.locator('[data-qa-id="copy"]').click(); + await page.locator('role=menuitem[name="Copy text"]').click(); + + const clipboardText = await page.evaluate("navigator.clipboard.readText()"); + expect(clipboardText).toBe('this is a message for reply'); }); - test('expect permalink the thread message', async ({ page }) => { + test('expect copy the thread message link to clipboard', async ({ page, context }) => { + await context.grantPermissions(['clipboard-read', 'clipboard-write']); await poHomeChannel.content.openLastThreadMessageMenu(); - await page.locator('[data-qa-id="permalink"]').click(); + await page.locator('role=menuitem[name="Copy link"]').click(); + + const clipboardText = await page.evaluate("navigator.clipboard.readText()"); + expect(clipboardText).toContain('http'); }); test('expect close thread if has only one message and user press escape', async ({ page }) => { From 5ff4110886f9f936e09ca20b682677528e5c3715 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 21 Feb 2024 01:18:39 -0300 Subject: [PATCH 053/207] ci: change arg place to reuse cache (#31800) --- .github/actions/meteor-build/action.yml | 2 +- apps/meteor/package.json | 2 +- turbo.json | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/actions/meteor-build/action.yml b/.github/actions/meteor-build/action.yml index e5b5d7740789..d261000ceb87 100644 --- a/.github/actions/meteor-build/action.yml +++ b/.github/actions/meteor-build/action.yml @@ -115,7 +115,7 @@ runs: echo "Coverage enabled" fi - yarn build:ci -- --directory /tmp/dist + yarn build:ci - name: Prepare build shell: bash diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 6e1b6a0b3836..5cf08d8b2b0e 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -14,7 +14,7 @@ ], "scripts": { "start": "meteor", - "build:ci": "METEOR_DISABLE_OPTIMISTIC_CACHING=1 meteor build --server-only", + "build:ci": "METEOR_DISABLE_OPTIMISTIC_CACHING=1 meteor build --server-only --directory /tmp/dist", "dev": "meteor --exclude-archs \"web.browser.legacy, web.cordova\"", "dsv": "meteor npm run dev", "ha": "meteor npm run ha:start", diff --git a/turbo.json b/turbo.json index a7f40f95782e..1eee804197d7 100644 --- a/turbo.json +++ b/turbo.json @@ -2,10 +2,6 @@ "$schema": "https://turborepo.org/schema.json", "globalDependencies": ["tsconfig.base.json", "tsconfig.base.server.json", "tsconfig.base.client.json"], "pipeline": { - "build:ci": { - "dependsOn": ["^build"], - "outputs": ["dist/**"] - }, "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] From c18f3e384b74779518f0ac4aef0290106a0498f4 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 21 Feb 2024 10:45:15 -0300 Subject: [PATCH 054/207] fix: DM notifications when preference set to mentions (#31759) --- .changeset/flat-fishes-sniff.md | 5 ++++ .../server/functions/notifications/desktop.ts | 4 +-- .../server/lib/sendNotificationsOnMessage.ts | 27 +++++++++---------- 3 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 .changeset/flat-fishes-sniff.md diff --git a/.changeset/flat-fishes-sniff.md b/.changeset/flat-fishes-sniff.md new file mode 100644 index 000000000000..836b83123f7d --- /dev/null +++ b/.changeset/flat-fishes-sniff.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fix notifications specially for DMs when preference is set to mentions. diff --git a/apps/meteor/app/lib/server/functions/notifications/desktop.ts b/apps/meteor/app/lib/server/functions/notifications/desktop.ts index 7d8d0f5f4db8..1afac17f9f58 100644 --- a/apps/meteor/app/lib/server/functions/notifications/desktop.ts +++ b/apps/meteor/app/lib/server/functions/notifications/desktop.ts @@ -81,7 +81,7 @@ export function shouldNotifyDesktop({ disableAllMessageNotifications: boolean; status: string; statusConnection: string; - desktopNotifications: string; + desktopNotifications: string | undefined; hasMentionToAll: boolean; hasMentionToHere: boolean; isHighlighted: boolean; @@ -90,7 +90,7 @@ export function shouldNotifyDesktop({ roomType: string; isThread: boolean; }): boolean { - if (disableAllMessageNotifications && desktopNotifications == null && !isHighlighted && !hasMentionToUser && !hasReplyToThread) { + if (disableAllMessageNotifications && !desktopNotifications && !isHighlighted && !hasMentionToUser && !hasReplyToThread) { return false; } diff --git a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts index ff3261a4bcf7..44654428ae8f 100644 --- a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts +++ b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts @@ -10,7 +10,7 @@ import { import { Subscriptions, Users } from '@rocket.chat/models'; import emojione from 'emojione'; import moment from 'moment'; -import type { Filter, RootFilterOperators } from 'mongodb'; +import type { RootFilterOperators } from 'mongodb'; import { callbacks } from '../../../../lib/callbacks'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; @@ -117,7 +117,7 @@ export const sendNotification = async ({ disableAllMessageNotifications, status: receiver.status ?? 'offline', statusConnection: receiver.statusConnection ?? 'offline', - desktopNotifications: desktopNotifications ?? 'default', + desktopNotifications, hasMentionToAll, hasMentionToHere, isHighlighted, @@ -304,33 +304,32 @@ export async function sendMessageNotifications(message: IMessage, room: IRoom, u } as const; (['desktop', 'mobile', 'email'] as const).forEach((kind) => { - const notificationField = `${kind === 'mobile' ? 'mobilePush' : kind}Notifications`; + const notificationField = kind === 'mobile' ? 'mobilePush' : `${kind}Notifications`; - const filter: Filter = { [notificationField]: 'all' }; + query.$or.push({ + [notificationField]: 'all', + ...(disableAllMessageNotifications ? { [`${kind}PrefOrigin`]: { $ne: 'user' } } : {}), + }); if (disableAllMessageNotifications) { - filter[`${kind}PrefOrigin`] = { $ne: 'user' }; + return; } - query.$or.push(filter); - - if (mentionIdsWithoutGroups.length > 0) { + if (room.t === 'd') { query.$or.push({ [notificationField]: 'mentions', - 'u._id': { $in: mentionIdsWithoutGroups }, }); - } else if (!disableAllMessageNotifications && (hasMentionToAll || hasMentionToHere)) { + } else if (mentionIdsWithoutGroups.length > 0) { query.$or.push({ [notificationField]: 'mentions', + 'u._id': { $in: mentionIdsWithoutGroups }, }); } const serverField = kind === 'email' ? 'emailNotificationMode' : `${kind}Notifications`; const serverPreference = settings.get(`Accounts_Default_User_Preferences_${serverField}`); - if ( - (room.t === 'd' && serverPreference !== 'nothing') || - (!disableAllMessageNotifications && (serverPreference === 'all' || hasMentionToAll || hasMentionToHere)) - ) { + + if (serverPreference === 'all' || hasMentionToAll || hasMentionToHere || room.t === 'd') { query.$or.push({ [notificationField]: { $exists: false }, }); From 46cdee02e1b49fc71e1da388125e1ac3d2b970e5 Mon Sep 17 00:00:00 2001 From: "lingohub[bot]" <69908207+lingohub[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:19:31 +0000 Subject: [PATCH 055/207] =?UTF-8?q?i18n:=20Language=20update=20from=20Ling?= =?UTF-8?q?oHub=20=F0=9F=A4=96=20on=202024-02-20Z=20(#31795)?= 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 | 1 + .../rocketchat-i18n/i18n/ar.i18n.json | 22 +- .../rocketchat-i18n/i18n/az.i18n.json | 1 + .../rocketchat-i18n/i18n/be-BY.i18n.json | 1 + .../rocketchat-i18n/i18n/bg.i18n.json | 1 + .../rocketchat-i18n/i18n/bs.i18n.json | 1 + .../rocketchat-i18n/i18n/ca.i18n.json | 1 + .../rocketchat-i18n/i18n/cs.i18n.json | 9 +- .../rocketchat-i18n/i18n/cy.i18n.json | 1 + .../rocketchat-i18n/i18n/da.i18n.json | 98 +++- .../rocketchat-i18n/i18n/de-AT.i18n.json | 1 + .../rocketchat-i18n/i18n/de.i18n.json | 1 + .../rocketchat-i18n/i18n/el.i18n.json | 1 + .../rocketchat-i18n/i18n/eo.i18n.json | 1 + .../rocketchat-i18n/i18n/es.i18n.json | 19 +- .../rocketchat-i18n/i18n/fa.i18n.json | 1 + .../rocketchat-i18n/i18n/fi.i18n.json | 1 + .../rocketchat-i18n/i18n/fr.i18n.json | 7 +- .../rocketchat-i18n/i18n/hi-IN.i18n.json | 217 ------- .../rocketchat-i18n/i18n/hr.i18n.json | 1 + .../rocketchat-i18n/i18n/hu.i18n.json | 1 + .../rocketchat-i18n/i18n/id.i18n.json | 1 + .../rocketchat-i18n/i18n/it.i18n.json | 551 +++++++++++++++++- .../rocketchat-i18n/i18n/ja.i18n.json | 1 + .../rocketchat-i18n/i18n/ka-GE.i18n.json | 1 + .../rocketchat-i18n/i18n/km.i18n.json | 1 + .../rocketchat-i18n/i18n/ko.i18n.json | 1 + .../rocketchat-i18n/i18n/ku.i18n.json | 1 + .../rocketchat-i18n/i18n/lo.i18n.json | 1 + .../rocketchat-i18n/i18n/lt.i18n.json | 1 + .../rocketchat-i18n/i18n/lv.i18n.json | 1 + .../rocketchat-i18n/i18n/mn.i18n.json | 1 + .../rocketchat-i18n/i18n/ms-MY.i18n.json | 1 + .../rocketchat-i18n/i18n/nl.i18n.json | 1 + .../rocketchat-i18n/i18n/no.i18n.json | 6 +- .../rocketchat-i18n/i18n/pl.i18n.json | 19 +- .../rocketchat-i18n/i18n/pt-BR.i18n.json | 11 +- .../rocketchat-i18n/i18n/pt.i18n.json | 2 + .../rocketchat-i18n/i18n/ro.i18n.json | 1 + .../rocketchat-i18n/i18n/ru.i18n.json | 27 +- .../rocketchat-i18n/i18n/sk-SK.i18n.json | 1 + .../rocketchat-i18n/i18n/sl-SI.i18n.json | 1 + .../rocketchat-i18n/i18n/sq.i18n.json | 1 + .../rocketchat-i18n/i18n/sr.i18n.json | 1 + .../rocketchat-i18n/i18n/sv.i18n.json | 1 + .../rocketchat-i18n/i18n/ta-IN.i18n.json | 1 + .../rocketchat-i18n/i18n/th-TH.i18n.json | 1 + .../rocketchat-i18n/i18n/tr.i18n.json | 1 + .../rocketchat-i18n/i18n/uk.i18n.json | 5 +- .../rocketchat-i18n/i18n/vi-VN.i18n.json | 1 + .../rocketchat-i18n/i18n/zh-HK.i18n.json | 1 + .../rocketchat-i18n/i18n/zh-TW.i18n.json | 1 + .../rocketchat-i18n/i18n/zh.i18n.json | 1 + 53 files changed, 747 insertions(+), 286 deletions(-) delete mode 100644 apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json index f9412da3aee1..386ab9826ece 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json @@ -2464,6 +2464,7 @@ "Turn_OFF": "Skakel af", "Turn_ON": "Sit aan", "Two-factor_authentication": "Twee-faktor-verifikasie", + "Two-factor_authentication_via_TOTP": "Twee-faktor-verifikasie", "Two-factor_authentication_disabled": "Tweefaktor-verifikasie gedeaktiveer", "Two-factor_authentication_enabled": "Tweefaktor-verifikasie aangeskakel", "Two-factor_authentication_is_currently_disabled": "Tweefaktor-verifikasie is tans gedeaktiveer", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json index a677f60895ac..5fd02c33c1fe 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json @@ -487,6 +487,7 @@ "Apps_License_Message_appId": "لم يتم إصدار الترخيص لهذا التطبيق", "Apps_License_Message_bundle": "ترخيص صادر لحزمة لا تحتوي على التطبيق", "Apps_License_Message_expire": "لم يعد الترخيص صالحًا ويحتاج إلى تجديد", + "Calls_in_queue_many": "{{count}} من المكالمات الانتظار", "Apps_License_Message_maxSeats": "لا يستوعب الترخيص العدد الحالي من المستخدمين النشطين. تُرجى زيادة عدد المقاعد", "Apps_License_Message_publicKey": "حدث خطأ أثناء محاولة فك تشفير الترخيص. تُرجى مزامنة مساحة العمل الخاصة بك في خدمات الاتصال وإعادة المحاولة.", "Apps_License_Message_renewal": "انتهت صلاحية الترخيص ويلزم تجديده", @@ -526,6 +527,7 @@ "Apps_Permissions_livechat-message_read": "الوصول إلى معلومات رسالة Livechat ", "Apps_Permissions_livechat-message_write": "تعديل معلومات رسالة Livechat ", "Apps_Permissions_livechat-room_read": "الوصول إلى معلومات غرفة Livechat", + "Calls_in_queue_few": "{{count}} من المكالمات الانتظار", "Apps_Permissions_livechat-room_write": "تعديل معلومات غرفة Livechat", "Apps_Permissions_livechat-department_read": "الوصول إلى معلومات قسم Livechat", "Apps_Permissions_livechat-department_multiple": "الوصول إلى معلومات أقسام Livechat المتعددة", @@ -583,11 +585,8 @@ "Auditing": "تدقيق", "Auth_Token": "الرمز المميز للمصادقة", "Authentication": "المصادقة", - "Calls_in_queue_two": "{{count}} من المكالمات الانتظار", "Author": "المؤلف", - "Calls_in_queue_few": "{{count}} من المكالمات الانتظار", "Author_Information": "معلومات المؤلف", - "Calls_in_queue_many": "{{count}} من المكالمات الانتظار", "Author_Site": "موقع المؤلف", "Authorization_URL": "عنوان URL للمصادقة", "Authorize": "تصريح", @@ -1967,6 +1966,7 @@ "Finish": "إنهاء", "Finish_Registration": "إنهاء التسجيل", "First_Channel_After_Login": "أول Channel بعد تسجيل الدخول", + "message_counter_many": "{{count}} رسائل", "First_response_time": "وقت للاستجابة الأولى", "Flags": "أعلام", "Follow_message": "رسالة المتابعة", @@ -2026,6 +2026,7 @@ "get-password-policy-mustContainAtLeastOneSpecialCharacter": "يجب أن تحتوي كلمة المرور تحتوي على رمز خاص واحد على الأقل", "get-password-policy-mustContainAtLeastOneUppercase": "يجب أن تحتوي كلمة المرور على حرف كبير واحد على الأقل", "get-server-info": "الحصول على معلومات الخادم", + "meteor_status_reconnect_in_many": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "github_no_public_email": "ليس لديك أي بريد إلكتروني عام في حسابك على Github", "github_HEAD": "العنوان", "Give_a_unique_name_for_the_custom_oauth": "امنح اسمًا فريدًا مخصصًا لـ oauth", @@ -2121,6 +2122,7 @@ "Ignored": "تم التجاهل", "Images": "الصور", "IMAP_intercepter_already_running": "أداة اعتراض IMAP قيد التشغيل الآن", + "message_counter_few": "{{count}} رسائل", "IMAP_intercepter_Not_running": "أداة اعتراض IMAP لا تعمل", "Impersonate_next_agent_from_queue": "انتحال شخصية الوكيل التالي من قائمة الانتظار", "Impersonate_user": "انتحال شخصية المستخدم", @@ -2193,6 +2195,7 @@ "Install": "تثبيت", "Install_Extension": "تثبيت الامتداد", "Install_FxOs": "تثبيت Rocket.Chat على Firefox لديك", + "meteor_status_reconnect_in_few": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "Install_FxOs_done": "رائع! يمكنك الآن استخدام Rocket.Chat عبر الأيقونة التي على الشاشة الرئيسية لديك. انعم بالمرح مع Rocket.Chat!", "Install_FxOs_error": "عذرًا، هذا لم يعمل على النحو المنشود! ظهر الخطأ التالي:", "Install_FxOs_follow_instructions": "يُرجى التأكد من تثبيت التطبيق على جهازك (اضغط على \"تثبيت\" عند التوجيه بذلك).", @@ -2380,14 +2383,10 @@ "Language_Japanese": "اليابانية", "Language_Latvian": "اللاتفية", "Language_Lithuanian": "اللتوانية", - "message_counter_zero": "{{count}} رسائل", "Language_Not_set": "غير محددة", "Language_Polish": "البولندية", - "message_counter_two": "{{count}} رسائل", "Language_Portuguese": "البرتغالية", - "message_counter_few": "{{count}} رسائل", "Language_Romanian": "الرومانية", - "message_counter_many": "{{count}} رسائل", "Language_Russian": "الروسية", "Language_Slovak": "السلوفاكية", "Language_Slovenian": "السلوفينية", @@ -2457,14 +2456,10 @@ "LDAP_Background_Sync": "مزامنة الخلفية", "LDAP_Background_Sync_Avatars": "مزامنة خلفية الصورة الرمزية", "LDAP_Background_Sync_Avatars_Description": "تمكين إجراء خلفية منفصلة لمزامنة الصور الرمزية للمستخدم.", - "meteor_status_reconnect_in_zero": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "LDAP_Background_Sync_Avatars_Interval": "الفاصل الزمني لمزامنة خلفية الصورة الرمزية", "LDAP_Background_Sync_Import_New_Users": "مزامنة الخلفية لاستيراد مستخدمين جدد", "LDAP_Background_Sync_Import_New_Users_Description": "سيتم استيراد كل المستخدمين (بناءً على معايير التصفية الخاصة بك) الموجودين في LDAP وغير الموجودين في Rocket.Chat", - "meteor_status_reconnect_in_two": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "LDAP_Background_Sync_Interval": "الفاصل الزمني لمزامنة الخلفية", - "meteor_status_reconnect_in_few": "المحاولة مرة أخرى خلال {{count}} من الثواني...", - "meteor_status_reconnect_in_many": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "LDAP_Background_Sync_Interval_Description": "الفاصل الزمني بين عمليات المزامنة. مثال \"كل 24 ساعة\" أو \"في اليوم الأول من الأسبوع\"، المزيد من الأمثلة على [محلل نص Cron] (http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "مزامنة الخلفية لتحديث المستخدمين الحاليين", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "ستتم مزامنة الصورة الرمزية والحقول واسم المستخدم، وما إلى ذلك (بناءً على التكوين الخاص بك) لجميع المستخدمين الذين تم استيرادهم من LDAP في كل **فاصل مزامنة**", @@ -2861,6 +2856,8 @@ "Message_Code_highlight_Description": "قائمة اللغات المفصولة بفواصل (كل اللغات المدعومة على [highlight.js](https://github.com/highlightjs/highlight.js/tree/11.6.0#supported-languages)) التي سيتم استخدامها لتمييز الكتل البرمجية للتعليمات البرمجية", "message_counter_one": "{{count}} رسالة", "message_counter_other": "{{count}} رسائل", + "message_counter_two": "{{count}} رسائل", + "message_counter_zero": "{{count}} رسائل", "Message_DateFormat": "تنسيق التاريخ", "Message_DateFormat_Description": "انظر أيضًا: [Moment.js](http://momentjs.com/docs/#/displaying/format/)", "Message_deleting_blocked": "لا يمكن حذف هذه الرسالة بعد الآن", @@ -2955,6 +2952,8 @@ "meteor_status_offline": "وضع عدم الاتصال.", "meteor_status_reconnect_in_one": "المحاولة مرة أخرى خلال ثانية واحدة...", "meteor_status_reconnect_in_other": "المحاولة مرة أخرى خلال {{count}} من الثواني...", + "meteor_status_reconnect_in_two": "المحاولة مرة أخرى خلال {{count}} من الثواني...", + "meteor_status_reconnect_in_zero": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "meteor_status_try_now_offline": "الاتصال مرة أخرى", "meteor_status_try_now_waiting": "المحاولة الآن", "meteor_status_waiting": "في انتظار اتصال الخادم،", @@ -4313,6 +4312,7 @@ "Turn_off_video": "إيقاف تشغيل الفيديو", "Two Factor Authentication": "المصادقة الثنائية", "Two-factor_authentication": "المصادقة الثنائية عبر TOTP", + "Two-factor_authentication_via_TOTP": "المصادقة الثنائية عبر TOTP", "Two-factor_authentication_disabled": "تم تعطيل المصادقة الثنائية", "Two-factor_authentication_email": "المصادقة الثنائية عبر البريد الإلكتروني", "Two-factor_authentication_email_is_currently_disabled": "تم تعطيل المصادقة الثنائية عبر البريد الإلكتروني حاليًا", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json index 61e5a5dba863..a4410953ab14 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json @@ -2464,6 +2464,7 @@ "Turn_OFF": "Söndür", "Turn_ON": "İşə sal", "Two-factor_authentication": "İki faktorlu identifikasiya", + "Two-factor_authentication_via_TOTP": "İki faktorlu identifikasiya", "Two-factor_authentication_disabled": "İki faktorlu kimlik doğrulaması aradan qaldırıldı", "Two-factor_authentication_enabled": "İki faktorlu autentifikasiya effektivdir", "Two-factor_authentication_is_currently_disabled": "İki faktorlu kimlik doğrulaması hazırda əlil", 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 cb52163f1c5f..7814ebf6efa5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json @@ -2482,6 +2482,7 @@ "Turn_OFF": "Выключыць", "Turn_ON": "Уключыць", "Two-factor_authentication": "двухфакторную аўтэнтыфікацыя", + "Two-factor_authentication_via_TOTP": "двухфакторную аўтэнтыфікацыя", "Two-factor_authentication_disabled": "Двухфакторную аўтэнтыфікацыя адключаная", "Two-factor_authentication_enabled": "ўключана праверка сапраўднасці двухфакторную", "Two-factor_authentication_is_currently_disabled": "Двухфакторную аўтэнтыфікацыя ў цяперашні час адключана", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json index 492a6c2dc616..162895dfe48a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json @@ -2460,6 +2460,7 @@ "Turn_OFF": "Изключи", "Turn_ON": "Включи", "Two-factor_authentication": "Двуфакторна удостоверяване", + "Two-factor_authentication_via_TOTP": "Двуфакторна удостоверяване", "Two-factor_authentication_disabled": "Двуфакторното удостоверяване е деактивирано", "Two-factor_authentication_enabled": "Разрешено е двуфакторно удостоверяване", "Two-factor_authentication_is_currently_disabled": "Двуфакторното удостоверяване понастоящем е деактивирано", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json index 4b4481e620f3..6c8f9528afa1 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json @@ -2456,6 +2456,7 @@ "Turn_OFF": "Isključiti", "Turn_ON": "Upaliti", "Two-factor_authentication": "Provjera autentičnosti s dva faktora", + "Two-factor_authentication_via_TOTP": "Provjera autentičnosti s dva faktora", "Two-factor_authentication_disabled": "Autentifikacija s dva faktora je onemogućena", "Two-factor_authentication_enabled": "Omogućena je autentifikacija s dva faktora", "Two-factor_authentication_is_currently_disabled": "Trenutačno je onemogućena autentikacija s dva faktora", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json index d6538ddd8366..376d37de9175 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json @@ -4224,6 +4224,7 @@ "Turn_off_video": "Desactivar el vídeo", "Two Factor Authentication": "Autenticació de dos factors", "Two-factor_authentication": "Autenticació de dos factors a través de TOTP", + "Two-factor_authentication_via_TOTP": "Autenticació de dos factors a través de TOTP", "Two-factor_authentication_disabled": "Autenticació de dos factors desactivada", "Two-factor_authentication_email": "Autenticació de dos factors via correu electrònic", "Two-factor_authentication_email_is_currently_disabled": "L'autenticació en 2 passos via correu electrònic està inhabilitada", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json index 8721449b0cf4..bc23ea19b98f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json @@ -1655,6 +1655,7 @@ "Filters": "Filtry", "Financial_Services": "Finanční služby", "First_Channel_After_Login": "Výchozí místnost po přihlášení", + "message_counter_many": "{{count}} zpráv(y)", "First_response_time": "První doba odezvy", "Flags": "Vlajky", "Follow_message": "Sledovat zprávu", @@ -1709,6 +1710,7 @@ "get-password-policy-mustContainAtLeastOneNumber": "Heslo musí obsahovat alespoň jednu číslici", "get-password-policy-mustContainAtLeastOneSpecialCharacter": "Heslo musí obsahovat alespoň jeden speciální znak", "get-password-policy-mustContainAtLeastOneUppercase": "Heslo musí obsahovat alespoň jedno velké písmeno", + "meteor_status_reconnect_in_many": "zkusím znovu za {{count}} sekund...", "github_no_public_email": "Ve svém účtu GitHub nemáte žádný e-mail jako veřejný", "Give_a_unique_name_for_the_custom_oauth": "Zadejte jedinečný název pro vlastní OAuth", "strike": "přeškrtnuté", @@ -1795,6 +1797,7 @@ "Ignored": "Ignorováno", "Images": "Obrázky", "IMAP_intercepter_already_running": "Zachytávání IMAP již běží", + "message_counter_few": "{{count}} zpráv(y)", "IMAP_intercepter_Not_running": "Zachytávání IMAP neběží", "Impersonate_next_agent_from_queue": "Vydávat se za dalšího operátora ve frontě", "Impersonate_user": "Vydávat se za uživatele", @@ -1863,6 +1866,7 @@ "Install": "Instalace", "Install_Extension": "Nainstalovat rozšíření", "Install_FxOs": "Nainstalovat Rocket.Chat do Vašeho Firefoxu", + "meteor_status_reconnect_in_few": "zkusím znovu za {{count}} sekund...", "Install_FxOs_done": "Super! Nyní můžete používat Rocket.Chat přes ikonu na úvodní obrazovce. Užijte si Rocket.Chat!", "Install_FxOs_error": "Jejda, tohle se stát nemělo! Objevila se následující chyba:", "Install_FxOs_follow_instructions": "Prosím potvrďte instalaci aplikace na Vašem přístroji (stiskněte tlačítko \"Install\" po výzvě).", @@ -2028,8 +2032,6 @@ "Language_Not_set": "Nespecifikováno", "Language_Polish": "Polština", "Language_Portuguese": "Portugalština", - "message_counter_few": "{{count}} zpráv(y)", - "message_counter_many": "{{count}} zpráv(y)", "Language_Russian": "Ruština", "Language_Spanish": "Španělština", "Language_Version": "Anglická verze", @@ -2070,8 +2072,6 @@ "LDAP_Background_Sync_Import_New_Users": "Synchronizace na pozadí importuje nové uživatele", "LDAP_Background_Sync_Import_New_Users_Description": "Naimportuje všechny uživatele (podle filtrů) nalezených v LDAP a neexistují v Rocket.Chat", "LDAP_Background_Sync_Interval": "Interval synchronizace na pozadí", - "meteor_status_reconnect_in_few": "zkusím znovu za {{count}} sekund...", - "meteor_status_reconnect_in_many": "zkusím znovu za {{count}} sekund...", "LDAP_Background_Sync_Interval_Description": "Interval mezi synchronizacemi. Například `every 24 hours` nebo `on the first day of the week`, více příkladů - [Cron Text Parser](http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Synchronizace na pozadí upravuje existující uživatele", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Synchronizuje avatary, uživatelská pole, uživatelské jméno atd (podle vaší konfigurace) všech uživatelů importovaných z LDAP podle **Interval synchronizace na pozadí**", @@ -3567,6 +3567,7 @@ "Turn_ON": "Zapnout", "Two Factor Authentication": "Dvoufázové ověření", "Two-factor_authentication": "Dvoufázové ověření pomocí TOTP", + "Two-factor_authentication_via_TOTP": "Dvoufázové ověření pomocí TOTP", "Two-factor_authentication_disabled": "Dvoufázová ověření zakázáno", "Two-factor_authentication_email": "Dvoufázové ověření pomocí Email", "Two-factor_authentication_email_is_currently_disabled": "Dvoufázové ověření pomocí Emailu zakázáno", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json index 26d6af592d26..8d2b105ee23f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json @@ -2458,6 +2458,7 @@ "Turn_OFF": "Trowch i ODDI", "Turn_ON": "Trowch AR", "Two-factor_authentication": "Dilysu dau ffactor", + "Two-factor_authentication_via_TOTP": "Dilysu dau ffactor", "Two-factor_authentication_disabled": "Dilysu dau ffactor anabl", "Two-factor_authentication_enabled": "Gall dilysu dau ffactor alluogi", "Two-factor_authentication_is_currently_disabled": "Mae dilysiad dau ffactor ar hyn o bryd yn anabl", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json index 51f7290b324a..58cec2856880 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json @@ -1,9 +1,18 @@ { "500": "Intern serverfejl", + "__agents__agents_and__count__conversations__period__": "{{agents}} agenter og {{count}} samtaler, {{period}} ", "__count__empty_rooms_will_be_removed_automatically": "{{count}} tomme rum fjernes automatisk.", "__count__empty_rooms_will_be_removed_automatically__rooms__": "{{count}} tomme rum vil blive fjernet automatisk:
          {{rooms}}.", + "__count__message_pruned_one": "{{count}} besked er beskåret", + "__count__message_pruned_other": "{{count}} beskeder er beskåret", + "__count__conversations__period__": "{{count}} samtaler, {{period}}", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} is no longer {{role}} by {{user_by}}", "__username__was_set__role__by__user_by_": "{{username}} was set {{role}} by {{user_by}}", + "Third_party_login": "Login fra tredjepart", + "Enabled_E2E_Encryption_for_this_room": "aktiverede E2E-kryptering for dette rum", + "Enable_business_hours": "Aktivér åbningstider", + "disabled": "deaktiveret", + "Disabled_E2E_Encryption_for_this_room": "deaktiverede E2E-kryptering for dette rum", "@username": "@brugernavn", "@username_message": "@brugernavn ", "#channel": "#kanal", @@ -13,23 +22,40 @@ "2_Erros_Information_and_Debug": "2 - Fejl, information og fejlfinding", "12_Hour": "12-timers ur", "24_Hour": "24-timers ur", + "A_cloud-based_platform_for_those_needing_a_plug-and-play_app": "En cloud-baseret platform til dem, der har brug for en plug-and-play-app.", "A_new_owner_will_be_assigned_automatically_to__count__rooms": "En ny ejer tildeles automatisk til {{count}} rum.", "A_new_owner_will_be_assigned_automatically_to_the__roomName__room": "En ny ejer tildeles automatisk til {{roomName}} rum.", "A_new_owner_will_be_assigned_automatically_to_those__count__rooms__rooms__": "En ny ejer tildeles automatisk til disse {{count}} rum:
          {{rooms}}.", + "A_secure_and_highly_private_self-managed_solution_for_conference_calls": "En sikker og meget privat selvadministreret løsning til konferenceopkald.", + "A_workspace_admin_needs_to_install_and_configure_a_conference_call_app": "En workspace-administrator skal installere og konfigurere en konferenceopkalds-app.", + "An_app_needs_to_be_installed_and_configured": "En app skal installeres og konfigureres.", + "Accessibility": "Tilgængelighed", + "Accessibility_and_Appearance": "Tilgængelighed og udseende", + "Accessibility_activation": "Her kan du aktivere en række funktioner for at forbedre din browseroplevelse.", + "Accessibility_statement": "Erklæring om tilgængelighed", + "Accessibility_feature_documentation": "Dokumentation af tilgængelighedsfunktioner", + "Accept_Call": "Acceptér opkald", "Accept": "Acceptér", - "Accept_incoming_livechat_requests_even_if_there_are_no_online_agents": "Acceptér indgående omnikanal-anmodninger, selvom der ikke er online agenter", + "Accept_incoming_livechat_requests_even_if_there_are_no_online_agents": "Acceptér indgående omnikanal-anmodninger, selvom der ikke er online-agenter", "Accept_new_livechats_when_agent_is_idle": "Accepter nye anmodninger fra omnikanalen når agenten er ledig", "Accept_with_no_online_agents": "Acceptér uden online agenter", "Access_not_authorized": "Adgang ikke godkendt", "Access_Token_URL": "Adgangstoken-URL", + "Access_Your_Account": "Få adgang til din konto", + "access_your_basic_information": "få adgang til dine grundlæggende oplysninger", "access-mailer": "Åbn mail-skærmen", "access-mailer_description": "Tilladelse til at sende masse-emails til alle brugere.", + "access-marketplace": "Adgang til markedsplads", + "access-marketplace_description": "Tilladelse til at gennemse og hente apps fra markedspladsen", "access-permissions": "Åbn tilladelsesskærmen", "access-permissions_description": "Redigér tilladelser for forskellige roller.", "access-setting-permissions": "Redigér indstillingsbaserede rettigheder", + "access-setting-permissions_description": "Tilladelse til at ændre indstillingsbaserede tilladelser", "Accessing_permissions": "Tilgår tilladelser", "Account_SID": "Konto-SID", + "Account": "Konto", "Accounts": "Konti", + "Accounts_Description": "Rediger indstillingerne i medlemskonto for arbejdsområdet.", "Accounts_Admin_Email_Approval_Needed_Default": "

          Brugeren [name] ([email]) er blevet registreret.

          Vær venlig at aktivere eller slette denne bruger under \"Administration -> Brugere\".

          ", "Accounts_Admin_Email_Approval_Needed_Subject_Default": "En ny bruger har registreret sig og har brug for godkendelse", "Accounts_Admin_Email_Approval_Needed_With_Reason_Default": "

          Brugeren [navn] ([email]) er blevet registreret.

          Årsag: [reason]

          Vær venlig at aktivere eller slette denne bruger under \"Administration -> Brugere\".

          ", @@ -38,9 +64,12 @@ "Accounts_AllowDeleteOwnAccount": "Tillad brugere at slette deres egen konto", "Accounts_AllowedDomainsList": "Liste over tilladte domæner", "Accounts_AllowedDomainsList_Description": "Kommasepareret liste over tilladte domæner", + "Accounts_AllowInvisibleStatusOption": "Mulighed for usynlig status", "Accounts_AllowEmailChange": "Tillad ændring af e-mail", "Accounts_AllowEmailNotifications": "Tillad e-mail-notifikationer", + "Accounts_AllowFeaturePreview": "Tillad forhåndsvisning af funktioner", "Accounts_AllowPasswordChange": "Tillad adgangskodeændring", + "Accounts_AllowPasswordChangeForOAuthUsers": "Tillad ændring af adgangskode for OAuth-brugere", "Accounts_AllowRealNameChange": "Tillad navneændring", "Accounts_AllowUserAvatarChange": "Tillad at brugerne skifter avatar", "Accounts_AllowUsernameChange": "Tillad brugernavneændring", @@ -64,6 +93,7 @@ "Accounts_Default_User_Preferences_desktopNotifications": "Standardbesked for skrivebordsnotifikationer", "Accounts_Default_User_Preferences_pushNotifications": "Standardbesked for mobilnotifikationer", "Accounts_Default_User_Preferences_not_available": "Kunne ikke hente brugerindstillingerne, fordi de endnu ikke er konfigureret af brugeren", + "Accounts_Default_User_Preferences_showThreadsInMainChannel_Description": "Når det er aktiveret, vises alle svar under en tråd også direkte i hovedrummet. Når den er deaktiveret, vil trådens svar blive vist baseret på afsenderens valg.", "Accounts_DefaultUsernamePrefixSuggestion": "Forslag til standard-præfiks til brugernavne", "Accounts_denyUnverifiedEmail": "Afvis ubekræftet e-mail", "Accounts_Directory_DefaultView": "Standardvisning for visning af folder", @@ -87,6 +117,7 @@ "Accounts_LoginExpiration": "Login-udløb i dage", "Accounts_ManuallyApproveNewUsers": "Nye brugere skal godkendes manuelt", "Accounts_OAuth_Apple": "Log ind med Apple", + "Accounts_OAuth_Apple_Description": "Hvis du kun vil have Apple-login aktiveret på mobilen, kan du lade alle felter være tomme.", "Accounts_OAuth_Custom_Access_Token_Param": "Parameternavn for adgangs-token", "Accounts_OAuth_Custom_Authorize_Path": "Autorisationssti", "Accounts_OAuth_Custom_Avatar_Field": "Avatar felt", @@ -96,14 +127,21 @@ "Accounts_OAuth_Custom_Channel_Admin": "Brugerdata-gruppeoversigt", "Accounts_OAuth_Custom_Email_Field": "Felt til e-mail", "Accounts_OAuth_Custom_Enable": "Aktivér", + "Accounts_OAuth_Custom_Groups_Claim": "Feltet Roller/Grupper til kanaltilknytning", "Accounts_OAuth_Custom_id": "Id", "Accounts_OAuth_Custom_Identity_Path": "Identitetssti", "Accounts_OAuth_Custom_Identity_Token_Sent_Via": "Identitets-token sendt via", + "Accounts_OAuth_Custom_Key_Field": "Nøglefelt", "Accounts_OAuth_Custom_Login_Style": "Loginstil", + "Accounts_OAuth_Custom_Map_Channels": "Kortlæg Roller/Grupper til kanaler", "Accounts_OAuth_Custom_Merge_Roles": "Flet roller fra SSO", "Accounts_OAuth_Custom_Merge_Users": "Sammenflet brugere", + "Accounts_OAuth_Custom_Merge_Users_Distinct_Services": "Flet brugere fra forskellige tjenester", + "Accounts_OAuth_Custom_Merge_Users_Distinct_Services_Description": "Når det givne nøglefelt matcher en eksisterende brugers, tillades brugere fra denne OAuth-tjeneste at blive flettet til eksisterende brugere uanset deres oprindelsestjeneste.", "Accounts_OAuth_Custom_Name_Field": "Navnefelt", "Accounts_OAuth_Custom_Roles_Claim": "Feltnavn for Roller/Grupper", + "Accounts_OAuth_Custom_Roles_To_Sync": "Roller som synkroniseres", + "Accounts_OAuth_Custom_Roles_To_Sync_Description": "OAuth-roller, der skal synkroniseres ved brugerlogin og -oprettelse (kommasepareret).", "Accounts_OAuth_Custom_Scope": "Anvendelsesområde", "Accounts_OAuth_Custom_Secret": "Secret", "Accounts_OAuth_Custom_Show_Button_On_Login_Page": "Vis knap på login-siden", @@ -193,6 +231,9 @@ "Accounts_Registration_AuthenticationServices_Default_Roles": "Standardroller til godkendelsestjenester", "Accounts_Registration_AuthenticationServices_Default_Roles_Description": "Standardroller (kommaseparerede), som brugere får, når de tilmelder sig via godkendelsestjenester", "Accounts_Registration_AuthenticationServices_Enabled": "Registrering med godkendelsestjenester", + "Accounts_Registration_Users_Default_Roles": "Standardroller for brugere", + "Accounts_Registration_Users_Default_Roles_Description": "Standardroller (kommasepareret), som brugerne får, når de registrerer sig manuelt (også via API)", + "Accounts_Registration_Users_Default_Roles_Enabled": "Aktivér standardroller til manuel registrering", "Accounts_Registration_InviteUrlType": "Typen af invitations-URL", "Accounts_Registration_InviteUrlType_Direct": "Direkte", "Accounts_Registration_InviteUrlType_Proxy": "Proxy", @@ -213,6 +254,8 @@ "Accounts_SetDefaultAvatar": "Vælg standardavatar", "Accounts_SetDefaultAvatar_Description": "Forsøger at finde en standardavatar baseret på OAuth-konto eller Gravatar", "Accounts_ShowFormLogin": "Vis standardformular for login", + "Accounts_TwoFactorAuthentication_By_TOTP_Enabled": "Aktivér tofaktorautentificering via TOTP", + "Accounts_TwoFactorAuthentication_By_TOTP_Enabled_Description": "Brugere kan konfigurere deres tofaktorautentificering ved hjælp af en hvilken som helst TOTP-app, som Google Authenticator eller Authy.", "Accounts_TwoFactorAuthentication_By_Email_Auto_Opt_In": "Tilmeld nye brugere automatisk til to-faktor via e-mail", "Accounts_TwoFactorAuthentication_By_Email_Auto_Opt_In_Description": "Nye brugere har som standard to-faktor-godkendelse via e-mail aktiveret. De vil være i stand til at deaktivere det på deres profilside.", "Accounts_TwoFactorAuthentication_By_Email_Code_Expiration": "Udløbstid i sekunder for koden sendt via e-mail", @@ -233,26 +276,36 @@ "Accounts_UserAddedEmail_Description": "Du kan bruge følgende felter: \n - [navn], [fname] eller [lname] for brugerens fulde navn, fornavn eller efternavn. \n - `[email]` for brugerens e-mail. \n - `[password]` for brugerens adgangskode. \n - `[Site_Name]` og `[Site_URL]` for henholdsvis applikationsnavn og URL. ", "API_EmbedDisabledFor_Description": "Kommasepareret liste med brugernavne, der ikke får indlejret link-forhåndsvisninger.", "Accounts_UserAddedEmailSubject_Default": "Du er blevet føjet til [Site_Name]", + "Action": "Handling", "Action_required": "Handling påkrævet", "Activate": "Aktivér", "Active": "Aktiv", "Active_users": "Aktive brugere", "Activity": "Aktivitet", "Add": "Tilføj", + "Add_a_Message": "Tilføj en besked", "Add_agent": "Tilføj agent", "Add_custom_oauth": "Tilføj brugerdefineret OAuth", "Add_Domain": "Tilføj domæne", + "Add_emoji": "Tilføj emoji", "Add_files_from": "Tilføj filer fra", "Add_manager": "Tilføj manager", "Add_monitor": "Tilføj monitor", "Add_Reaction": "Tilføj reaktion", "Add_Role": "Tilføj rolle", + "Add_Server": "Tilføj server", "Add_user": "Tilføj bruger", "Add_User": "Tilføj bruger", "Add_users": "Tilføj brugere", + "Add_members": "Tilføj medlemmer", + "add-all-to-room": "Tilføj alle brugere til et rum", + "add-all-to-room_description": "Tilladelse til at tilføje alle brugere til et rum", "add-livechat-department-agents": "Tilføj omni-kanal-agenter til afdelinger", "add-oauth-service": "Tilføj OAuth-tjeneste", "add-oauth-service_description": "Tilladelse til at tilføje nye OAuth-tjenester", + "add-team-channel": "Tilføj teamkanal", + "add-team-channel_description": "Tilladelse til at tilføje en kanal til et team", + "add-team-member": "Tilføj teammedlem", "add-user": "Tilføj bruger", "add-user_description": "Tilladelse til at tilføje nye brugere til serveren via brugere-menuen", "add-user-to-any-c-room": "Tilføj bruger til enhver offentlig kanal", @@ -264,6 +317,7 @@ "Apps_Framework_enabled": "Aktivér App-framework", "Adding_OAuth_Services": "Tilføjelse af OAuth-tjenester", "Adding_permission": "Tilføjelse af tilladelse", + "Adjustable_layout": "Justerbart layout", "Adding_user": "Tilføjelse af bruger", "Additional_emails": "Yderligere e-mails", "Additional_Feedback": "Yderligere feedback", @@ -271,15 +325,24 @@ "Admin_disabled_encryption": "Din administrator har ikke aktiveret E2E-kryptering", "Admin_Info": "Administratorinfo", "Administration": "Administration", + "Address": "Adresse", + "Adjustable_font_size": "Justerbar skriftstørrelse", + "Adjustable_font_size_description": "Designet til dem, der foretrækker større eller mindre tekst for bedre læsbarhed. Denne fleksibilitet fremmer inklusivitet ved at give brugerne mulighed for at skræddersy softwaregrænsefladen til deres specifikke behov.", "Adult_images_are_not_allowed": "Voksenbilleder er ikke tilladt", + "Aerospace_and_Defense": "Luftfart og forsvar", "After_OAuth2_authentication_users_will_be_redirected_to_this_URL": "Efter OAuth2-godkendelse bliver brugerne sendt til en URL på denne liste. Du kan tilføje en URL pr. linje.", + "After_guest_registration": "Efter gæsteregistrering", "Agent": "Agent", "Agent_added": "Agent tilføjet", "Agent_Info": "Agent-info", "Agent_messages": "Agentmeddelelser", + "Agent_Name": "Agentens navn", "Agent_Name_Placeholder": "Indtast et agentnavn...", "Agent_removed": "Agent fjernet", + "Agent_deactivated": "Agenten blev deaktiveret", + "Agent_Without_Extensions": "Agent uden udvidelser", "Agents": "Agenter", + "Agree": "Enig", "Alerts": "Advarsler", "Alias": "Alias", "Alias_Format": "Alias-format", @@ -291,8 +354,10 @@ "AutoLinker_Phone_Description": "Automatisk koblet til telefonnumre. f.eks. '0045-12345678'", "All": "Alle", "AutoLinker_StripPrefix": "AutoLinker Strip Prefix", + "All_Apps": "Alle apps", "AutoLinker_StripPrefix_Description": "Kort visning. F.eks. https://rocket.chat => rocket.chat", "All_added_tokens_will_be_required_by_the_user": "Alle tilføjede tokens vil blive krævet af brugeren", + "All_categories": "Alle kategorier", "AutoLinker_Urls_Scheme": "AutoLinker skema: // URLs", "All_channels": "Alle kanaler", "AutoLinker_Urls_TLD": "AutoLinker TLD URLs", @@ -301,6 +366,8 @@ "All_logs": "Alle logfiler", "AutoLinker_UrlsRegExp": "AutoLinker URL Regular Expression", "All_messages": "Alle beskeder", + "All_Prices": "Alle priser", + "All_status": "Alle statusser", "All_users": "Alle brugere", "All_users_in_the_channel_can_write_new_messages": "Alle brugere i kanalen kan skrive nye beskeder", "Allow_collect_and_store_HTTP_header_informations": "Tillad for at samle og gemme HTTP-header-oplysninger", @@ -441,6 +508,10 @@ "Apps_Interface_IPreRoomCreatePrevent": "Begivenhed der sker inden et rum oprettes", "Apps_Interface_IPreRoomDeletePrevent": "Begivenhed der sker før et rum slettes", "Apps_Interface_IPreRoomUserJoined": "Begivenhed der indtræffer før en bruger deltager i et rum (privat gruppe, offentlig kanal)", + "Apps_Logs_TTL_7days": "7 dage", + "Apps_Logs_TTL_14days": "14 dage", + "Apps_Logs_TTL_30days": "30 dage", + "Apps_Logs_TTL_Alert": "Afhængigt af størrelsen på Logs-samlingen kan ændring af denne indstilling føre til kortvarige perioder med lav hastighed", "Apps_Marketplace_Deactivate_App_Prompt": "Vil du virkelig deaktivere denne app?", "Apps_Marketplace_Login_Required_Description": "For at købe apps fra Rocket.Chat markedsplads, skal du registrere dit workspace og logge ind.", "Apps_Marketplace_Login_Required_Title": "Markedsplads-login påkræves", @@ -450,6 +521,12 @@ "Apps_Marketplace_Uninstall_App_Prompt": "Vil du virkelig afinstallere denne app?", "Apps_Marketplace_Uninstall_Subscribed_App_Anyway": "Afinstaller det alligevel", "Apps_Marketplace_Uninstall_Subscribed_App_Prompt": "Denne app har et aktivt abonnement og afinstallation annullerer den ikke. Hvis du gerne vil gøre det, skal du ændre dit abonnement, før du afinstallerer.", + "Apps_Permissions_Review_Modal_Title": "Nødvendige tilladelser", + "Apps_Permissions_Review_Modal_Subtitle": "Denne app vil gerne have adgang til følgende tilladelser. Er du enig?", + "Apps_Permissions_No_Permissions_Required": "Appen kræver ikke yderligere tilladelser", + "Apps_Permissions_cloud_workspace-token": "Interagér med Cloud-tjenester på vegne af denne server", + "Apps_Permissions_user_read": "Adgang til brugeroplysninger", + "Apps_Permissions_user_write": "Tilpas brugeroplysninger", "Apps_Settings": "App'ens indstillinger", "Apps_User_Already_Exists": "Brugernavnet \"{{username}}\" bruges allerede. Omdøb eller fjern brugeren, der bruges til at installere denne app", "AutoLinker": "AutoLinker", @@ -1088,6 +1165,7 @@ "Custom_Sound_Has_Been_Deleted": "Den brugerdefinerede lyd er blevet slettet.", "Custom_Sound_Info": "Brugerdefineret lydinfo", "Custom_Sound_Saved_Successfully": "Brugerdefineret lyd blev gemt", + "Custom_Status": "Brugerdefineret status", "Custom_Translations": "Brugerdefinerede oversættelser", "Custom_Translations_Description": "Skal være et gyldigt JSON, hvor nøgler er sprog, der indeholder en ordbog med nøgle og oversættelser. F.eks: \n `{\"en\": {\"Channels\": \"Rooms\"},\"pt\": {\"Channels\": \"Salas\"}}`", "Custom_User_Status": "Tilpasset brugerstatus", @@ -1135,6 +1213,8 @@ "Default": "Standard", "Default_value": "Standardværdi", "Delete": "Slet", + "Delete_account": "Slet konto", + "Delete_account?": "Slet konto?", "Delete_all_closed_chats": "Slet alle lukkede chats", "Delete_message": "Slet besked", "Delete_my_account": "Slet min konto", @@ -1220,6 +1300,9 @@ "Discussion_title": "Opret en ny diskussion", "discussion-created": "{{message}}", "Discussions": "Diskussioner", + "Display": "Visning", + "Display_avatars": "Vis avatarer", + "Display_Avatars_Sidebar": "Vis avatarer i sidepanelet", "Display_chat_permissions": "Vis chat tilladelser", "Display_offline_form": "Vis offline-formular", "Display_setting_permissions": "Vis tilladelser til at ændre indstillinger", @@ -1719,6 +1802,8 @@ "get-password-policy-mustContainAtLeastOneNumber": "Adgangskoden skal indeholde mindst et tal", "get-password-policy-mustContainAtLeastOneSpecialCharacter": "Adgangskoden skal indeholde mindst én specialtegn", "get-password-policy-mustContainAtLeastOneUppercase": "Adgangskoden skal indeholde mindst et stort bogstav", + "get-password-policy-minLength-label": "Mindst {{limit}} tegn", + "get-password-policy-forbidRepeatingCharactersCount-label": "Max. {{limit}} gentagne tegn", "github_no_public_email": "Du har ikke nogen e-mail som offentlig e-mail i din GitHub-konto", "Give_a_unique_name_for_the_custom_oauth": "Angiv et unikt navn til den brugerdefinerede oauth", "strike": "gennemstreget", @@ -1728,6 +1813,7 @@ "Global_purge_override_warning": "Der er blevet oprettet en global opbevaringspolitikken. Hvis du lader \"Tilsidesættelse af global opbevaringspolitik\" være slået fra, kan du kun anvende en politik, der er strengere end den globale politik.", "Global_Search": "Global søgning", "Go_to_your_workspace": "Gå til dit workspace", + "Go_to_accessibility_and_appearance": "Gå til tilgængelighed og udseende", "GoogleCloudStorage": "Google Cloud Storage", "GoogleNaturalLanguage_ServiceAccount_Description": "JSON-fil for servicekonto nøgle. Flere oplysninger kan findes [her] (https://cloud.google.com/natural-language/docs/common/auth#set_up_a_service_account)", "GoogleTagManager_id": "Google Tag Manager-id", @@ -2730,6 +2816,7 @@ "Password_Changed_Email_Subject": "[Site_Name] - Adgangskode ændret", "Password_changed_section": "Adgangskode ændret", "Password_changed_successfully": "Password ændret med succes", + "Password_must_have": "Adgangskoden skal have:", "Password_Policy": "Adgangskoderegler", "Password_to_access": "Adgangskode til adgang", "Passwords_do_not_match": "Kodeordene er ikke ens", @@ -2899,6 +2986,7 @@ "Regenerate_codes": "Regenerere koder", "Regexp_validation": "Validering ved regular expression", "Register": "Tilmeld en ny konto", + "Register_new_account": "Tilmeld en ny konto", "Register_Server": "Registrer server", "Register_Server_Info": "Brug de forkonfigurerede gateways og proxies leveret af Rocket.Chat Technologies Corp.", "Register_Server_Opt_In": "Nyhedsbrev, tilbud og produktopdateringer", @@ -3027,6 +3115,7 @@ "Room_has_been_unarchived": "Værelset er blevet arkiveret", "Room_Info": "Info om rummet", "room_is_blocked": "Dette rum er blokeret", + "room_account_deactivated": "Denne konto er deaktiveret", "room_is_read_only": "Dette rum er skrivebeskyttet", "room_name": "navn på rum", "Room_name_changed": "Rumnavnet er ændret til: {{room_name}}af {{user_by}}", @@ -3314,8 +3403,10 @@ "Sort": "Sortere", "Sort_By": "Sortér efter", "Sort_by_activity": "Sorter efter aktivitet", - "Sound": "Sund", + "Sound": "Lyd", + "Sounds": "Lyde", "Sound_File_mp3": "Lydfil (mp3)", + "Sound File": "Lydfil", "SSL": "SSL", "Star": "Stjerne", "Star_Message": "Stjerne besked", @@ -3584,6 +3675,7 @@ "Turn_ON": "Tænde for", "Two Factor Authentication": "To-faktor-godkendelse", "Two-factor_authentication": "Tofaktorgodkendelse", + "Two-factor_authentication_via_TOTP": "Tofaktorgodkendelse", "Two-factor_authentication_disabled": "Tofaktorgodkendelse er deaktiveret", "Two-factor_authentication_email": "To-faktor-godkendelse via e-mail", "Two-factor_authentication_email_is_currently_disabled": "To-faktor-godkendelse via e-mail er i øjeblikket deaktiveret", @@ -3968,6 +4060,7 @@ "registration.component.resetPassword": "Nulstille kodeord", "registration.component.form.username": "Brugernavn", "registration.component.form.name": "Navn", + "registration.component.form.createAnAccount": "Opret en konto", "registration.component.form.emailAlreadyExists": "E-mailen eksisterer allerede", "registration.component.form.usernameAlreadyExists": "Brugernavnet eksisterer allerede. Prøv venligst et andet brugernavn.", "registration.component.form.invalidEmail": "Den indtastede e-mail er ugyldig", @@ -3981,6 +4074,7 @@ "registration.component.form.sendConfirmationEmail": "Send bekræftelses-email", "onboarding.component.form.action.pasteHere": "Indsæt her ...", "onboarding.form.registerOfflineForm.title": "Registrer offline", + "New_custom_status": "Ny brugerdefineret status", "Enterprise": "Firma", "UpgradeToGetMore_engagement-dashboard_Title": "Analyse", "UpgradeToGetMore_auditing_Title": "Meddelelsesovervågning" 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 c2cd820646e9..8981a77afade 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json @@ -2466,6 +2466,7 @@ "Turn_OFF": "Abschalten", "Turn_ON": "Anmachen", "Two-factor_authentication": "Zwei-Faktor-Authentifizierung", + "Two-factor_authentication_via_TOTP": "Zwei-Faktor-Authentifizierung", "Two-factor_authentication_disabled": "Zwei-Faktor-Authentifizierung deaktiviert", "Two-factor_authentication_enabled": "Zwei-Faktor-Authentifizierung aktiviert", "Two-factor_authentication_is_currently_disabled": "Die Zwei-Faktor-Authentifizierung ist derzeit deaktiviert", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json index 70cb24fd2239..94d69b121469 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json @@ -4825,6 +4825,7 @@ "Turn_off_video": "Video ausschalten", "Two Factor Authentication": "Zwei-Faktor-Authentifizierung", "Two-factor_authentication": "Zwei-Faktor-Authentifizierung", + "Two-factor_authentication_via_TOTP": "Zwei-Faktor-Authentifizierung", "Two-factor_authentication_disabled": "Zwei-Faktor-Authentifizierung deaktiviert", "Two-factor_authentication_email": "Zwei-Faktor-Authentifizierung per E-Mail", "Two-factor_authentication_email_is_currently_disabled": "Die Zwei-Faktor-Authentifizierung per E-Mail ist derzeit deaktiviert", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json index d10bc47830a7..e9ffacd76117 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json @@ -2471,6 +2471,7 @@ "Turn_OFF": "Σβήνω", "Turn_ON": "Ανάβω", "Two-factor_authentication": "Έλεγχος ταυτότητας δύο παραγόντων", + "Two-factor_authentication_via_TOTP": "Έλεγχος ταυτότητας δύο παραγόντων", "Two-factor_authentication_disabled": "Ο έλεγχος ταυτότητας δύο στοιχείων είναι απενεργοποιημένος", "Two-factor_authentication_enabled": "Έχει ενεργοποιηθεί ο έλεγχος ταυτότητας δύο παραγόντων", "Two-factor_authentication_is_currently_disabled": "Ο έλεγχος ταυτότητας δύο στοιχείων είναι επί του παρόντος απενεργοποιημένος", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json index f5ff30796309..69cc5595f560 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json @@ -2464,6 +2464,7 @@ "Turn_OFF": "Malŝalti", "Turn_ON": "Ŝalti", "Two-factor_authentication": "Du-faktora aŭtentigo", + "Two-factor_authentication_via_TOTP": "Du-faktora aŭtentigo", "Two-factor_authentication_disabled": "Du-faktora aŭtentigo malŝaltita", "Two-factor_authentication_enabled": "Du faktoro aŭtentigo ebligita", "Two-factor_authentication_is_currently_disabled": "Du-faktora aŭtentigo estas nuntempe malebligita", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json index 47ac5b69953c..d49ad1bd10c6 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json @@ -4,16 +4,16 @@ "__count__empty_rooms_will_be_removed_automatically": "{{count}} salas vacías se eliminarán automáticamente.", "__count__empty_rooms_will_be_removed_automatically__rooms__": "{{count}} salas vacías se eliminarán automáticamente.
          {{rooms}}.", "__count__message_pruned_one": "{{count}} mensaje borrado", - "__count__message_pruned_many": "{{count}} mensajes borrados", "__count__message_pruned_other": "{{count}} mensajes borrados", + "__count__message_pruned_many": "{{count}} mensajes borrados", "__count__conversations__period__": "{{count}} conversaciones, {{period}}", "__count__tags__and__count__conversations__period__": "{{count}} etiquetas y {{conversations}} conversaciones, {{period}}", "__departments__departments_and__count__conversations__period__": "{{departments}} departamentos y {{count}} conversaciones, {{period}}", "__usersCount__member_joined_one": "{{count}} miembro se ha unido", "__usersCount__member_joined_other": "{{count}} miembros se han unido", - "__usersCount__member_joined_many": "{{count}} miembros se han unido", "__usersCount__people_will_be_invited": "{{usersCount}} miembros sern invitados", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} ya no es {{role}} (por {{user_by}})", + "__usersCount__member_joined_many": "{{count}} miembros se han unido", "__username__was_set__role__by__user_by_": "{{username}} se ha establecido como {{role}} por {{user_by}}", "__count__without__department__": "{{count}} sin departamentos", "__count__without__tags__": "{{count}} sin etiquetas", @@ -1960,6 +1960,7 @@ "Finish": "Finalizar", "Finish_Registration": "Finalizar registro", "First_Channel_After_Login": "Primer Channel tras iniciar sesión", + "message_counter_many": "{{count}} mensajes", "First_response_time": "Tiempo de primera respuesta", "Flags": "Indicadores", "Follow_message": "Mensaje para seguirnos", @@ -2017,6 +2018,7 @@ "get-password-policy-mustContainAtLeastOneNumber": "La contraseña debe contener al menos un número", "get-password-policy-mustContainAtLeastOneSpecialCharacter": "La contraseña debe contener al menos un carácter especial", "get-password-policy-mustContainAtLeastOneUppercase": "La contraseña debe contener al menos una letra en mayúscula", + "meteor_status_reconnect_in_many": "intentando de nuevo dentro de {{count}} segundos...", "github_no_public_email": "No tienes ningún correo electrónico como correo público en tu cuenta de GitHub", "github_HEAD": "HEAD", "Give_a_unique_name_for_the_custom_oauth": "Indicar un nombre único para el OAuth personalizado", @@ -2377,7 +2379,6 @@ "Language_Polish": "Polaco", "Language_Portuguese": "Portugués", "Language_Romanian": "Rumano", - "message_counter_many": "{{count}} mensajes", "Language_Russian": "Ruso", "Language_Slovak": "Eslovaco", "Language_Slovenian": "Esloveno", @@ -2450,7 +2451,6 @@ "LDAP_Background_Sync_Import_New_Users": "Sincronización en segundo plano: importar nuevos usuarios", "LDAP_Background_Sync_Import_New_Users_Description": "Importará todos los usuarios (según tus criterios de filtro) que existen en LDAP y no existan en Rocket.Chat", "LDAP_Background_Sync_Interval": "Intervalo de sincronización en segundo plano", - "meteor_status_reconnect_in_many": "intentando de nuevo dentro de {{count}} segundos...", "LDAP_Background_Sync_Interval_Description": "Intervalo entre sincronizaciones, como \"cada 24 horas\" o \"el primer día de la semana\"; más ejemplos en [Cron Text Parser] (http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Sincronización en segundo plano: actualizar usuarios existentes", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Sincronizará el avatar, los campos, el nombre de usuario, etc. (según tu configuración) de todos los usuarios ya importados desde LDAP en cada **Intervalo de sincronización**", @@ -3245,9 +3245,12 @@ "Password_Changed_Email_Subject": "[Site_Name] - Contraseña cambiada", "Password_changed_section": "Contraseña cambiada", "Password_changed_successfully": "Contraseña cambiada correctamente", + "mentions_counter_many": "{{count}} menciones", "Password_History": "Historial de contraseñas", "Password_History_Amount": "Longitud del historial de contraseñas", + "threads_counter_many": "{{count}} mensajes en hilo sin leer", "Password_History_Amount_Description": "Cantidad de contraseñas usadas más recientemente para evitar que los usuarios las reutilicen.", + "unread_messages_counter_many": "{{count}} mensajes sin leer", "Password_Policy": "Política de contraseñas", "Password_to_access": "Contraseña para acceder", "Passwords_do_not_match": "Las contraseñas no coinciden", @@ -3935,9 +3938,11 @@ "Sound": "Sonido", "Sound_File_mp3": "Archivo de sonido (mp3)", "Source": "Fuente", + "subscription.callout.description.limitsExceeded_many": "Su espacio de trabajo ha superado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "SSL": "SSL", "Star": "Destacar", "Star_Message": "Destacar mensaje", + "subscription.callout.description.limitsReached_many": "Su espacio de trabajo ha alcanzado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "Starred_Messages": "Mensajes destacados", "Start": "Iniciar", "Start_audio_call": "Iniciar llamada de audio", @@ -4029,10 +4034,7 @@ "Take_it": "Atiéndelo", "Take_rocket_chat_with_you_with_mobile_applications": "Llévate Rocket.Chat contigo con nuestras aplicaciones móviles.", "Taken_at": "Atendido a las", - "mentions_counter_many": "{{count}} menciones", "Talk_Time": "Tiempo de conversación", - "threads_counter_many": "{{count}} mensajes en hilo sin leer", - "unread_messages_counter_many": "{{count}} mensajes sin leer", "Target user not allowed to receive messages": "El usuario objetivo no puede recibir mensajes", "TargetRoom": "Room objetivo", "TargetRoom_Description": "Sala a la que se enviarán los mensajes que son el resultado de la activación de este evento. Solo se permite una sala objetivo, que debe existir ya.", @@ -4291,6 +4293,7 @@ "Turn_off_video": "Desactivar vídeo", "Two Factor Authentication": "Autenticación de dos factores", "Two-factor_authentication": "Autenticación de dos factores vía TOTP", + "Two-factor_authentication_via_TOTP": "Autenticación de dos factores vía TOTP", "Two-factor_authentication_disabled": "Autenticación de dos factores deshabilitada", "Two-factor_authentication_email": "Autenticación de dos factores vía correo electrónico", "Two-factor_authentication_email_is_currently_disabled": "La autenticación de dos factores vía correo electrónico está actualmente deshabilitada", @@ -4908,10 +4911,8 @@ "subscription.callout.servicesDisruptionsOccurring": "Se están produciendo interrupciones en los servicios.", "subscription.callout.capabilitiesDisabled": "Características desactivadas", "subscription.callout.description.limitsExceeded_one": "Su espacio de trabajo ha superado el límite de <1> {{val}} . <3> Administre su suscripción para incrementar los límites.", - "subscription.callout.description.limitsExceeded_many": "Su espacio de trabajo ha superado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "subscription.callout.description.limitsExceeded_other": "Su espacio de trabajo ha superado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "subscription.callout.description.limitsReached_one": "Su espacio de trabajo ha alcanzado el límite <1> {{val}} . <3> Administre su suscripción para incrementar los límites.", - "subscription.callout.description.limitsReached_many": "Su espacio de trabajo ha alcanzado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "subscription.callout.description.limitsReached_other": "Su espacio de trabajo ha alcanzado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "subscription.callout.allPremiumCapabilitiesDisabled": "Todas las funciones premium desactivadas", "subscription.callout.activeUsers": "puestos", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json index a4dfa77e34f9..a04ca62af4b1 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json @@ -2795,6 +2795,7 @@ "Turn_OFF": "خاموش شود", "Turn_ON": "روشن کن", "Two-factor_authentication": "تایید هویت دومرحله ای", + "Two-factor_authentication_via_TOTP": "تایید هویت دومرحله ای", "Two-factor_authentication_disabled": "تایید هویت دومرحله ای غیر فعال است", "Two-factor_authentication_enabled": "تایید هویت دومرحله ای فعال است", "Two-factor_authentication_is_currently_disabled": "تایید هویت دومرحله ای فعلا غیر فعال است", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json index 2746e382a91f..8fa7e2cfa32b 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json @@ -4934,6 +4934,7 @@ "Turn_off_video": "Kytke video pois päältä", "Two Factor Authentication": "Kaksivaiheinen todennus", "Two-factor_authentication": "Kaksivaiheinen tunnistautuminen TOTP:n avulla", + "Two-factor_authentication_via_TOTP": "Kaksivaiheinen tunnistautuminen", "Two-factor_authentication_disabled": "Kaksivaiheinen todennus poissa käytöstä", "Two-factor_authentication_email": "Kaksivaiheinen tunnistautuminen sähköpostitse", "Two-factor_authentication_email_is_currently_disabled": "Sähköpostin kautta tapahtuva kaksivaiheinen tunnistautuminen on tällä hetkellä poistettu käytöstä", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json index e60c6127d655..ed73b52dccbd 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json @@ -488,6 +488,7 @@ "Apps_License_Message_appId": "La licence n'a pas été émise pour cette application", "Apps_License_Message_bundle": "Licence émise pour un bundle qui ne contient pas l'application", "Apps_License_Message_expire": "La licence n'est plus valide et doit être renouvelée", + "Calls_in_queue_many": "{{count}} appels en file d'attente", "Apps_License_Message_maxSeats": "La licence n'est pas adaptée au nombre actuel d'utilisateurs actifs. Augmentez le nombre de sièges", "Apps_License_Message_publicKey": "Une erreur s'est produite lors du déchiffrement de la licence. Synchronisez votre espace de travail dans les services de connectivité et réessayez", "Apps_License_Message_renewal": "La licence a expiré et doit être renouvelée", @@ -585,7 +586,6 @@ "Authentication": "Authentification", "Author": "Auteur", "Author_Information": "Informations sur l'auteur", - "Calls_in_queue_many": "{{count}} appels en file d'attente", "Author_Site": "Site de l'auteur", "Authorization_URL": "URL d'autorisation", "Authorize": "Autoriser", @@ -1957,6 +1957,7 @@ "Finish": "Terminer", "Finish_Registration": "Terminer l'inscription", "First_Channel_After_Login": "Premier canal après la connexion", + "message_counter_many": "{{count}} messages", "First_response_time": "Temps de première réponse", "Flags": "Indicateurs", "Follow_message": "Suivre le message", @@ -2015,6 +2016,7 @@ "get-password-policy-mustContainAtLeastOneSpecialCharacter": "Le mot de passe doit contenir au moins un caractère spécial", "get-password-policy-mustContainAtLeastOneUppercase": "Le mot de passe doit contenir au moins une lettre majuscule", "get-server-info": "Obtenir des informations sur le serveur", + "meteor_status_reconnect_in_many": "nouvelle tentative dans {{count}} secondes...", "github_no_public_email": "Vous n'avez pas d'adresse e-mail publique associée à votre compte GitHub", "github_HEAD": "EN-TÊTE", "Give_a_unique_name_for_the_custom_oauth": "Indiquez un nom unique pour OAuth personnalisé", @@ -2377,7 +2379,6 @@ "Language_Polish": "Polonais", "Language_Portuguese": "Portugais", "Language_Romanian": "Roumain", - "message_counter_many": "{{count}} messages", "Language_Russian": "Russe", "Language_Slovak": "Slovaque", "Language_Slovenian": "Slovène", @@ -2450,7 +2451,6 @@ "LDAP_Background_Sync_Import_New_Users": "Synchronisation en arrière-plan de l'importation de nouveaux utilisateurs", "LDAP_Background_Sync_Import_New_Users_Description": "Importe tous les utilisateurs (en fonction de vos critères de filtrage) qui existent dans LDAP et qui n'existent pas dans Rocket.Chat", "LDAP_Background_Sync_Interval": "Intervalle de synchronisation en arrière-plan", - "meteor_status_reconnect_in_many": "nouvelle tentative dans {{count}} secondes...", "LDAP_Background_Sync_Interval_Description": "Intervalle entre les synchronisations. Exemple : `toutes les 24 heures` ou`le premier jour de la semaine`, plus d'exemples sur [Cron Text Parser](http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Synchronisation en arrière-plan de la mise à jour d'utilisateurs existants", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Synchronise l'avatar, les champs, le nom d'utilisateur, etc. (en fonction de votre configuration) de tous les utilisateurs déjà importés depuis LDAP à chaque **intervalle de synchronisation**", @@ -4306,6 +4306,7 @@ "Turn_off_video": "Désactiver la vidéo", "Two Factor Authentication": "Authentification à deux facteurs", "Two-factor_authentication": "Authentification à deux facteurs via TOTP", + "Two-factor_authentication_via_TOTP": "Authentification à deux facteurs via TOTP", "Two-factor_authentication_disabled": "Authentification à deux facteurs désactivée", "Two-factor_authentication_email": "Authentification à deux facteurs par e-mail", "Two-factor_authentication_email_is_currently_disabled": "L'authentification à 2 facteurs par e-mail est actuellement désactivée", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json deleted file mode 100644 index 33e3f43083f5..000000000000 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json +++ /dev/null @@ -1,217 +0,0 @@ -{ - "500": "आंतरिक सर्वर त्रुटि", - "__username__is_no_longer__role__defined_by__user_by_": "{{username}} is no longer {{role}} by {{user_by}}", - "__username__was_set__role__by__user_by_": "{{username}} was set {{role}} by {{user_by}}", - "@username": "@यूज़रनेम", - "@username_message": "@यूज़रनेम ", - "#channel": "#चैनल", - "%_of_conversations": "% बातचीत", - "0_Errors_Only": "0 - त्रुटियां केवल", - "1_Errors_and_Information": "1 - त्रुटियां और सूचना", - "2_Erros_Information_and_Debug": "2 - त्रुटियां, सूचना और डिबग", - "12_Hour": "12-घंटे की घड़ी", - "24_Hour": "24-घंटे की घड़ी", - "Accept": "स्वीकार करें", - "Accept_incoming_livechat_requests_even_if_there_are_no_online_agents": "यदि कोई ऑनलाइन एजेंट नहीं हैं, तो भी इनकमिंग लाइवचैट अनुरोध स्वीकार करें", - "Accept_with_no_online_agents": "कोई ऑनलाइन एजेंटों के साथ स्वीकार करें", - "Access_not_authorized": "प्रवेश अधिकृत नहीं है", - "Access_Token_URL": "एक्सेस टोकन URL", - "access-mailer": "मेलर स्क्रीन एक्सेस करें", - "access-mailer_description": "सभी उपयोगकर्ताओं को बड़े पैमाने पर ईमेल भेजने की अनुमति।", - "access-permissions": "अनुमतियाँ स्क्रीन एक्सेस करें", - "access-permissions_description": "विभिन्न भूमिकाओं के लिए अनुमतियों को संशोधित करें।", - "Accessing_permissions": "अक्सेस्सिंग की अनुमति", - "Account_SID": "खाता एसआईडी", - "Accounts": "खाता", - "Accounts_Admin_Email_Approval_Needed_Default": "

          The user [name] ([email]) has been registered.

          Please check \"Administration -> Users\" to activate or delete it.

          ", - "Accounts_Admin_Email_Approval_Needed_Subject_Default": "एक नया उपयोगकर्ता पंजीकृत है और उसे अनुमोदन की आवश्यकता है", - "Accounts_Admin_Email_Approval_Needed_With_Reason_Default": "

          The user [name] ([email]) has been registered.

          Reason: [reason]

          Please check \"Administration -> Users\" to activate or delete it.

          ", - "Accounts_AllowAnonymousRead": "अनाम पढ़ने की अनुमति दें", - "Accounts_AllowAnonymousWrite": "अनाम लिखने की अनुमति दें", - "Accounts_AllowDeleteOwnAccount": "उपयोगकर्ताओं को स्वयं का खाता हटाने की अनुमति दें", - "Accounts_AllowedDomainsList": "अनुमत डोमेन सूची", - "Accounts_AllowedDomainsList_Description": "अनुमत डोमेन की कोमा-पृथक सूची", - "Accounts_AllowEmailChange": "ईमेल परिवर्तन की अनुमति दें", - "Accounts_AllowPasswordChange": "पासवर्ड बदलने की अनुमति दें", - "Accounts_AllowRealNameChange": "नाम बदलने की अनुमति दें", - "Accounts_AllowUserAvatarChange": "उपयोगकर्ता अवतार परिवर्तन की अनुमति दें", - "Accounts_AllowUsernameChange": "उपयोगकर्ता नाम बदलने की अनुमति दें", - "Accounts_AllowUserProfileChange": "उपयोगकर्ता प्रोफ़ाइल बदलने की अनुमति दें", - "Accounts_AvatarBlockUnauthenticatedAccess": "अपुष्ट एक्सेस को अवतारों से ब्लॉक करें", - "Accounts_AvatarCacheTime": "अवतार कैश समय", - "Accounts_AvatarCacheTime_description": "HTTP प्रोटोकॉल को अवतार छवियों को कैश करने के लिए सेकंड की संख्या बताई गई है।", - "Accounts_AvatarExternalProviderUrl": "अवतार बाहरी प्रदाता URL", - "Accounts_AvatarExternalProviderUrl_Description": "उदाहरण: `https://acme.com/api/v1/ {उपयोगकर्ता नाम}`", - "Accounts_AvatarResize": "अवतार का आकार बदलें", - "Accounts_AvatarSize": "अवतार का आकार", - "Accounts_BlockedDomainsList": "अवरुद्ध डोमेन सूची", - "Accounts_BlockedDomainsList_Description": "अवरुद्ध डोमेन की कोमा से अलग सूची", - "Accounts_BlockedUsernameList": "अवरुद्ध उपयोगकर्ता नाम सूची", - "Accounts_BlockedUsernameList_Description": "कॉमा-अवरुद्ध उपयोगकर्ता नाम की अलग-अलग सूची (केस-असंवेदनशील)", - "Accounts_CustomFields_Description": "एक वैध JSON होना चाहिए जहां कुंजियाँ फ़ील्ड नाम हैं जिसमें फ़ील्ड सेटिंग्स का शब्दकोश है। उदाहरण: \n`{\"role\":{ \"type\": \"select\", \"defaultValue\": \"student\", \"options\": [\"teacher\", \"student\"], \"required\": true, \"modifyRecordField\": { \"array\": true, \"field\": \"roles\" } }, \"twitter\": { \"type\": \"text\", \"required\": true, \"minLength\": 2, \"maxLength\": 10 }}`", - "Accounts_CustomFieldsToShowInUserInfo": "कस्टम फ़ील्ड उपयोगकर्ता जानकारी में दिखाने के लिए", - "Accounts_Default_User_Preferences": "डिफ़ॉल्ट उपयोगकर्ता प्राथमिकताएं", - "Accounts_Default_User_Preferences_audioNotifications": "ऑडियो सूचनाएं डिफ़ॉल्ट चेतावनी", - "Accounts_Default_User_Preferences_desktopNotifications": "डेस्कटॉप सूचनाएं डिफ़ॉल्ट चेतावनी", - "Accounts_Default_User_Preferences_pushNotifications": "मोबाइल सूचनाएं डिफ़ॉल्ट चेतावनी", - "Accounts_Default_User_Preferences_not_available": "उपयोगकर्ता प्राथमिकताएँ प्राप्त करने में विफल, क्योंकि वे उपयोगकर्ता द्वारा अभी तक सेट नहीं किए गए हैं", - "Accounts_DefaultUsernamePrefixSuggestion": "डिफ़ॉल्ट उपयोगकर्ता नाम उपसर्ग सुझाव", - "Accounts_denyUnverifiedEmail": "अयोग्य ईमेल अस्वीकार करें", - "Accounts_Email_Activated": "[name]

          आपका खाता सक्रिय हो गया था।

          ", - "Accounts_Email_Activated_Subject": "खाता सक्रिय किया गया", - "Accounts_Email_Approved": "[name]

          आपका खाता स्वीकृत हो गया।

          ", - "Accounts_Email_Approved_Subject": "खाता स्वीकृत हुआ", - "Accounts_Email_Deactivated": "[name]

          आपका खाता निष्क्रिय कर दिया गया।

          ", - "Accounts_Email_Deactivated_Subject": "खाता निष्क्रिय किया गया", - "Accounts_EmailVerification": "ई - मेल सत्यापन", - "Accounts_EmailVerification_Description": "सुनिश्चित करें कि आपके पास इस सुविधा का उपयोग करने के लिए SMTP सेटिंग्स सही हैं", - "Accounts_Enrollment_Email": "नामांकन ईमेल", - "Accounts_Enrollment_Email_Default": "

          Welcome to [Site_Name]

          Go to [Site_URL] and try the best open source chat solution available today!

          ", - "Accounts_Enrollment_Email_Description": "आप निम्नलिखित प्लेसहोल्डर का उपयोग कर सकते हैं: \n - `[name]`, `[fname]`, `[lname]` उपयोगकर्ता के पूर्ण नाम के लिए, क्रमशः पहला नाम या अंतिम नाम। - । ईमेल] उपयोगकर्ता के ईमेल के लिए। \n - `[Site_Name]` और `[Site_URL]` क्रमशः अनुप्रयोग नाम और URL के लिए। ", - "Accounts_Enrollment_Email_Subject_Default": "[Site_Name] में आपका स्वागत है", - "Accounts_ForgetUserSessionOnWindowClose": "विंडो बंद होने पर उपयोगकर्ता सत्र को भूल जाएं", - "Accounts_Iframe_api_method": "API विधि", - "Accounts_Iframe_api_url": "API URL", - "Accounts_iframe_enabled": "सक्रिय", - "Accounts_iframe_url": "Iframe URL", - "Accounts_LoginExpiration": "दिन में प्रवेश की समाप्ति", - "Accounts_ManuallyApproveNewUsers": "नए उपयोगकर्ताओं को मैन्युअल रूप से अनुमोदित करें", - "Accounts_OAuth_Custom_Authorize_Path": "पथ अधिकृत करें", - "Accounts_OAuth_Custom_Button_Color": "बटन का रंग", - "Accounts_OAuth_Custom_Button_Label_Color": "बटन टेक्स्ट का रंग", - "Accounts_OAuth_Custom_Button_Label_Text": "बटन टेक्स्ट", - "Accounts_OAuth_Custom_Enable": "सक्षम करें", - "Accounts_OAuth_Custom_id": "Id", - "Accounts_OAuth_Custom_Identity_Path": "पहचान पथ", - "Accounts_OAuth_Custom_Identity_Token_Sent_Via": "के जरिए पहचान टोकन भेजा गया", - "Accounts_OAuth_Custom_Login_Style": "लॉगिन शैली", - "Accounts_OAuth_Custom_Merge_Users": "उपयोगकर्ताओं को मर्ज करें", - "Accounts_OAuth_Custom_Scope": "क्षेत्र", - "Accounts_OAuth_Custom_Secret": "गुप्त", - "Accounts_OAuth_Custom_Token_Path": "टोकन पथ", - "Accounts_OAuth_Custom_Token_Sent_Via": "के जरिए टोकन भेजा गया", - "Accounts_OAuth_Custom_Username_Field": "उपयोगकर्ता नाम फ़ील्ड", - "Accounts_OAuth_Drupal": "Drupal लॉगिन सक्षम है", - "Accounts_OAuth_Drupal_callback_url": "Drupal oAuth2 रीडायरेक्ट URI", - "Accounts_OAuth_Drupal_id": "Drupal oAuth2 क्लाइंट आईडी", - "Accounts_OAuth_Drupal_secret": "Drupal oAuth2 क्लाइंट सीक्रेट", - "Accounts_OAuth_Facebook": "फेसबुक लोगिन", - "Accounts_OAuth_Facebook_callback_url": "फेसबुक कॉलबैक URL", - "Accounts_OAuth_Facebook_id": "फेसबुक ऐप आईडी", - "Accounts_OAuth_Facebook_secret": "फेसबुक सीक्रेट", - "Accounts_OAuth_Github": "OAuth सक्षम", - "Accounts_OAuth_Github_callback_url": "GitHub कॉलबैक यूआरएल", - "Accounts_OAuth_GitHub_Enterprise": "OAuth सक्षम", - "Accounts_OAuth_GitHub_Enterprise_callback_url": "GitHub एंटरप्राइज़ कॉलबैक URL", - "Accounts_OAuth_GitHub_Enterprise_id": "क्लाइंट ID", - "Accounts_OAuth_GitHub_Enterprise_secret": "क्लाइंट Secret", - "Accounts_OAuth_Github_id": "क्लाइंट Id", - "Accounts_OAuth_Github_secret": "क्लाइंट Secret", - "Accounts_OAuth_Gitlab": "OAuth सक्षम", - "Accounts_OAuth_Gitlab_callback_url": "GitLab कॉलबैक URL", - "Accounts_OAuth_Gitlab_id": "Gitlab Id", - "Accounts_OAuth_Gitlab_identity_path": "पहचान पथ", - "Accounts_OAuth_Gitlab_secret": "क्लाइंट Secret", - "Accounts_OAuth_Google": "Google लॉगिन", - "Accounts_OAuth_Google_callback_url": "Google कॉलबैक URL", - "Accounts_OAuth_Google_id": "Google Id", - "Accounts_OAuth_Google_secret": "Google Secret", - "Accounts_OAuth_Linkedin": "LinkedIn लॉगिन", - "Accounts_OAuth_Linkedin_callback_url": "LinkedIn कॉलबैक URL", - "Accounts_OAuth_Linkedin_id": "LinkedIn Id", - "Accounts_OAuth_Linkedin_secret": "LinkedIn Secret", - "Accounts_OAuth_Meteor": "Meteor Login", - "Accounts_OAuth_Meteor_callback_url": "Meteor कॉलबैक URL", - "Accounts_OAuth_Meteor_id": "Meteor Id", - "Accounts_OAuth_Meteor_secret": "Meteor Secret", - "Accounts_OAuth_Nextcloud": "OAuth सक्षम", - "Accounts_OAuth_Nextcloud_secret": "क्लाइंट Secret", - "Accounts_OAuth_Proxy_host": "प्रॉक्सी होस्ट", - "Accounts_OAuth_Proxy_services": "प्रॉक्सी सेवाएँ", - "Accounts_OAuth_Tokenpass": "Tokenpass लॉगइन", - "Accounts_OAuth_Tokenpass_callback_url": "Tokenpass कॉलबैक URL", - "Accounts_OAuth_Tokenpass_id": "Tokenpass Id", - "Accounts_OAuth_Tokenpass_secret": "Tokenpass Secret", - "Accounts_OAuth_Twitter": "ट्विटर लॉगइन", - "Accounts_OAuth_Twitter_callback_url": "ट्विटर कॉलबैक URL", - "Accounts_OAuth_Twitter_id": "ट्विटर Id", - "Accounts_OAuth_Twitter_secret": "ट्विटर Secret", - "Accounts_OAuth_Wordpress": "वर्डप्रेस लॉगिन", - "Accounts_OAuth_Wordpress_authorize_path": "पथ अधिकृत करें", - "Accounts_OAuth_Wordpress_callback_url": "वर्डप्रेस कॉलबैक URL", - "Accounts_OAuth_Wordpress_id": "वर्डप्रेस Id", - "Accounts_OAuth_Wordpress_identity_path": "पहचान पथ", - "Accounts_OAuth_Wordpress_identity_token_sent_via": "पहचान टोकन भेजा गया", - "Accounts_OAuth_Wordpress_scope": "क्षेत्र", - "Accounts_OAuth_Wordpress_secret": "वर्डप्रेस Secret", - "Accounts_OAuth_Wordpress_server_type_custom": "कस्टम", - "Accounts_OAuth_Wordpress_server_type_wordpress_com": "Wordpress.com", - "Accounts_OAuth_Wordpress_server_type_wp_oauth_server": "WP OAuth Server Plugin", - "Accounts_OAuth_Wordpress_token_path": "टोकन पथ", - "Accounts_Password_Policy_AtLeastOneLowercase": "कम से कम एक लोअरकेस में", - "Accounts_Password_Policy_AtLeastOneLowercase_Description": "लागू करें कि पासवर्ड में कम से कम एक लोअरकेस वर्ण हो।", - "Accounts_Password_Policy_AtLeastOneNumber": "कम से कम एक नंबर", - "Accounts_Password_Policy_AtLeastOneNumber_Description": "लागू करें कि एक पासवर्ड में कम से कम एक संख्यात्मक चरित्र होता है।", - "Accounts_Password_Policy_AtLeastOneUppercase_Description": "लागू करें कि पासवर्ड में कम से कम एक लोअरकेस वर्ण हो।", - "Accounts_Registration_InviteUrlType_Direct": "सीधा", - "Accounts_RegistrationForm": "पंजीकरण पत्र", - "Accounts_RegistrationForm_Disabled": "उपयोग करने की अनुमति नहीं है", - "Accounts_RegistrationForm_Public": "जनता", - "Accounts_TwoFactorAuthentication_MaxDelta": "soochna", - "Additional_Feedback": "अतिरिक्त प्रतिक्रिया", - "App_status_auto_enabled": "सक्रिय", - "App_status_disabled": "उपयोग करने की अनुमति नहीं है", - "App_status_manually_enabled": "सक्रिय", - "Appearance": "दिखावट", - "Audio_Notifications_Default_Alert": "ऑडियो सूचनाएं डिफ़ॉल्ट चेतावनी", - "Cancel": "रद्द करना", - "Cancel_message_input": "रद्द करना", - "CAS_enabled": "सक्रिय", - "Client_Secret": "क्लाइंट Secret", - "Close_menu": "मेनू बंद करें", - "Custom": "कस्टम", - "Desktop_Notifications_Default_Alert": "डेस्कटॉप सूचनाएं डिफ़ॉल्ट चेतावनी", - "Disabled": "उपयोग करने की अनुमति नहीं है", - "Edit_Status": "स्थिति संपादित करें", - "Email_Notification_Mode_Disabled": "उपयोग करने की अनुमति नहीं है", - "Enable": "सक्षम करें", - "Enabled": "सक्रिय", - "Filters": "फिल्टर", - "Home": "होम", - "How_friendly_was_the_chat_agent": "चैट एजेंट कितना दोस्ताना था?", - "How_knowledgeable_was_the_chat_agent": "चैट एजेंट कितना जानकार था?", - "How_responsive_was_the_chat_agent": "चैट एजेंट कितना उत्तरदायी था?", - "How_satisfied_were_you_with_this_chat": "आप इस चैट से कितने संतुष्ट थे?", - "Installation": "स्थापना", - "LDAP_Authentication": "सक्षम करें", - "LDAP_Enable": "सक्षम करें", - "LDAP_User_Search_Scope": "क्षेत्र", - "Livechat_registration_form": "पंजीकरण ", - "Mobile_Push_Notifications_Default_Alert": "मोबाइल सूचनाएं डिफ़ॉल्ट चेतावनी", - "New_messages": "नए संदेश", - "No": "नहीं", - "Options": "विकल्प", - "Outlook_Calendar_Enabled": "सक्रिय", - "Please_answer_survey": "कृपया इस चैट के बारे में त्वरित सर्वेक्षण का उत्तर देने के लिए एक क्षण लें", - "Please_fill_name_and_email": "कृपया नाम और ईमेल भरें", - "Public": "जनता", - "Push_enable": "सक्षम करें", - "RetentionPolicy_Enabled": "सक्रिय", - "Scope": "क्षेत्र", - "Select_a_department": "एक विभाग का चयन करें", - "Select_department": "एक विभाग का चयन करें", - "Send": "भेजना", - "Skip": "छोड़ें", - "Start_Chat": "बातचीत शुरू ", - "Survey": "सर्वेक्षण", - "Survey_instructions": "प्रत्येक प्रश्न को अपनी संतुष्टि के अनुसार रेट करें, 1 मतलब कि आप पूरी तरह से असंतुष्ट हैं और 5 का अर्थ है कि आप पूरी तरह से संतुष्ट हैं।", - "Thank_you_for_your_feedback": "आपकी प्रतिक्रिया के लिए आपका धन्यवाद", - "Type_your_email": "अपना ईमेल टाइप करें", - "Type_your_message": "अपना संदेश टाइप करें", - "Type_your_name": "अपना नाम लिखें", - "Upload_file_question": "दस्तावेज अपलोड करें?", - "User_left": "उपयोगकर्ता छोड़ दिया", - "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 240f86dbe9b0..4933ef5f94f9 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json @@ -2599,6 +2599,7 @@ "Turn_OFF": "Isključiti", "Turn_ON": "Upaliti", "Two-factor_authentication": "Provjera autentičnosti s dva faktora", + "Two-factor_authentication_via_TOTP": "Provjera autentičnosti s dva faktora", "Two-factor_authentication_disabled": "Autentifikacija s dva faktora je onemogućena", "Two-factor_authentication_enabled": "Omogućena je autentifikacija s dva faktora", "Two-factor_authentication_is_currently_disabled": "Trenutačno je onemogućena autentikacija s dva faktora", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json index 5a756161ca6e..9c43825fe02f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json @@ -4746,6 +4746,7 @@ "Turn_off_video": "Videó kikapcsolása", "Two Factor Authentication": "Kétfaktoros hitelesítés", "Two-factor_authentication": "Időalapú, egyszer használatos jelszóval történő kétfaktoros hitelesítés", + "Two-factor_authentication_via_TOTP": "Kétlépcsős azonosítás", "Two-factor_authentication_disabled": "A kétfaktoros hitelesítés le van tiltva", "Two-factor_authentication_email": "E-mailen keresztüli kétfaktoros hitelesítés", "Two-factor_authentication_email_is_currently_disabled": "Az e-mailen keresztüli kétfaktoros hitelesítés jelenleg le van tiltva", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json index ba4c9e1605cf..9c078112ecbc 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json @@ -2471,6 +2471,7 @@ "Turn_OFF": "Matikan", "Turn_ON": "Nyalakan", "Two-factor_authentication": "Autentikasi dua faktor", + "Two-factor_authentication_via_TOTP": "Autentikasi dua faktor", "Two-factor_authentication_disabled": "Autentikasi dua faktor dinonaktifkan", "Two-factor_authentication_enabled": "Autentikasi dua faktor diaktifkan", "Two-factor_authentication_is_currently_disabled": "Autentikasi dua faktor saat ini dinonaktifkan", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json index fa7b76d6c53d..737acbf3b847 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json @@ -1,18 +1,32 @@ { "500": "Errore interno del server", + "__agents__agents_and__count__conversations__period__": "{{agents}} agenti e {{count}} conversazioni, {{period}}", "__count__empty_rooms_will_be_removed_automatically": "{{count}} stanze vuote verranno automaticamente rimosse.", "__count__empty_rooms_will_be_removed_automatically__rooms__": "{{count}} stanze vuote saranno rimosse automaticamente:
          {{rooms}}.", "__count__message_pruned_one": "{{count}} messaggio eliminato", - "__count__message_pruned_many": "{{count}} messaggi eliminati", "__count__message_pruned_other": "{{count}} messaggi eliminati", + "__count__message_pruned_many": "{{count}} messaggi eliminati", + "__count__conversations__period__": "{{count}} conversazioni, {{period}}", + "__count__tags__and__count__conversations__period__": "{{count}} tag e {{conversations}} conversazioni, {{period}}", + "__departments__departments_and__count__conversations__period__": "{{departments}} reparti e {{count}} conversazioni, {{period}}", "__usersCount__member_joined_one": "+ {{count}} membro si è unito", "__usersCount__member_joined_other": "+ {{count}} membri si sono uniti", - "__usersCount__member_joined_many": "+ {{count}} membri si sono uniti", "__usersCount__people_will_be_invited": "{{usersCount}} persone saranno invitate", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} non è più {{role}}, da {{user_by}}", + "__usersCount__member_joined_many": "+ {{count}} membri si sono uniti", "__username__was_set__role__by__user_by_": "A {{username}} è stato assegnato il ruolo di {{role}} da {{user_by}}", + "__count__without__department__": "{{count}} senza reparto", + "__count__without__tags__": "{{count}} senza tag", + "__count__without__assignee__": "{{count}} non assegnate", + "removed__username__as__role_": "ha rimosso {{username}} come {{role}}", + "set__username__as__role_": "ha impostato {{username}} come {{role}}", "This_room_encryption_has_been_enabled_by__username_": "La crittografia di questa stanza è stata attivata da {{username}}", "This_room_encryption_has_been_disabled_by__username_": "La crittografia di questa stanza è stata disabilitata da {{username}}", + "Third_party_login": "Login di terze parti", + "Enabled_E2E_Encryption_for_this_room": "criptazione E2E attiva per questo canale", + "Enable_business_hours": "Attiva orari di lavoro", + "disabled": "disattivo", + "Disabled_E2E_Encryption_for_this_room": "criptazione E2E disattiva per questo canale", "@username": "@username", "@username_message": "@username ", "#channel": "#canale", @@ -22,9 +36,17 @@ "2_Erros_Information_and_Debug": "2 - Errori, informazioni e debug", "12_Hour": "Orologio 12 ore", "24_Hour": "Orologio 24 ore", + "A_cloud-based_platform_for_those_needing_a_plug-and-play_app": "Una piattaforma basata sul cloud per chi ha bisogno di un'applicazione plug-and-play.", "A_new_owner_will_be_assigned_automatically_to__count__rooms": "Un nuovo proprietario verrà assegnato automaticamente a{{count}}stanze.", "A_new_owner_will_be_assigned_automatically_to_the__roomName__room": "Un nuovo proprietario verrà assegnato automaticamente alla stanza {{roomName}}.", "A_new_owner_will_be_assigned_automatically_to_those__count__rooms__rooms__": "Un nuovo proprietario verrà assegnato automaticamente a queste_count__stanze:
          {{rooms}}.", + "A_secure_and_highly_private_self-managed_solution_for_conference_calls": "Una soluzione autogestita sicura e altamente privata per le chiamate in teleconferenza.", + "A_workspace_admin_needs_to_install_and_configure_a_conference_call_app": "L'amministratore di uno spazio di lavoro deve installare e configurare un'applicazione per le chiamate in teleconferenza.", + "An_app_needs_to_be_installed_and_configured": "È necessario installare e configurare un'applicazione.", + "Accessibility": "Accessibilità", + "Accessibility_and_Appearance": "Accessibilità e aspetto", + "Accessibility_activation": "Qui puoi attivare una serie di funzioni per migliorare l'esperienza di navigazione.", + "Accessibility_statement": "Dichiarazione di accessibilità", "Accept_Call": "Accetta la chiamata", "Accept": "Accetta", "Accept_incoming_livechat_requests_even_if_there_are_no_online_agents": "Accetta richieste livechat in arrivo anche se non c'è alcun operatore online", @@ -32,8 +54,12 @@ "Accept_with_no_online_agents": "Accetta con nessun operatore online", "Access_not_authorized": "Accesso non autorizzato", "Access_Token_URL": "URL del Token di Accesso", + "Access_Your_Account": "Accedi al tuo account", + "access_your_basic_information": "accedere alle tue informazioni di base", "access-mailer": "Accedi alla schermata del client di posta", "access-mailer_description": "Autorizzazione a inviare email di massa a tutti gli utenti.", + "access-marketplace": "Accedi al marketplace", + "access-marketplace_description": "Autorizzazione a sfogliare e ottenere applicazioni dal marketplace", "access-permissions": "Vai alla schermata delle autorizazioni", "access-permissions_description": "Modifica autorizzazioni per vari ruoli.", "Accessing_permissions": "Permessi di Accesso", @@ -49,11 +75,15 @@ "Accounts_AllowedDomainsList": "Elenco domini consentiti", "Accounts_AllowedDomainsList_Description": "Elenco separato da virgole dei domini consentiti", "Accounts_AllowEmailChange": "Consenti cambio dell'email", + "Accounts_AllowEmailNotifications": "Consenti notifiche via e-mail", + "Accounts_AllowFeaturePreview": "Consenti funzionalità in anteprima", "Accounts_AllowPasswordChange": "Consenti cambio password", + "Accounts_AllowPasswordChangeForOAuthUsers": "Consenti cambio password per gli utenti OAuth", "Accounts_AllowRealNameChange": "Consenti cambio del nome", "Accounts_AllowUserAvatarChange": "Consenti cambio dell'avatar", "Accounts_AllowUsernameChange": "Consenti cambio nome utente", "Accounts_AllowUserProfileChange": "Consenti cambio profilo utente", + "Accounts_AllowUserStatusMessageChange": "Consenti messaggi di stato personalizzati", "Accounts_AvatarBlockUnauthenticatedAccess": "Blocca accesso non autenticato agli avatar", "Accounts_AvatarCacheTime": "Durata cache dell'avatar", "Accounts_AvatarCacheTime_description": "Numero di secondi in cui al protocollo http viene detto di memorizzare le immagini dell'avatar.", @@ -62,16 +92,18 @@ "Accounts_AvatarResize": "Ridimensiona avatar", "Accounts_AvatarSize": "Dimensione avatar", "Accounts_BlockedDomainsList": "Elenco domini bloccati", - "Accounts_BlockedDomainsList_Description": "Elenco separato da virgole dei domini bloccati", + "Accounts_BlockedDomainsList_Description": "Elenco di domini bloccati, separati da virgole \n", "Accounts_BlockedUsernameList": "Elenco nomi utenti bloccati", "Accounts_BlockedUsernameList_Description": "Elenco di nomi utenti bloccati separati da virgole (case-insensitive)", "Accounts_CustomFields_Description": "Dovrebbe essere un JSON valido dove le chiavi sono i campi che contengono un dizionario di impostazioni. Esempio: \n`{\"role\":{ \"type\": \"select\", \"defaultValue\": \"student\", \"options\": [\"teacher\", \"student\"], \"required\": true, \"modifyRecordField\": { \"array\": true, \"field\": \"roles\" } }, \"twitter\": { \"type\": \"text\", \"required\": true, \"minLength\": 2, \"maxLength\": 10 }}`", "Accounts_CustomFieldsToShowInUserInfo": "Campi personalizzati da mostrare in Informazioni utente", "Accounts_Default_User_Preferences": "Preferenze utente predefinite", "Accounts_Default_User_Preferences_audioNotifications": "Notifiche audio avviso predefinito", + "Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Consenti agli utenti di selezionare il comportamento \"inviare anche sul canale\"", "Accounts_Default_User_Preferences_desktopNotifications": "Notifiche desktop avviso predefinito", - "Accounts_Default_User_Preferences_pushNotifications": "Notifiche dispositivi mobili avviso predefinito", + "Accounts_Default_User_Preferences_pushNotifications": "Suoneria notifiche push predefinita", "Accounts_Default_User_Preferences_not_available": "Impossibile recuperare le preferenze dell'utente perché non sono ancora state impostate dall'utente", + "Accounts_Default_User_Preferences_showThreadsInMainChannel_Description": "Se attivato, tutte le risposte di una discussione saranno visualizzate direttamente nella stanza principale. Quando è disattivato, le risposte alle discussioni saranno visualizzate in base alla scelta del mittente.", "Accounts_DefaultUsernamePrefixSuggestion": "Suggerimento predefinito del prefisso del nome utente", "Accounts_denyUnverifiedEmail": "Nega email non verificate", "Accounts_Email_Activated": "[name]

          Il tuo account è stato attivato.

          ", @@ -104,6 +136,8 @@ "Accounts_OAuth_Custom_Identity_Token_Sent_Via": "Token identità inviato via", "Accounts_OAuth_Custom_Login_Style": "Stile di accesso", "Accounts_OAuth_Custom_Merge_Users": "Unisci utenti", + "Accounts_OAuth_Custom_Merge_Users_Distinct_Services": "Unire utenti di servizi diversi", + "Accounts_OAuth_Custom_Merge_Users_Distinct_Services_Description": "Quando il campo chiave indicato corrisponde a quello di un utente esistente, consente agli utenti di questo servizio OAuth di essere uniti agli utenti esistenti, indipendentemente dal servizio di origine.", "Accounts_OAuth_Custom_Roles_Claim": "Nome del campo Ruoli / Gruppi", "Accounts_OAuth_Custom_Scope": "Scopo", "Accounts_OAuth_Custom_Secret": "Chiave segreta", @@ -218,13 +252,16 @@ "Active": "Attivo", "Activity": "Attività", "Add": "Aggiungi", + "Add_a_Message": "Aggiungi un messaggio", "Add_agent": "Aggiungi operatore", "Add_custom_oauth": "Aggiungi OAuth personalizzato", "Add_Domain": "Aggiungi dominio", + "Add_emoji": "Aggiungi emoji", "Add_files_from": "Aggiungi file da", "Add_manager": "Aggiungi gestore", "Add_Reaction": "Aggiungi reazione", "Add_Role": "Aggiungi ruolo", + "Add_Server": "Aggiungi server", "Add_user": "Aggiungi utente", "Add_User": "Aggiungi utente", "Add_users": "Aggiungi utenti", @@ -238,9 +275,14 @@ "add-user-to-any-p-room_description": "Autorizzazione ad aggiungere un utente a qualsiasi canale privato", "add-user-to-joined-room": "Aggiungi utente a qualsiasi canale congiunto", "add-user-to-joined-room_description": "Autorizzazione ad aggiungere un utente a un canale attualmente collegato", + "added__roomName__to_team": "ha aggiunto #{{roomName}} a questo Team", + "Added__username__to_team": "ha aggiunto @{{user_added}} a questo Team", + "added__roomName__to_this_team": "ha aggiunto #{{roomName}} a questo team", "Apps_Framework_enabled": "Abilita l'App Framework", + "Added__username__to_this_team": "ha aggiunto @{{user_added}} a questo Team", "Adding_OAuth_Services": "In fase di aggiunta dei Servizi OAuh ", "Adding_permission": "In fase di aggiunta dei permessi", + "Adjustable_layout": "Layout regolabile", "Adding_user": "In fase di aggiunta dell'utente", "Additional_emails": "Email aggiuntive", "Additional_Feedback": "Feedback aggiuntivo", @@ -248,6 +290,7 @@ "Admin_disabled_encryption": "Il tuo amministratotre non ha abilitato la criptazione E2E", "Admin_Info": "Informazioni di amministrazione", "Administration": "Amministrazione", + "Adjustable_font_size": "Dimensione dei caratteri regolabile", "Adult_images_are_not_allowed": "Non sono ammesse immagini per adulti", "After_OAuth2_authentication_users_will_be_redirected_to_this_URL": "Dopo l'autenticazione OAuth2, gli utenti saranno reindirizzati a questo URL", "Agent": "Operatore", @@ -289,6 +332,8 @@ "Analytics_features_users_Description": "Traccia eventi personalizzati relativi alle azioni relative agli utenti (tempi di reimpostazione della password, il cambiamento d'immagine del profilo, ecc).", "Analytics_Google": "Google Analytics", "Analytics_Google_id": "ID monitoraggio", + "Analytics_page_briefing_first_paragraph": "Rocket.Chat raccoglie dati di utilizzo anonimi, come l'utilizzo delle funzionalità e la durata delle sessioni, per migliorare il prodotto per tutti.", + "Analytics_page_briefing_second_paragraph": "Proteggiamo la vostra privacy non raccogliendo mai dati personali o sensibili. Questa sezione mostra ciò che viene raccolto, rafforzando il nostro impegno alla trasparenza e alla fiducia.", "and": "e", "And_more": "E altri {{length}}", "Animals_and_Nature": "Animali & Natura", @@ -332,23 +377,56 @@ "App_author_homepage": "homepage dell'autore", "App_Information": "Informazioni sull'app", "App_Installation": "Installazione dell'app", + "App_Installation_Deprecation_Title": "Avviso di deprecation", + "App_Installation_Deprecation": "L'opzione \"Installa app da URL\" è deprecata e sarà rimossa nella prossima major release.", + "App_not_enabled": "App non attiva", + "App_not_found": "App non trovata", "App_status_auto_enabled": "Abilitato", "App_status_constructed": "Costruito", "App_status_disabled": "Disabilitato", "App_status_error_disabled": "Disabilitato: errore non rilevato", "App_status_initialized": "Initialized", + "App_status_invalid_license_disabled": "Disabilitato: Licenza non valida", "App_status_invalid_settings_disabled": "Disabilitato: configurazione necessaria", "App_status_manually_disabled": "Disabilitato: manualmente", "App_status_manually_enabled": "Abilitato", "App_status_unknown": "Sconosciuto", + "App_Store": "App Store", "App_support_url": "supporto url", + "App_Url_to_Install_From": "Installa da URL", + "App_Url_to_Install_From_File": "Installa da file", + "App_user_not_allowed_to_login": "Gli utenti dell'app non possono accedere direttamente.", "Appearance": "Aspetto", "Application_added": "Applicazione aggiunta", + "Application_delete_warning": "Non sarà possibile recuperare questa Applicazione!", "Application_Name": "Nome applicazione", "Application_updated": "Applicazione aggiornata", "Apply": "Applicare", "Apply_and_refresh_all_clients": "Applica e aggiorna tutti i clienti", "Apps": "Apps", + "Apps_context_explore": "Esplora", + "Apps_context_installed": "Installato/a", + "Apps_context_requested": "Richiesto/a", + "Apps_context_private": "App private", + "Apps_context_premium": "Premium", + "Apps_Count_Enabled_one": "{{count}} app abilitate", + "Apps_Count_Enabled_other": "{{count}} app abilitate", + "Apps_Count_Enabled_many": "{{count}} app abilitate", + "Private_Apps_Count_Enabled_one": "{{count}} applicazione private abilitate", + "Private_Apps_Count_Enabled_other": "{{count}} applicazioni private abilitate", + "Private_Apps_Count_Enabled_many": "{{count}} applicazioni private abilitate", + "Apps_Count_Enabled_tooltip": "Gli spazi di lavoro comunitari possono abilitare fino a {{number}} applicazioni {{context}}", + "Apps_disabled_when_Premium_trial_ended": "App disabilitate al termine della prova del piano Premium", + "Apps_Game_Center": "Game Center", + "Apps_Game_Center_Play_Game_Together": "@qui Giochiamo insieme a {{name}}!", + "Apps_Logs_TTL_7days": "7 giorni", + "Apps_Logs_TTL_14days": "14 giorni", + "Apps_Logs_TTL_30days": "30 giorni", + "Apps_Marketplace_pricingPlan_monthly": "{{price}} / mese", + "Apps_Marketplace_pricingPlan_monthly_perUser": "{{price}} / mese per utente", + "Apps_Marketplace_pricingPlan_monthly_trialDays": "{{price}} / mese - {{trialDays}} giorni di prova", + "Apps_Marketplace_pricingPlan_monthly_perUser_trialDays": "{{price}} / mese per utente - {{trialDays}} giorni di prova", + "Apps_Marketplace_Uninstall_Subscribed_App_Anyway": "Disinstalla comunque", "Apps_Settings": "Impostazioni dell'app", "Apps_WhatIsIt": "App: cosa sono?", "Apps_WhatIsIt_paragraph1": "Una nuova icona nell'area di amministrazione! Che cosa significa e quali sono le app?", @@ -360,12 +438,22 @@ "archive-room": "Sala degli archivi", "archive-room_description": "Autorizzazione all'archiviazione di un canale", "are_typing": "sta scrivendo", + "are_playing": "stanno giocando", + "is_playing": "sta giocando", + "are_uploading": "stanno caricando", + "are_recording": "stanno registrando", + "is_uploading": "sta caricando", + "is_recording": "sta registrando", "Are_you_sure": "Sei sicuro?", + "Are_you_sure_delete_department": "Sei sicuro di voler eliminare questo reparto? Questa azione non può essere annullata. Inserisci il nome del reparto per confermare.", "Are_you_sure_you_want_to_delete_your_account": "Sei sicuro di voler cancellare il tuo account?", "Are_you_sure_you_want_to_disable_Facebook_integration": "Sei sicuro di voler disabilitare l'integrazione di Facebook?", + "Are_you_sure_you_want_to_pin_this_message": "Sei sicuro di voler appuntare questo messaggio?", + "Are_you_sure_you_want_to_reset_the_name_of_all_priorities": "Sei sicuro di voler reimpostare il nome di tutte le priorità?", "Assign_admin": "Assegna amministratore", "assign-admin-role": "Assegna ruolo amministrativo", "assign-admin-role_description": "Autorizzazione ad assegnare il ruolo di amministratore ad altri utenti", + "assign-roles": "Assegna ruoli", "at": "a", "At_least_one_added_token_is_required_by_the_user": "Almeno un token aggiunto è richiesto dall'utente", "AtlassianCrowd": "Atlassian Crowd", @@ -381,16 +469,24 @@ "Author_Information": "Informazioni sull'autore", "Authorization_URL": "URL di autorizzazione", "Authorize": "Autorizza", + "Authorize_access_to_your_account": "Autorizza l'accesso al tuo account", + "Automatic_translation_not_available": "Traduzione automatica non disponibile", + "Automatic_translation_not_available_info": "Questa stanza ha la crittografia E2E attiva, la traduzione non può funzionare con i messaggi crittografati", "Auto_Load_Images": "Auto-caricamento immagini", "Auto_Translate": "Auto-traduci", + "Calls_in_queue": "{{calls}} chiamate in coda", "auto-translate": "Auto Translate", "auto-translate_description": "Autorizzazione a utilizzare lo strumento di traduzione automatica", "Automatic_Translation": "Traduzione automatica", "AutoTranslate": "Auto-traduci", "AutoTranslate_APIKey": "API Key", "AutoTranslate_Change_Language_Description": "Cambiando la lingua della traduzione automatica non traduce i messaggi precedenti.", + "AutoTranslate_Disabled_for_room": "Traduzione automatica disabilitata per #{{roomName}}", "AutoTranslate_Enabled": "Abilita traduzione automatica", "AutoTranslate_Enabled_Description": "Abilitando la traduzione automatica consentirà alle persone con il permesso `auto-traduci` di avere tutti i messaggi automaticamente tradotti nella loro lingua. Potranno essere richieste delle tariffe, leggere la [Documentazione di Google](https://cloud.google.com/translate/pricing)", + "AutoTranslate_AutoEnableOnJoinRoom": "Traduzione automatica per le lingue non predefinite", + "AutoTranslate_AutoEnableOnJoinRoom_Description": "Se attivo, ogni volta che un utente con una preferenza linguistica diversa da quella predefinita dell'area di lavoro si unisce a una stanza, questa verrà tradotta automaticamente per lui.", + "AutoTranslate_language_set_to": "Lingua di traduzione automatica impostata su {{language}}", "Available": "Disponibile", "Available_agents": "Operatori disponibili", "Avatar": "Avatar", @@ -401,6 +497,7 @@ "Away": "Assente", "Back": "Indietro", "Back_to_applications": "Torna alle applicazioni", + "Back_to_calendar": "Torna al calendario", "Back_to_chat": "Torna alla chat", "Back_to_integration_detail": "Torna ai dettagli della integrazione", "Back_to_integrations": "Torna alle integrazioni", @@ -411,9 +508,16 @@ "ban-user": "Divieto all'utente", "ban-user_description": "Autorizzazione a vietare un utente da un canale", "Beta_feature_Depends_on_Video_Conference_to_be_enabled": "Funzionalità Beta. Dipende se la Video Conferenza è abilitata.", + "Block_Multiple_Failed_Logins_Attempts_Until_Block_By_Ip": "Numero di tentativi falliti prima di bloccare l'indirizzo IP", + "Block_Multiple_Failed_Logins_Attempts_Until_Block_by_User": "Numero di tentativi falliti prima di bloccare l'utente", + "Block_Multiple_Failed_Logins_Time_To_Unblock_By_Ip_In_Minutes": "Durata del blocco dell'indirizzo IP (in minuti)", + "Block_Multiple_Failed_Logins_Time_To_Unblock_By_Ip_In_Minutes_Description": "Questo è il tempo entro il quale l'indirizzo IP è bloccato e il tempo in cui i tentativi falliti possono avvenire prima che il contatore si azzeri", + "Block_Multiple_Failed_Logins_Time_To_Unblock_By_User_In_Minutes": "Durata del blocco utente (in minuti)", + "Block_Multiple_Failed_Logins_Time_To_Unblock_By_User_In_Minutes_Description": "È il tempo in cui l'utente è bloccato e il tempo in cui i tentativi falliti possono avvenire prima che il contatore si azzeri", "Block_User": "Blocca Utente", "Blockchain": "Blockchain", "Body": "Corpo", + "Bold": "Grassetto", "bot_request": "Richiesta bot", "BotHelpers_userFields": "Campi utente", "BotHelpers_userFields_Description": "File CSV con i campi utente a cui il bot può accedere tramite i metodi helper.", @@ -426,12 +530,48 @@ "Build_Environment": "Costruisci l'ambiente", "bulk-register-user": "Crea canali di massa", "bulk-register-user_description": "Autorizzazione a creare canali alla rinfusa", + "Bundles": "Pacchetti", + "Business_hours_is_disabled": "Gli orari di lavoro sono disattivi", + "Business_hours_is_disabled_description": "Attiva gli orari di lavoro nel pannello di amministrazione dell'area di lavoro per far sapere ai clienti quando siete disponibili e quando possono aspettarsi una risposta.", "busy": "occupato", "Busy": "Occupato", + "Buy": "Acquista", + "By": "Da", "by": "di", "cache_cleared": "Svuota Cache", + "Calendar_MeetingUrl_Regex": "Espressione regolare dell'url della riunione", + "Calendar_MeetingUrl_Regex_Description": "Espressione utilizzata per rilevare gli URL delle riunioni nelle descrizioni degli eventi. Verrà utilizzato il primo gruppo corrispondente con un URL valido. Gli URL codificati in HTML saranno decodificati automaticamente.", + "Calendar_settings": "Impostazioni del calendario", + "Call": "Chiamata", + "Call_again": "Richiama", + "Call_back": "Richiama", + "Call_not_found": "Chiamata non trovata", + "Call_not_found_error": "Questo può accadere quando l'URL di chiamata non è valido o si verificano problemi di connessione. Verifica l'origine dell'URL di chiamata e riprova, oppure rivolgiti all'amministratore dell'area di lavoro se il problema persiste", + "Calling": "Chiamata in corso", + "Call_ended": "Chiamata terminata", + "Calls": "Chiamate", + "Calls_in_queue_zero": "La coda è vuota", + "Calls_in_queue_one": "{{count}} chiamata in coda", + "Calls_in_queue_other": "{{count}} chiamate in coda", + "Call_number_premium_only": "Numero di chiamata (solo per piani Premium)", + "call-management": "Gestione delle chiamate", + "call-management_description": "Permesso di avviare una riunione", + "Call_ongoing": "Chiamata in corso", + "Call_started": "Chiamata iniziata", + "Call_was_not_answered": "La chiamata non ha avuto risposta", + "Caller": "Chiamante", + "Caller_Id": "ID chiamante", + "Camera_access_not_allowed": "L'accesso alla telecamera non è stato consentito, controlla le impostazioni del browser.", + "Cam_on": "Camera accesa", + "Cam_off": "Camera spenta", "Cancel": "Annulla", "Cancel_message_input": "Annulla", + "Cancel_subscription": "Annulla l'abbonamento", + "Create_department": "Crea un reparto", + "Create_direct_message": "Crea un messaggio diretto", + "Create_tag": "Crea un tag", + "Create_trigger": "Crea un trigger", + "Create_SLA_policy": "Crea una policy SLA", "Cannot_invite_users_to_direct_rooms": "Impossibile invitare gli utenti nei canali diretti", "Cannot_open_conversation_with_yourself": "Impossibile aprire una conversazione con se stessi", "CAS_autoclose": "Chiudi automaticamente il popup di login", @@ -471,11 +611,20 @@ "Channels": "Canali", "Channels_are_where_your_team_communicate": "I canali sono dove la tua squadra comunica", "Channels_list": "Elenco dei canali pubblici", + "Chart": "Grafico", "Chat_button": "Pulsante chat", "Chat_closed": "Chat chiusa", "Chat_closed_by_agent": "Chat chiusa dall'agente", "Chat_closed_successfully": "Chat chiusa con successo", "Chat_Now": "Chatta adesso", + "chat_on_hold_due_to_inactivity": "Questa chat è in attesa a causa di inattività", + "Chat_On_Hold": "Chat in attesa", + "Chat_On_Hold_Successfully": "Questa chat è stata messa in attesa con successo", + "Chat_queued": "Chat aggiunta alla coda", + "Chat_removed": "Chat rimossa", + "Chat_resumed": "Chat ripresa", + "Chat_start": "Avvio della chat", + "Chat_started": "Chat avviata", "Chat_window": "Finestra chat", "Chatops_Enabled": "Abilita Chatops", "Chatops_Title": "Pannello Chatops", @@ -484,29 +633,51 @@ "Choose_messages": "Scegli i messaggi", "Choose_the_alias_that_will_appear_before_the_username_in_messages": "Scegli l'alias che verrà visualizzato prima del nome utente nei messaggi.", "Choose_the_username_that_this_integration_will_post_as": "Scegliere il nome utente che verrà usato per questa integrazione.", + "Choose_users": "Scegli gli utenti", "clean-channel-history": "Pulisci cronologia canale", "clean-channel-history_description": "Autorizzazione a cancellare la cronologia dai canali", "clear": "Pulisci", "Clear_all_unreads_question": "Marcare come letti tutti i messaggi non letti?", "clear_cache_now": "Svuota la cache ora", "clear_history": "Svuota lo storico", + "clear-oembed-cache": "Cancella la cache di OEmbed", "Click_here": "Clicca qui", "Click_here_for_more_info": "Clicca qui per altre informazioni", + "Click_here_to_clear_the_selection": "Fai clic qui per cancellare la selezione", + "Click_here_to_enter_your_encryption_password": "Fai clic qui per inserire la password di crittografia", + "Click_here_to_view_and_copy_your_password": "Fai clic qui per visualizzare e copiare la password.", "Click_the_messages_you_would_like_to_send_by_email": "Fare clic sui messaggi che si desidera inviare per e-mail", "Click_to_join": "Clicca per iscriverti!", + "Click_to_load": "Fai clic per caricare", "Client_ID": "ID cliente", "Client_Secret": "Chiave segreta", + "Client": "Client", "Clients_will_refresh_in_a_few_seconds": "I clienti saranno aggiornati in pochi secondi", "close": "chiudi", "Close": "Chiudi", + "Close_chat": "Chiudi la chat", "close-livechat-room": "Chiudi Livechat Room", "close-livechat-room_description": "Autorizzazione a chiudere l'attuale canale di LiveChat", "Close_menu": "Chiudi menu", "close-others-livechat-room": "Chiudi Livechat Room", "close-others-livechat-room_description": "Autorizzazione a chiudere altri canali di LiveChat", + "Close_Window": "Chiud la finestra", "Closed": "Chiuso", + "Closed_At": "Chiusa a", + "Closed_automatically": "Chiusa automaticamente dal sistema", "Closed_by_visitor": "Chiuso dal visitatore", "Closing_chat": "In fase di chiusura della chat", + "Closing_chat_message": "Messaggio di chiusura della chat", + "Cloud_register_success": "Il tuo spazio di lavoro è stato registrato con successo!", + "Cloud_registration_required": "Registrazione richiesta", + "Cloud_registration_required_link_text": "Fai clic qui per registrare il proprio spazio di lavoro.", + "Cloud_resend_email": "Reinvia e-mail", + "Cloud_Service_Agree_PrivacyTerms": "Accordo sui termini e sulla privacy del Servizio cloud", + "Cloud_troubleshooting": "Risoluzione dei problemi", + "Cloud_update_email": "Aggiorna e-mail", + "Cloud_what_is_it": "Che cos'è questo?", + "Copy_password": "Copia password", + "Cloud_what_is_it_services_like": "Servizi come:", "Collaborative": "Collaborativo", "Collapse_Embedded_Media_By_Default": "Riduci i media incorporati (embedded) di default", "color": "Colore", @@ -516,32 +687,46 @@ "Common_Access": "Accesso comune", "Community": "Comunità", "Condensed": "Ridotta", + "Commit_details": "Dettagli commit", "Computer": "Computer", + "Configure_Incoming_Mail_IMAP": "Configura la posta in arrivo (IMAP)", + "Configure_Outgoing_Mail_SMTP": "Configura la posta in uscita (SMTP)", "Confirm": "Conferma", "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", + "Connected": "Connesso", "Connection_Closed": "Connessione chiusa", "Connection_Reset": "Reset della connessione", "Consulting": "Consulenza", "Contact": "Contatto", + "Contacts": "Contatti", + "Contact_Name": "Nome del contatto", "Contains_Security_Fixes": "Contiene correzioni di sicurezza", "Content": "Contenuto", "Continue": "Continuare", "Continuous_sound_notifications_for_new_livechat_room": "Notifiche continue del suono per la nuova stanza livechat", "Conversation": "Conversazione", "Conversation_closed": "Conversazione chiusa: {{comment}}.", + "Conversation_closed_without_comment": "Conversazione chiusa", "Conversation_finished": "Conversazione terminata", "Conversation_finished_message": "Messaggio di conversazione terminato", "conversation_with_s": "la conversazione con %s", + "Conversations": "Conversazioni", "Convert_Ascii_Emojis": "Converti gli ASCII in Emoji", + "Converted__roomName__to_team": "ha convertito #{{roomName}} in una squadra", + "Converted__roomName__to_channel": "ha convertito #{{roomName}} in un Channel", + "Converted__roomName__to_a_team": "ha convertito #{{roomName}} in un Team", + "Converted__roomName__to_a_channel": "ha convertito #{{roomName}} in canale", "Copied": "Copiato", "Copy": "Copia", + "Copy_text": "Copia testo", "Copy_to_clipboard": "Copia negli appunti", "COPY_TO_CLIPBOARD": "COPIA NEGLI APPUNTI", "Count": "Conteggio", + "Counters": "Contatori", "Country": "Nazione", "Country_Afghanistan": "Afghanistan", "Country_Albania": "Albania", @@ -785,25 +970,33 @@ "Country_Zambia": "Zambia", "Country_Zimbabwe": "Zimbabwe", "Create": "Crea", + "Create_custom_field": "Crea campo personalizzato", "Create_channel": "Crea canale", + "Create_channels": "Crea canali", "Create_A_New_Channel": "Crea un nuovo canale", "Create_new": "Crea nuovo", "Create_unique_rules_for_this_channel": "Crea regole uniche per questo canale", + "Create_unit": "Crea una unit", "create-c": "Crea canali pubblici", "create-c_description": "Autorizzazione a creare canali pubblici", "create-d": "Crea messaggi diretti", "create-d_description": "Autorizzazione a iniziare i messaggi diretti", "create-p": "Crea canali privati", "create-p_description": "Autorizzazione a creare canali privati", + "create-team": "Crea squadra", "create-user": "Creare un utente", "create-user_description": "Autorizzazione a creare utenti", + "Created": "Creato", + "Created_as": "Creato come", "Created_at": "Creato a", "Created_at_s_by_s": "Creato a %s da %s", "Created_at_s_by_s_triggered_by_s": "Creato alle %s da %s scatenato da %s", + "Created_by": "Creato da", "CRM_Integration": "Integrazione CRM", "CROWD_Reject_Unauthorized": "Rifiuta non autorizzati", "Crowd_Remove_Orphaned_Users": "Rimuovi utenti orfani", "Crowd_sync_interval_Description": "L'intervallo tra le sincronizzazioni. Esempio \"ogni 24 ore\" o \"il primo giorno della settimana\", altri esempi su [Cron Text Parser] (http://bunkat.github.io/later/parsers.html#text)", + "CSV": "CSV", "Current_Chats": "Chat attuali", "Current_Status": "Stato attuale", "Custom": "Personalizzato", @@ -821,6 +1014,7 @@ "Custom_Fields": "Campi personalizzati", "Custom_oauth_helper": "Quando si imposta l'OAuth Provider, è necessario impostare un URL Callback. Usa
          %s
          .", "Custom_oauth_unique_name": "Nome univoco dell'OAuth personalizzato", + "Custom_roles": "Ruoli personalizzati", "Custom_Script_Logged_In": "Script personalizzato per gli utenti collegati", "Custom_Script_Logged_Out": "Script personalizzato per gli utenti scollegati", "Custom_Scripts": "Script personalizzati", @@ -834,21 +1028,32 @@ "Custom_Status": "Stato personalizzato", "Custom_Translations": "Traduzioni personalizzate", "Custom_Translations_Description": "Dovrebbe essere un JSON valido dove le chiavi sono le lingue che contengono un dizionario delle traduzioni. Esempio: \n `{\"en\": {\"Channels\": \"Rooms\"},\"pt\": {\"Channels\": \"Salas\"}}`", - "Customize": "Personalizzare", + "Customize": "Personalizza", + "Customize_Content": "Personalizza contenuto", "CustomSoundsFilesystem": "Filesystem suoni personalizzati", "Dashboard": "Dashboard", "Date": "Data", "Date_From": "Da", "Date_to": "A", + "DAU_value": "DAU {{value}}", "days": "giorni", + "Days": "Giorni", "DB_Migration": "Migrazione database", "DB_Migration_Date": "Data di migrazione del database", "Deactivate": "Disattiva", "Decline": "Rifiuta", + "default": "predefinito", "Default": "Predefinito", + "Default_provider": "Provider predefinito", + "Default_value": "Valore predefinito", "Delete": "Cancella", + "Delete_account": "Cancella account", + "Delete_account?": "Cancellare l'account?", + "Delete_Department?": "Cancellare il reparto?", "Delete_message": "Cancella messaggio", "Delete_my_account": "Cancella il mio account", + "Delete_Role_Warning": "Questa operazione è irreversibile", + "Delete_Role_Warning_Not_Enterprise": "Questa operazione è irreversibile. Non sarà possibile creare un nuovo ruolo personalizzato, poiché questa funzione non è più disponibile per il piano attuale.", "Delete_Room_Warning": "Cancellando un canale rimuoverà tutti i messaggi postati all'interno del canale. Questa azione non può essere annullata.", "Delete_User_Warning": "Cancellando un utente verranno cancellati anche tutti i suoi messaggi. Questa operazione non può essere annullata.", "Delete_User_Warning_Delete": "Cancellando un utente verranno cancellati anche tutti i suoi messaggi. Questa operazione non può essere annullata.", @@ -862,16 +1067,21 @@ "delete-message_description": "Permesso di cancellare un messaggio all'interno del canale", "delete-p": "Cancella canali privati", "delete-p_description": "Permesso di cancellare canali privati", + "delete-team": "Elimina squadra", "delete-user": "Cancella utente", "delete-user_description": "Permesso di cancellare gli utente", "Deleted": "Cancellato!", + "Deleted_user": "Utente cancellato", "Department": "Dipartimento", + "Department_name": "Nome del reparto", "Department_not_found": "Dipartimento non trovato", "Department_removed": "Dipartimento rimosso", "Departments": "Dipartimenti", "Deployment_ID": "ID Installazione", + "Deployment": "Installazione", "Description": "Descrizione", "Desktop": "Desktop", + "Desktop_apps": "Applicazioni desktop", "Desktop_Notification_Test": "Esegui test di notifica desktop", "Desktop_Notifications": "Notifiche desktop", "Desktop_Notifications_Default_Alert": "Avviso predefinito notifiche desktop", @@ -879,12 +1089,26 @@ "Desktop_Notifications_Duration": "Durata notifiche desktop", "Desktop_Notifications_Duration_Description": "Durata in secondi della notifica desktop. Può influenzare il Centro di Notifiche di OS X. Inserisci 0 per utilizzare le impostazioni di default del browser e per non influire sul Centro di Notifiche di OS X.", "Desktop_Notifications_Enabled": "Le notifiche desktop sono abilitate", + "Unseen_features": "Funzionalità inedite", + "Details": "Dettagli", "line": "linea", + "Device_Management_IP": "IP", + "Device_Management_OS": "OS", + "Device_ID": "ID dispositivo", + "Device_Info": "Info sul dispositivo", + "Device_Logged_Out": "Dispositivo disconnesso", + "Devices": "Dispositivi", + "Devices_Set": "Set di dispositivi", + "Device_settings": "Impostazioni del dispositivo", + "Dialed_number_doesnt_exist": "Il numero selezionato non esiste", + "Dialed_number_is_incomplete": "Il numero selezionato non è completo", "Different_Style_For_User_Mentions": "Stile diverso per le menzioni dell'utente", "Livechat_Facebook_API_Key": "Chiave API OmniChannel", + "Direct": "Diretto", "Livechat_Facebook_API_Secret": "OmniChannel API Secret", "Livechat_Facebook_Enabled": "Integrazione Facebook abilitata", "Direct_message_someone": "Invia un messaggio diretto", + "Direct_message_you_have_joined": "Conversazione diretta con", "Direct_Messages": "Messaggi privati", "Direct_Reply": "Risposta diretta", "Direct_Reply_Debug": "Debug Direct Reply", @@ -917,22 +1141,30 @@ "Discussion_target_channel": "Canale o gruppo padre", "Discussion_target_channel_prefix": "Stai creando una discussione in ", "Discussion_title": "Crea una nuova discussione", + "discussion-created": "{{message}}", "Discussions": "Discussioni", "Display_offline_form": "Mostra il modulo offline", "Display_unread_counter": "Mostra il numero di messaggi non letti", "Displays_action_text": "Mostra il testo di azione", + "Do_It_Later": "Fallo dopo", "Do_not_display_unread_counter": "Non visualizzare alcun contatore di questo canale", + "Do_not_provide_this_code_to_anyone": "Non dare questo codice a nessuno.", + "Do_Nothing": "Non fare nulla", + "Do_nothing": "Non fare nulla", "Do_you_want_to_accept": "Vuoi accettare?", "Do_you_want_to_change_to_s_question": "Vuoi cambiare in %s?", + "Documentation": "Documentazione", "Document_Domain": "Dominio del documento", "Domain": "Dominio", "Domain_added": "Dominio aggiunto", "Domain_removed": "Dominio rimosso", "Domains": "Domini", "Domains_allowed_to_embed_the_livechat_widget": "Lista di domini permessi per l'embed del widget livechat. Lascia vuoto per permettere tutti i domini.", + "Done": "Fatto", "Dont_ask_me_again": "Non chiedermelo più!", "Dont_ask_me_again_list": "Non chiedermelo di nuovo", "Download": "Scarica", + "Download_Destkop_App": "Scarica l'applicazione per desktop", "Download_My_Data": "Scarica i miei dati", "Download_Snippet": "Scarica", "Drop_to_upload_file": "Rilascia per caricare il file", @@ -944,15 +1176,21 @@ "Duplicate_archived_private_group_name": "Un gruppo privato archiviato con il nome ' %s' già esiste", "Duplicate_channel_name": "Un canale con il nome '%s' già esiste", "Markdown_Marked_GFM": "Abilita GFM contrassegnato", + "Duplicate_file_name_found": "È stato trovato un nome di file duplicato.", "Markdown_Marked_Pedantic": "Abilita Marked Pedantic", "Markdown_Marked_SmartLists": "Abilita elenchi intelligenti contrassegnati", "Duplicate_private_group_name": "Un gruppo privato con il nome ' %s' già esiste", "Markdown_Marked_Smartypants": "Abilita gli Smartypants contrassegnati", + "Duplicated_Email_address_will_be_ignored": "Gli indirizzi e-mail duplicati saranno ignorati.", "Markdown_Marked_Tables": "Abilita tabelle contrassegnate", + "duplicated-account": "Account duplicato", "E2E Encryption": "Crittografia E2E", "Markdown_Parser": "Markdown Parser", "Markdown_SupportSchemesForLink": "Schemi di supporto Markdown per i link", "Markdown_SupportSchemesForLink_Description": "Elenco separato da virgole per i programmi consentiti", + "E2E_enable": "Attiva E2E", + "E2E_disable": "Disattivare E2E", + "E2E_Enabled": "E2E abilitato", "E2E_Encryption_Password_Change": "Cambia la password di cifratura", "Edit": "Modifica", "Edit_Custom_Field": "Modifica campo personalizzato", @@ -960,10 +1198,15 @@ "Message_AllowSnippeting": "Permetti Messaggi Snippet", "Edit_Invite": "Modifica invito", "Edit_previous_message": "`%s` - Modifica messaggio precedente", + "Edit_Priority": "Modifica priorità", + "Edit_SLA_Policy": "Modifica policy SLA", "Edit_Status": "Modifica stato", + "Edit_Tag": "Modifica tag", "Edit_Trigger": "Modifica Trigger", + "Edit_Unit": "Modifica unit", "Message_Attachments_GroupAttach": "Pulsanti di collegamento di gruppo", "Message_Attachments_GroupAttachDescription": "Questo raggruppa le icone sotto un menu espandibile. Prende meno spazio sullo schermo.", + "Edit_User": "Modifica utente", "edit-message": "Modifica messaggio", "edit-message_description": "Autorizzazione a modificare un messaggio all'interno di una stanza", "edit-other-user-active-status": "Modifica lo stato attivo di altri utenti", @@ -974,14 +1217,17 @@ "edit-other-user-password_description": "Autorizzazione a modificare le password di altri utenti. Richiede l'autorizzazione modifica-altro-utente-info.", "edit-privileged-setting": "Modifica impostazioni privilegiate", "edit-privileged-setting_description": "Autorizzazione a modificare le impostazioni", + "edit-team": "Squadra di modifica", "edit-room": "Modifica stanza", "edit-room_description": "Autorizzazione a modificare il nome, l'argomento, il tipo di una stanza (stato privato o pubblico) e lo stato (attivo o archiviato)", "edit-room-retention-policy": "Modifica politica di conservazione della stanza", "edit-room-retention-policy_description": "Autorizzazione a modificare il criterio di conservazione di una stanza, per eliminare automaticamente i messaggi al suo interno", "multi_line": "multi linea", "edited": "modificato", + "Editing_message_hint": "esc per annullare - enter per salvare", "Editing_room": "In fase di modifica del canale", "Editing_user": "In fase di modifica dell'utente", + "Editor": "Editore", "Message_ShowEditedStatus": "Mostra lo stato modificato", "Education": "educazione", "Message_ShowFormattingTips": "Mostra suggerimenti di formattazione", @@ -990,17 +1236,28 @@ "Email_already_exists": "Email già esistente", "Email_body": "Corpo email", "Email_Change_Disabled": "Il tuo amministratore di Rocket.Chat ha disattivato il cambio dell'email", + "Email_changed_section": "Indirizzo e-mail modificato", "Email_Footer_Description": "È possibile utilizzare i seguenti marcatori: \n - `[Site_Name]` e `[Site_URL]` rispettivamente per il nome dell'applicazione e dell'URL. ", "Email_from": "Mittente", "Email_Header_Description": "È possibile utilizzare i seguenti marcatorii: \n - `[Site_Name]` e `[Site_URL]` rispettivamente per il nome dell'applicazione e l'URL. ", + "Email_Inbox": "Posta in arrivo", + "Email_Inboxes": "Caselle di posta elettronica", + "Email_Inbox_has_been_added": "È stata aggiunta la casella di posta elettronica", + "Email_Inbox_has_been_removed": "La casella di posta elettronica è stata rimossa", "Email_Notification_Mode": "Notifiche email offline", "Email_Notification_Mode_All": "Ogni menzione/DM", "Email_Notification_Mode_Disabled": "Disabilitato", "Email_or_username": "Email o nome utente", + "Enterprise_capability": "Funzionalità Enterprise", "Email_Placeholder": "Inserisci il tuo indirizzo email...", "Email_Placeholder_any": "Per favore inserisci gli indirizzi email ...", + "email_style_label": "Stile e-mail", + "Enterprise_Description": "Aggiorna manualmente la tua licenza Premium.", "Email_subject": "Oggetto", + "Enterprise_License": "Licenza Enterprise", "Email_verified": "Email verificata", + "Enterprise_Only": "Solo Enterprise", + "Email_sent": "Email inviata", "Emoji": "Emoji", "EmojiCustomFilesystem": "Filesystem personalizzato per le Emoji", "Empty_title": "Titolo vuoto", @@ -1009,9 +1266,17 @@ "Enable_Desktop_Notifications": "Abilita notifiche desktop", "Enable_Svg_Favicon": "Abilita Favicon SVG", "Enable_two-factor_authentication": "Abilita autenticazione a due fattori", + "Enable_unlimited_apps": "Abilita app illimitate", "Enabled": "Abilitato", "Encrypted": "Crittografato", "Encrypted_message": "Messaggio cifrato", + "End": "Fine", + "End_call": "Termina chiamata", + "End_conversation": "Termina conversazione", + "Explore": "Esplora", + "Explore_marketplace": "Esplora Marketplace", + "Export": "Esporta", + "End_Call": "Termina chiamata", "End_OTR": "Termina OTR", "Enter_a_regex": "Inserisci una regex", "Enter_a_room_name": "Inserisci un nome al canale", @@ -1023,6 +1288,8 @@ "Enter_name_here": "Inserisci il nome qui", "Enter_Normal": "Modalità normale (inviato con Invio)", "Enter_to": "Invio per", + "Premium_License": "Licenza Premium", + "Premium_only": "Solo Premium", "Entertainment": "Divertimento", "Error": "Errore", "Error_404": "Errore 404", @@ -1035,19 +1302,24 @@ "error-archived-duplicate-name": "C'è un canale archiviato con il nome '{{room_name}}'", "error-avatar-invalid-url": "URL avatar non valido: {{url}}", "error-avatar-url-handling": "Errore durante la manipolazione dell'impostazione dell'avatar da un URL ({{url}}) per {{username}}", + "error-business-hour-finish-time-before-start-time": "L'orario di fine deve essere successivo all'orario di inizio", "error-cant-invite-for-direct-room": "Impossibile invitare gli utenti in canali diretti", "error-channels-setdefault-is-same": "L'impostazione predefinita del canale è la stessa di come verrebbe modificata.", "error-channels-setdefault-missing-default-param": "È richiesto \"body\" predefinito bodyParam", "error-could-not-change-email": "Impossibile cambiare l'email", "error-could-not-change-name": "Impossibile cambiare il nome", "error-could-not-change-username": "Impossibile cambiare il nome utente", + "error-comment-is-required": "È necessario un commento", "error-delete-protected-role": "Impossibile cancellare un ruolo protetto", "error-department-not-found": "Sezione non trovata", + "error-department-removal-disabled": "La rimozione di un reparto è disabilitata dall'amministrazione, contatta l'amministratore", "error-direct-message-file-upload-not-allowed": "Condivisione dei file non permessa nei messaggi diretti", "error-duplicate-channel-name": "Un canale con il '{{channel_name}}' già esiste", + "error-duplicate-priority-name": "Esiste già una priorità con lo stesso nome", "error-edit-permissions-not-allowed": "Le autorizzazioni di modifica non sono consentite", "error-email-domain-blacklisted": "Il dominio dell'email è nella lista nera", "error-email-send-failed": "Errore nell'invio della mail: {{message}}", + "error-failed-to-delete-department": "Eliminazione del reparto fallita", "error-field-unavailable": "{{field}} è già in uso :(", "error-file-too-large": "Il file è troppo grande", "error-importer-not-defined": "L'importatore non è stato definito correttamente, manca la classe di importazione.", @@ -1059,6 +1331,7 @@ "error-invalid-channel-start-with-chars": "Canale non valido. Inizia con @ o #", "error-invalid-custom-field": "Campo personalizzato non valido", "error-invalid-custom-field-name": "Nome non valido per il campo personalizzato. Utilizza solo lettere, numeri, trattini e trattini bassi.", + "error-invalid-custom-field-value": "Valore non valido per il campo {{field}}", "error-invalid-date": "Dati forniti non validi", "error-invalid-description": "Descrizione non valida", "error-invalid-domain": "Dominio non valido", @@ -1068,6 +1341,7 @@ "error-invalid-file-type": "Tipo di file non valido", "error-invalid-file-width": "Larghezza file non valido", "error-invalid-from-address": "Hai specificato un indirizzo mittente non valido.", + "error-invalid-image-url": "URL immagine non valido", "error-invalid-integration": "Integrazione non valida", "error-invalid-message": "Messaggio non valido", "error-invalid-method": "Metodo non valido", @@ -1108,28 +1382,43 @@ "error-remove-last-owner": "Questo è l'ultimo proprietario. Imposta un nuovo proprietario prima di rimuoverlo.", "error-role-in-use": "Impossibile cancellare il ruolo perché è in uso", "error-role-name-required": "Il nome del ruolo è richiesto", + "error-room-already-closed": "La stanza è già chiusa", "error-room-is-not-closed": "La stanza non è chiusa", + "error-room-is-already-on-hold": "Errore! La stanza è già in attesa", "error-the-field-is-required": "ll campo {{field}} è richiesto", "error-this-is-not-a-livechat-room": "Questa non è una stanza Livechat", "error-too-many-requests": "Errore, troppe richieste. Si prega di rallentare. È necessario attendere {{seconds}} secondi prima di riprovare.", + "error-user-deactivated": "L'utente non è attivo", "error-user-has-no-roles": "L'utente non ha ruoli", "error-user-is-not-activated": "L'utente non è stato attivato", + "error-user-is-offline": "L'utente è offline", "error-user-limit-exceeded": "Il numero di utenti che stai provando ad invitare a #channel_name eccede il limite impostato dall'amministratore", "error-user-not-in-room": "L'utente non è in questo canale", "error-user-registration-disabled": "La registrazione utente è disattivata", "error-user-registration-secret": "La registrazione utente è consentita solo tramite URL segreto", + "error-unable-to-update-priority": "Impossibile aggiornare la priorità", "error-you-are-last-owner": "Tu sei l'ultimo proprietario. Si prega di impostare il nuovo proprietario prima di lasciare il canale.", + "You_do_not_have_permission_to_execute_this_command": "Non hai permessi sufficienti per eseguire il comando: `/{{command}}`", + "You_have_reached_the_limit_active_costumers_this_month": "Hai raggiunto il numero massimo di clienti attivi questo mese", "Esc_to": "Esc per", + "Estimated_wait_time": "Tempo di attesa stimato", + "Estimated_wait_time_in_minutes": "Tempo di attesa stimato (tempo in minuti)", + "Event_notifications": "Notifiche di eventi", "Event_Trigger": "Evento scatenante", "Event_Trigger_Description": "Selezione quale tipo di evento verrà scatenato con questa Integrazione WebHook in uscita", "every_5_minutes": "Una volta ogni 5 minuti", "every_10_seconds": "Una volta ogni 10 secondi", + "every_30_seconds": "Una volta ogni 30 secondi", + "every_10_minutes": "Una volta ogni 10 minuti", "every_30_minutes": "Una volta ogni 30 minuti", "every_day": "Una volta al giorno", "every_hour": "Una volta ogni ora", "every_minute": "Una volta ogni minuto", "every_second": "Una volta al secondo", "every_six_hours": "Una volta ogni 6 ore", + "every_12_hours": "Una volta ogni 12 ore", + "every_24_hours": "Una volta ogni 24 ore", + "every_48_hours": "Una volta ogni 48 ore", "Everyone_can_access_this_channel": "Tutti possono accedere a questo canale", "Example_s": "Esempio: %s", "except_pinned": "(eccetto quelli che sono bloccati)", @@ -1138,24 +1427,40 @@ "Exclude_pinned": "Escludere i messaggi aggiunti", "Execute_Synchronization_Now": "Esegui sincronizzazione ora", "Expiration_(Days)": "Scadenza (giorni)", + "Export_Messages": "Esporta messaggi", "Export_My_Data": "Esporta i miei dati", + "expression": "Espressione", + "Extensions": "Estensioni", "External_Queue_Service_URL": "URL servizio coda esterna", "External_Service": "Servizio esterno", + "Facebook": "Facebook", "Facebook_Page": "Pagina Facebook", + "Failed": "Fallito", + "Failed_to_activate_invite_token": "Impossibile attivare il token di invito", + "Failed_To_Download_Files": "Impossibile scaricare i file", "False": "Falso", "Favorite": "Favorito", "Favorite_Rooms": "Abilita canali preferiti", "Favorites": "Preferiti", + "Feature_preview": "Funzionalità in anteprima", + "featured": "in evidenza", + "Featured": "In primo piano", "Feature_Depends_on_Livechat_Visitor_navigation_as_a_message_to_be_enabled": "Questa funzione dipende da \"Invia cronologia di navigazione dei visitatori come messaggio\" per abilitare.", + "Features": "Funzionalità", + "Federation_Example_matrix_server": "Esempio: matrix.org", + "Federation_Public_key": "Chiave pubblica", "FEDERATION_Domain": "Dominio", + "FEDERATION_Public_Key": "Chiave pubblica", "FEDERATION_Status": "Stato", "Retry_Count": "Conteggio tentativi", "Field": "Campo", "Field_removed": "Campo rimosso", "Field_required": "Campo richiesto", + "File": "File", "File_exceeds_allowed_size_of_bytes": "Il file supera la dimensione consentita di {{size}} byte", "File_name_Placeholder": "Cerca file ...", "File_not_allowed_direct_messages": "Condivisione file non permessa nei messaggi diretti.", + "File_Path": "Percorso del file", "File_removed_by_automatic_prune": "File rimosso da prugna automatica", "File_removed_by_prune": "File rimosso da prugna", "File_type_is_not_accepted": "Il tipo di file non è accettato.", @@ -1163,6 +1468,9 @@ "files": "File", "Files": "File", "Files_only": "Rimuovere solo i file allegati, mantenere i messaggi", + "FileSize_Bytes": "{{fileSize}} byte", + "FileSize_KB": "{{fileSize}} KB", + "FileSize_MB": "{{fileSize}} MB", "FileUpload": "Caricamento file", "FileUpload_Disabled": "Caricamento file non permesso.", "FileUpload_Enabled": "Caricamento file abilitato", @@ -1174,6 +1482,7 @@ "FileUpload_GoogleStorage_AccessId_Description": "L'ID di accesso é generalmente in formato email, per esempio: \"`example-test@example.iam.gserviceaccount.com`\"", "FileUpload_GoogleStorage_Bucket": "Google Storage Bucket Name", "FileUpload_GoogleStorage_Bucket_Description": "Il nome del bucket dove i file saranno caricati.", + "FileUpload_GoogleStorage_ProjectId": "ID progetto", "FileUpload_GoogleStorage_Proxy_Avatars": "Proxy Avatars", "FileUpload_GoogleStorage_Proxy_Avatars_Description": "Trasmissioni di file avatar proxy tramite il server anziché accesso diretto all'URL del bene", "FileUpload_GoogleStorage_Proxy_Uploads": "Caricamenti proxy", @@ -1217,6 +1526,7 @@ "Financial_Services": "Servizi finanziari", "First_Channel_After_Login": "Primo canale dopo l'accesso", "Flags": "Bandiere", + "Follow_message": "Segui il messaggio", "Follow_social_profiles": "Segui i nostri profili sociali, fai un fork su github e condividi i tuoi pensieri su rocket.chat nella nostra bacheca Trello.", "Following": "Seguiti", "Fonts": "Caratteri", @@ -1231,13 +1541,16 @@ "Force_SSL_Description": "*Attenzione!* _Force SSL_ non dovrebbe mai essere usato con il reverse proxy. Se si dispone di un reverse proxy, si dovrebbe fare il redirect LÌ. Questa opzione esiste per ambienti come Heroku che non consentono la configurazione del re-indirizzamento del reverse proxy.", "force-delete-message": "Forza la cancellazione del messaggio", "force-delete-message_description": "Permesso di cancellare un messaggio bypassando tutte le restrizioni", + "Font_size": "Dimensione del carattere", "Forgot_password": "Password dimenticata", "Forgot_Password_Description": "Puoi usare i seguenti segnaposti: \n - `[Forgot_Password_Url]` per la URL del recupero password. \n - `[name]`, `[fname]`, `[lname]` rispettivamente per il nome completo dell'utente, nome or cognome. \n - `[email]` per la email dell'utente. \n - `[Site_Name]` e `[Site_URL]` rispettivamente per il nome della applicazione e la URL.", "Forgot_Password_Email": "Clicca qui per resettare la tua password.", "Forgot_Password_Email_Subject": "[Site_Name] - Recupero Password", "Forgot_password_section": "Password dimenticata", + "Format": "Formato", "Forward": "Inoltra", "Forward_chat": "Inoltra Chat", + "Forward_message": "Inoltra il messaggio", "Forward_to_department": "Inoltra al dipartimento", "Forward_to_user": "Inoltra all'utente", "Frequently_Used": "Usato frequentemente", @@ -1248,29 +1561,51 @@ "Gaming": "Gaming", "General": "Generale", "Generate_New_Link": "Genera nuovo link", + "Copy_link": "Copia link", + "get-password-policy-minLength-label": "Almeno {{limit}} caratteri", + "get-password-policy-maxLength-label": "Al massimo {{limit}} caratteri", + "get-password-policy-mustContainAtLeastOneLowercase-label": "Almeno una lettera minuscola", + "get-password-policy-mustContainAtLeastOneUppercase-label": "Almeno una lettera maiuscola", + "get-password-policy-mustContainAtLeastOneNumber-label": "Almeno un numero", + "get-password-policy-mustContainAtLeastOneSpecialCharacter-label": "Almeno un simbolo", + "meteor_status_reconnect_in_many": "riprovo tra {{count}} secondi...", "github_no_public_email": "Non hai un email publica sul tuo account GitHub", + "github_HEAD": "HEAD", "Give_a_unique_name_for_the_custom_oauth": "Dai un nome univoco per l'OAuth personalizzato", "strike": "barrato", "Give_the_application_a_name_This_will_be_seen_by_your_users": "Dai un nome all'applicazione. Sarà visibile agli utenti.", "Global": "Globale", "Global_purge_override_warning": "È in atto una politica di conservazione globale. Se si esce da \"Ignora criterio di conservazione globale\", è possibile solo applicare una politica più rigida rispetto alla politica globale.", "Global_Search": "Ricerca globale", + "Glossary_of_simplified_terms": "Glossario di termini semplificati", "Go_to_your_workspace": "Vai al tuo spazio di lavoro", + "Go_to_accessibility_and_appearance": "Vai a accessibilità e aspetto", + "Google_Meet_Premium_only": "Google Meet (solo Premium)", + "Hold_Call_Premium_only": "Attesa di chiamata (solo per i piani Premium)", "GoogleCloudStorage": "Google Cloud Storage", "GoogleNaturalLanguage_ServiceAccount_Description": "File JSON dell'account di servizio. Ulteriori informazioni sono disponibili [qui](https://cloud.google.com/natural-language/docs/common/auth#set_up_a_service_account)", "GoogleTagManager_id": "ID Google Tag Manager", + "Got_it": "Capito", "Government": "Governo", + "Grid_view": "Vista griglia", "Snippet_Messages": "Messaggi snippet", + "Group": "Gruppo", + "Group_by": "Gruppo per", "Group_by_Type": "Raggruppa per tipo", "snippet-message": "Messaggio di frammento", "snippet-message_description": "Autorizzazione a creare un messaggio snippet", + "Group_discussions": "Raggruppa discussioni", "Group_favorites": "Preferiti di gruppo", "Group_mentions_disabled_x_members": "Il gruppo cita `@ all` e` @ here` sono stati disabilitati per le stanze con più di {{total}} membri.", "Group_mentions_only": "Il gruppo menziona solo", + "Guest": "Ospite", "Hash": "Hash", "Header": "Testata", "Header_and_Footer": "Testata e Piè di pagina", + "Pharmaceutical": "Farmaceutico", + "Healthcare": "Assistenza sanitaria", "Helpers": "Helpers", + "Here_is_your_authentication_code": "Ecco il tuo codice di autenticazione:", "Hex_Color_Preview": "Anteprima colore esadecimale", "Hi": "Ciao", "Hi_username": "Ciao [name]", @@ -1280,6 +1615,7 @@ "Hide_flextab": "Nascondi la barra destra con un click", "Hide_Group_Warning": "Sei sicuro di voler nascondere il gruppo \"%s\"?", "Hide_Livechat_Warning": "Sei sicuro di voler nascondere il livechat con \"%s\"?", + "Hide_On_Workspace": "Nascondi nell'area di lavoro", "Hide_Private_Warning": "Sei sicuro di voler nascondere la discussione con \"%s\"?", "Hide_roles": "Nascondi ruoli", "Hide_room": "Nascondi il canale", @@ -1287,11 +1623,17 @@ "Hide_System_Messages": "Nascondi messaggi di sistema", "Hide_Unread_Room_Status": "Nascondi lo stato del canale non letto", "Hide_usernames": "Nascondi nomi utente", + "Hide_video": "Nascondi video", + "High": "Alta", + "Highest": "Altissima", "Highlights": "In evidenza", "Highlights_How_To": "Per essere notificati quando qualcuno menziona una parola o una frase, aggiungilo qua. È possibile separare parole o frasi con le virgole. Le parole evidenziate non sono 'case sensitive'.", "Highlights_List": "Evidenzia parole", "History": "Storico", + "Hold_Time": "Tempo in attesa", + "Hold": "In attesa", "Home": "Ciao", + "Homepage": "Homepage", "Host": "Host", "hours": "ore", "Hours": "Ore", @@ -1301,10 +1643,14 @@ "How_responsive_was_the_chat_agent": "Quanto è reattivo l'operatore chat?", "How_satisfied_were_you_with_this_chat": "Quanto sei soddisfatto di questa chat?", "How_to_handle_open_sessions_when_agent_goes_offline": "Come gestire le sessioni attive quando l'opreatore si disconnette", + "Http_timeout_value": "5000", + "Icon": "Icona", "Idle_Time_Limit": "Limite del tempo di inattività", "Idle_Time_Limit_Description": "Periodo di tempo fino a quando lo stato non cambia. Il valore deve essere in secondi.", "if_they_are_from": "(se sono da %s)", "If_this_email_is_registered": "Se questa email è registrata, invieremo istruzioni su come reimpostare la propria password. Se non la ricevi in breve tempo, si prega di ritornare e riprovare.", + "If_you_didnt_ask_for_reset_ignore_this_email": "Se non hai richiesto la reimpostazione della password, puoi ignorare questa e-mail.", + "If_you_didnt_try_to_login_in_your_account_please_ignore_this_email": "Se non hai provato ad accedere al tuo account, ignora questa e-mail.", "Iframe_Integration": "Integrazione Iframe", "Iframe_Integration_receive_enable": "Abilita ricezione", "Iframe_Integration_receive_enable_Description": "Consenti alla finestra padre di inviare comandi a Rocket.Chat.", @@ -1316,12 +1662,16 @@ "Iframe_Integration_send_target_origin_Description": "Solo pagine con una origine certa potranno ascoltare gli eventi o '*' per tutte le origini. Esempio `http://localhost`", "Ignore": "Ignorare", "Ignored": "ignorato", + "Ignore_Two_Factor_Authentication": "Ignora l'autenticazione a due fattori", + "Images": "Immagini", "IMAP_intercepter_already_running": "L'intercettatore IMAP è già in esecuzione", "IMAP_intercepter_Not_running": "Intercettatore IMAP Non in esecuzione", "Impersonate_next_agent_from_queue": "Impersonare il prossimo agente dalla coda", "Impersonate_user": "Impersona utente", "Impersonate_user_description": "Quando abilitato i post integrati come l'utente che ha scatenato l'integrazione", "Import": "Importazione", + "Import_New_File": "Importa un nuovo file", + "Import_Type": "Tipo di importazione", "Importer_Archived": "Archiviato", "Importer_CSV_Information": "L'importatore CSV richiede un formato specifico, leggi la documentazione per scoprire come organizzare il tuo file zip:", "Importer_done": "Importazione completata!", @@ -1345,22 +1695,37 @@ "Importer_setup_error": "Si è verificato un errore durante l'impostazione dell'importatore.", "Importer_Slack_Users_CSV_Information": "Il file caricato deve essere il file di esportazione degli utenti di Slack, che è un file CSV. Vedi qui per maggiori informazioni:", "Importer_Source_File": "Selezione dei file sorgente", + "importer_status_done": "Completato con successo", + "importer_status_file_loaded": "File caricato", "importer_status_finishing": "Quasi fatto", + "importer_status_import_cancelled": "Annullato", "importer_status_import_failed": "Errore", + "importer_status_new": "Non iniziato/a", + "In_progress": "In corso", + "inbound-voip-calls": "Chiamate Voip in entrata", + "Inbox_Info": "Info sulla posta in arrivo", "Inclusive": "inclusivo", + "Incoming": "In arrivo", + "Incoming_call_from": "Chiamata in arrivo da", "Incoming_Livechats": "LiveChat in arrivo", "Incoming_WebHook": "WebHook in Entrata", "Industry": "Industria", + "Info": "Info", "initials_avatar": "Iniziali Avatar", "Install_Extension": "Installa estensione", "Install_FxOs": "Installa Rocket.Chat sul tuo Firefox", "Install_FxOs_done": "Grande! È ora possibile utilizzare Rocket.Chat tramite l'icona sulla tua schermata principale. Buon divertimento con Rocket.Chat!", "Install_FxOs_error": "Siamo spiacenti che non ha funzionato come inteso! Si è verificato il seguente errore:", "Install_FxOs_follow_instructions": "Si prega di confermare l'installazione dell'applicazione sul proprio dispositivo (premere \"Installa\" quando richiesto).", + "Installing": "Installazione in corso", "Install_package": "Installa pacchetto", "Installation": "Installazione", + "Installed": "Installato", "Installed_at": "Installato il", + "Instance": "Istanza", + "Instances": "Istanze", "Instance_Record": "Record d'istanza", + "Instructions": "Istruzioni", "Instructions_to_your_visitor_fill_the_form_to_send_a_message": "Istruzioni per il visitatore, compila il modulo per inviare un messaggio", "Insurance": "Assicurazione", "Integration_added": "L'integrazione è stata aggiunta", @@ -1438,6 +1803,7 @@ "Invite_user_to_join_channel_all_from": "Invita tutti gli utenti da [#channell] per unirsi a questo canale", "Invite_user_to_join_channel_all_to": "Invita tutti gli utenti di questo canale a unirsi [#channel]", "Invite_Users": "Invita utenti", + "IP_Address": "Indirizzo IP", "IRC_Channel_Join": "Output del comando JOIN", "IRC_Channel_Leave": "Output del comando PART", "IRC_Description": "Internet Relay Chat (IRC) è uno strumento di comunicazione di gruppo basato su testo. Gli utenti entrano in canali univocamente denominati o in canali per discussioni aperte. IRC inoltre supporta messaggi privati tra utenti individuali e la possibilità di condividere file. Questo pacchetto integra questi livelli di funzionalità con Rocket.Chat.", @@ -1451,11 +1817,14 @@ "IssueLinks_LinkTemplate": "Modello per collegamenti di problemi", "IssueLinks_LinkTemplate_Description": "Modello per collegamenti di problemi; %s sarà sostituito dal numero di rilascio.", "It_works": "Funziona", + "Italic": "Corsivo", "italics": "corsivo", "Job_Title": "Titolo di lavoro", "Join": "Entra", "Join_audio_call": "Partecipa alla chiamata audio", + "Join_call": "Partecipa alla chiamata", "Join_Chat": "Iscriviti alla chat", + "Join_conference": "Partecipa alla conferenza", "Join_default_channels": "Partecipa ai canali predefiniti", "Join_the_Community": "Entra nella Comunità", "Join_the_given_channel": "Entra nel canale specificato", @@ -1463,6 +1832,8 @@ "join-without-join-code": "Iscriviti senza unire il codice", "join-without-join-code_description": "Autorizzazione a bypassare il codice di join nei canali con codice di abilitazione abilitato", "Joined": "Entrato", + "joined": "unito", + "Joined_at": "Iscritto a", "Jump": "Salta", "Jump_to_first_unread": "Vai al primo messaggio non letto", "Jump_to_message": "Vai al messaggio", @@ -1491,7 +1862,27 @@ "Knowledge_Base": "Knowledge Base", "Label": "Etichetta", "Language": "Lingua", + "Language_Bulgarian": "Bulgaro", + "Language_Chinese": "Cinese", + "Language_Czech": "Ceco", + "Language_Danish": "Danese", + "Language_Dutch": "Olandese", + "Language_English": "Inglese", + "Language_Estonian": "Estone", + "Language_Finnish": "Finlandese", + "Language_French": "Francese", + "Language_German": "Tedesco", + "Language_Greek": "Greco", + "Language_Hungarian": "Ungherese", + "Language_Italian": "Italiano", + "Language_Japanese": "Giapponese", + "Language_Latvian": "Lettone", + "Language_Lithuanian": "Lituano", "Language_Not_set": "No specifico", + "Language_Polish": "Polacco", + "Language_Portuguese": "Portoghese", + "Language_Romanian": "Rumeno", + "Language_Russian": "Russo", "Language_Version": "Versione Inglese", "Last_login": "Ultimo accesso", "Last_Message": "Ultimo messaggio", @@ -1499,16 +1890,35 @@ "Last_seen": "Ultima visualizzazione", "Launched_successfully": "Lanciato con successo", "Layout": "Aspetto", + "Layout_Login_Template_Vertical": "Verticale", + "Layout_Login_Template_Horizontal": "Orizzontale", "Layout_Home_Body": "Contenuto pagina iniziale", + "Layout_Home_Page_Content": "Layout / Contenuto della home page", + "Layout_Home_Page_Content_Title": "Contenuto della home page", "Layout_Home_Title": "Titolo pagina iniziale", "Layout_Login_Terms": "Condizioni d'accesso", "Layout_Privacy_Policy": "Privacy Policy", + "Layout_Show_Home_Button": "Mostra il pulsante home page nell'intestazione della barra laterale", + "Layout_Home_Custom_Block_Visible": "Mostra contenuto predefinito nella homepage", + "Layout_Custom_Body_Only": "Mostra solo contenuto personalizzato", "Layout_Sidenav_Footer": "Piè di Pagina Navigazione Laterale", "Layout_Sidenav_Footer_description": "La dimensione del piè di pagina è di 260 x 70px", "Layout_Sidenav_Footer_Dark_description": "La dimensione del piè di pagina è di 260 x 70px", "Layout_Terms_of_Service": "Termini di servizio", "LDAP": "LDAP", "LDAP_Description": "LDAP è un database gerarchico che molte aziende utilizzano per fornire single sign on - per la condivisione di una sola password tra più siti e servizi. Per informazioni sulla configurazione avanzata ed esempi, consulta il nostro wiki: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "Crittografia", + "LDAP_Connection_successful": "Connessione LDAP riuscita", + "LDAP_Connection_Timeouts": "Timeout", + "LDAP_UserSearch": "Ricerca utente", + "LDAP_UserSearch_Filter": "Filtro di ricerca", + "LDAP_UserSearch_GroupFilter": "Filtro di gruppo", + "LDAP_DataSync": "Sincronizzazione dei dati", + "LDAP_DataSync_DataMap": "Mappatura", + "LDAP_DataSync_Avatar": "Avatar", + "LDAP_DataSync_Advanced": "Sincronizzazione avanzata", + "LDAP_Enterprise": "Premium", + "LDAP_Server_Type_Other": "Altro", "LDAP_Authentication": "Permettere", "LDAP_Authentication_Password": "Password", "LDAP_Authentication_UserDN": "DN utente", @@ -1518,7 +1928,6 @@ "LDAP_Background_Sync_Import_New_Users": "Sincronizzazione in background Importa nuovi utenti", "LDAP_Background_Sync_Import_New_Users_Description": "Importerà tutti gli utenti (in base ai criteri del filtro) esistenti in LDAP e non esiste in Rocket.Chat", "LDAP_Background_Sync_Interval": "Intervallo sincronizzazione sfondo", - "meteor_status_reconnect_in_many": "riprovo tra {{count}} secondi...", "LDAP_Background_Sync_Interval_Description": "L'intervallo tra le sincronizzazioni. Esempio \"ogni 24 ore\" o \"il primo giorno della settimana\", altri esempi su [Cron Text Parser] (http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Aggiornamento in background Aggiorna gli utenti esistenti", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Sincronizza l'avatar, i campi, il nome utente, ecc. (In base alla configurazione) di tutti gli utenti già importati da LDAP su ogni ** Intervallo di sincronizzazione **", @@ -1568,7 +1977,9 @@ "LDAP_Search_Size_Limit_Description": "Il numero massimo di voci da restituire. \n **Attenzione** Questo numero deve essere maggiore di **Dimensione pagina ricerca**", "LDAP_Sync_Now": "Sincronizzazione in background adesso", "LDAP_Sync_Now_Description": "Eseguirà **Sincronizzazione di sfondo** ora anziché attendere **Intervallo di sincronizzazione** anche se **Sincronizzazione in background** è False. \n Questa azione è asincrona, consulta i registri per ulteriori informazioni sul processi", + "LDAP_Sync_User_Active_State_Nothing": "Non fare nulla", "LDAP_Sync_User_Avatar": "Sincronizzazione Avatar Utenti", + "LDAP_Sync_User_Data_Channels_Admin": "Amministratore Canale", "LDAP_Timeout": "Timeout (ms)", "LDAP_Timeout_Description": "Quanti chilometri quanti aspettano un risultato di ricerca prima di restituire un errore", "LDAP_Unique_Identifier_Field": "Campo Identificativo Univoco", @@ -1582,7 +1993,11 @@ "LDAP_Username_Field_Description": "Quale campo verrà utilizzato come *username* per i nuovi utenti. Lascia vuoto per usare il nome utente informato sulla pagina di login. \n È possibile utilizzare i tag modello di troppo, come `#{givenName}.#{sn}`. \n  Il valore predefinito è `sAMAccountName`.", "Lead_capture_email_regex": "Piombo regex di posta elettronica di acquisizione", "Lead_capture_phone_regex": "Piombo regex del telefono di acquisizione", + "Learn_more": "Per saperne di più", + "Least_recent_updated": "Aggiornamento più recente", + "Learn_how_to_unlock_the_myriad_possibilities_of_rocket_chat": "Scopri come sbloccare la miriade di possibilità offerte da Rocket.Chat.", "Leave": "Lascia", + "Leave_a_comment": "Lascia un commento", "Leave_Group_Warning": "Sei sicuro di voler lasciare il gruppo \"%s\"?", "Leave_Livechat_Warning": "Sei sicuro di voler lasciare il live con \"%s\"?", "Leave_Private_Warning": "Sei sicuro di volere lasciare la discussione con \"%s\"?", @@ -1593,8 +2008,11 @@ "leave-p": "Lascia i gruppi privati", "List_of_Channels": "Elenco di Canali", "List_of_Direct_Messages": "Elenco dei messaggi privati", + "Livechat": "Livechat", "Livechat_agents": "Operatori livechat", + "Livechat_Agents": "Agenti", "Livechat_AllowedDomainsList": "Domini permessi per Livechat", + "Livechat_close_chat": "Chiudi la chat", "Livechat_Dashboard": "Dashboard livechat", "Livechat_enabled": "Livechat abilitato", "Livechat_forward_open_chats": "Inoltra le chat aperte", @@ -1620,6 +2038,8 @@ "Livestream_switch_to_room": "Passa alla livestream della stanza corrente", "Livestream_url": "URL sorgente Livestream", "Livestream_url_incorrect": "L'URL di Livestream non è corretto", + "Livestream_live_now": "In diretta!", + "Load_Balancing": "Bilanciamento del carico", "Load_more": "Carica altri", "Loading_more_from_history": "Caricamento dallo storico", "Loading_suggestion": "Caricamento opzioni...", @@ -1739,6 +2159,7 @@ "Message_HideType_ru": "Nascondi messaggi \"Utente rimosso\"", "Message_HideType_uj": "Nascondi messaggi \"Utente entrato\"", "Message_HideType_ul": "Nascondi messaggi \"Utente uscito\"", + "Message_HideType_wm": "Nascondi i messaggi di benvenuto", "Message_Ignored": "Questo messaggio è stato ignorato", "Message_info": "Informazioni sul messaggio", "Message_KeepHistory": "Mantieni la Cronologia messaggi", @@ -1749,7 +2170,8 @@ "Message_Read_Receipt_Enabled": "Mostra conferme di lettura", "Message_Read_Receipt_Store_Users": "Ricevute di lettura dettagliate", "Message_Read_Receipt_Store_Users_Description": "Mostra le conferme di lettura di ciascun utente", - "Message_removed": "Messaggio rimosso", + "Message_removed": "messaggio rimosso", + "Message_is_removed": "messaggio rimosso", "Message_sent_by_email": "Messaggio inviato tramite email", "Message_ShowDeletedStatus": "Mostra lo stato eliminato", "Message_starring": "Segnalibro Messaggio", @@ -1868,6 +2290,7 @@ "Normal": "Normale", "Not_Available": "Non disponibile", "Not_found_or_not_allowed": "Non trovato o Non Permesso", + "Not_Visible_To_Workspace": "Non visibile nell'area di lavoro", "Nothing": "Niente", "Nothing_found": "Non abbiamo trovato nulla", "Notification_Desktop_Default_For": "Mostra notifiche desktop per", @@ -2008,10 +2431,15 @@ "Preferences_saved": "Preferenze salvate", "preview-c-room": "Anteprima canale pubblico", "preview-c-room_description": "Autorizzazione a visualizzare i contenuti di un canale pubblico prima di aderire", + "Priority_saved": "Priorità salvata", + "Priorities_restored": "Priorità ripristinate", "Privacy": "Privacy", "Privacy_Policy": "Privacy Policy", "Private": "Privato", + "Private_channels": "Canali privati", + "Private_Apps": "Applicazioni private", "Private_Channel": "Canale privato", + "Private_Channels": "Canali privati", "Private_Group": "Gruppo privato", "Private_Groups": "Gruppi Privati", "Private_Groups_list": "Elenco dei canali privati", @@ -2032,6 +2460,7 @@ "Pruning_messages": "Messaggi di eliminazione ...", "Public": "Pubblico", "Public_Channel": "Canale pubblico", + "Public_Channels": "Canali pubblici", "Public_Community": "Comunità pubblica", "Push": "Notifiche Push", "Push_Notifications": "Notifiche Push", @@ -2053,6 +2482,7 @@ "Query": "Ricerca", "Query_description": "Condizioni supplementari per determinare a quali utenti inviare l'email. Gli utenti non iscritti/rimossi vengono automaticamente esclusi. Deve essere un JSON valido. Esempio: \"{\" createdAt \": {\" $ gt \": {\" $ data \":\" 2015-01-01T00: 00: 00.000Z \"}}}\"", "Queue": "Coda", + "Queued": "In coda", "quote": "citazione", "Quote": "Citazione", "Random": "Casuale", @@ -2115,12 +2545,19 @@ "Reply": "Rispondi", "Reply_in_direct_message": "Rispondi via messaggio privato", "Reply_in_thread": "Rispondi in un thread", + "Reply_via_Email": "Rispondi via e-mail", "ReplyTo": "Rispondi a", "Report": "Segnala", "Report_Abuse": "Segnala un abuso", "Report_exclamation_mark": "Rapporto!", "Report_this_message_question_mark": "Segnalare questo messaggio?", + "Report_User": "Segnala l'utente", "Reporting": "Resoconti", + "request": "richiesta", + "requests": "richieste", + "Requests": "Richieste", + "Requested_apps_will_appear_here": "Le applicazioni richieste appariranno qui", + "request-pdf-transcript": "Richiedi la trascrizione in PDF", "Require_all_tokens": "Richiedi tutti i token", "Require_any_token": "Richiedi un token", "Require_password_change": "Richiedi cambio password", @@ -2132,6 +2569,7 @@ "Reset_section_settings": "Resetta le impostazioni della sezione", "Restart": "Riavvia", "Restart_the_server": "Riavvia il server", + "Results": "Risultati", "Retail": "Al dettaglio", "Retention_setting_changed_successfully": "Impostazione dei criteri di conservazione modificata correttamente", "RetentionPolicy": "Politica di conservazione", @@ -2247,6 +2685,7 @@ "seconds": "secondi", "Secret_token": "Token segreto", "Security": "Sicurezza", + "See_all_themes": "Vedi tutti i temi", "Select_a_department": "Seleziona un dipartimento", "Select_a_user": "Seleziona un utente", "Select_an_avatar": "Seleziona l'avatar", @@ -2281,9 +2720,14 @@ "Send_welcome_email": "Invia email di benvenuto", "Send_your_JSON_payloads_to_this_URL": "Invia i tuoi payload JSON a questo URL.", "Sending": "Invio...", + "Sending_your_mail_to_s": "Invio della posta a %s", "Sent_an_attachment": "Inviato un allegato", "Served_By": "Servito da", + "Server_already_added": "Server già aggiunto", + "Server_doesnt_exist": "Il server non esiste", + "Servers": "Server", "Server_Info": "Informazioni sul server", + "Server_name": "Nome del server", "Server_Type": "Tipo di server", "Service": "Servizio", "Service_account_key": "Chiave dell'account di servizio", @@ -2321,8 +2765,11 @@ "Show_room_counter_on_sidebar": "Mostra contatore stanza sulla barra laterale", "Show_Setup_Wizard": "Mostra procedura guidata di installazione", "Show_the_keyboard_shortcut_list": "Mostra l'elenco delle scorciatoie per la tastiera", + "Show_To_Workspace": "Mostra all'area di lavoro", "Showing_archived_results": "

          Mostra %s risultati archiviati

          ", "Showing_results": "

          Visualizzati %s risultati

          ", + "Show_usernames": "Mostra i nomi utente", + "Show_roles": "Mostra ruoli", "Sidebar": "Sidebar", "Sidebar_list_mode": "Modalità Elenco canali laterale", "Sign_in_to_start_talking": "Accedi per iniziare a parlare", @@ -2331,7 +2778,12 @@ "Site_Url": "URL del sito", "Site_Url_Description": "Esempio: `https://chat.domain.com/`", "Size": "Dimensione", + "Skin_tone": "Tonalità della pelle", "Skip": "Salta", + "SLA_Policy": "Politica SLA", + "SLA_Policies": "Politiche SLA", + "SLA_removed": "SLA rimosso", + "Slack": "Slack", "Slack_Users": "Utenti di Slack CSV", "SlackBridge_error": "SlackBridge ha rilevato un errore durante l'importazione dei messaggi alle %s: %s", "SlackBridge_finish": "SlackBridge ha finito di importare i messaggi alle %s. Ricarica per vedere tutti i messaggi.", @@ -2371,16 +2823,20 @@ "Social_Network": "Rete sociale", "Sorry_page_you_requested_does_not_exist_or_was_deleted": "Siamo spiacenti, la pagina richiesta non esiste o è stata cancellata!", "Sort": "Ordinare", + "Service_level_agreements": "Accordi sul livello di servizio", "Sort_by_activity": "Ordina per attività", "Sound": "Suoni", + "Sounds": "Suoni", "Sound_File_mp3": "File sonoro (mp3)", "SSL": "SSL", "Star": "Aggiungi ai preferiti", "Star_Message": "Evidenzia messaggio", "Starred_Messages": "Messaggi evidenziati", "Start": "Inizio", + "Start_a_free_trial": "Inizia una prova gratuita", "Start_audio_call": "Avvia chiamata audio", "Start_Chat": "Avvia chat", + "Start_free_trial": "Inizia la prova gratuita", "Start_of_conversation": "Inizio della conversazione", "Start_OTR": "Inizio OTR", "Start_video_call": "Avvia chiamata video", @@ -2394,6 +2850,7 @@ "Statistics_reporting": "Invia statistiche a Rocket.Chat", "Statistics_reporting_Description": "Inviando le tue statistiche, ci aiuterai ad identificare quante istanze di Rocket.Chat esistono, come funziona il sistema e come migliorarlo. Non ti preoccupare non saranno inviate informazioni degli utenti e tutte quelle che riceveremo saranno confidenziali.", "Stats_Active_Users": "Utenti Attivi", + "Stats_App_Users": "Utenti dell'app Rocket.Chat", "Stats_Avg_Channel_Users": "Media Canali Utenti", "Stats_Avg_Private_Group_Users": "Media Gruppi Privati Utenti", "Stats_Away_Users": "Utenti Assenti", @@ -2409,8 +2866,11 @@ "Stats_Total_Messages_Direct": "Messaggi totali nei messaggi diretti", "Stats_Total_Messages_Livechat": "Messaggi totali nelle livechat", "Stats_Total_Messages_PrivateGroup": "Messaggi totali nei gruppi privati", + "Stats_Total_Messages_Discussions": "Messaggi nelle discussioni", "Stats_Total_Private_Groups": "Totale Gruppi Privati", - "Stats_Total_Rooms": "Stanze Totali", + "Stats_Total_Rooms": "Stanze", + "Stats_Total_Uploads": "Upload totali", + "Stats_Total_Uploads_Size": "Dimensioni totali upload", "Stats_Total_Users": "Utenti Totali", "Status": "Stato", "StatusMessage_Placeholder": "Cosa stai facendo in questo momento?", @@ -2439,16 +2899,21 @@ "TargetRoom": "Stanza Target", "TargetRoom_Description": "La stanza dove i messaggi saranno inviati quando i risultati di questi eventi saranno scatenati. Solo la stanza target è permessa e deve esistere.", "Team": "Squadra", + "Teams": "Squadre", "Teams_New_Name_Label": "Nome", "Teams_New_Broadcast_Description": "Solo gli utenti autorizzati possono scrivere nuovi messaggi, ma gli altri utenti saranno in grado di rispondere", "Teams_New_Description_Label": "Argomento", "Teams_New_Encrypted_Label": "Crittografato", "Teams_New_Private_Label": "Privato", "Teams_Private_Team": "Squadra privata", + "Teams_Search_teams": "Cerca Squadre", "Teams_New_Read_only_Label": "Sola lettura", "Technology_Services": "Servizi tecnologici", "Test_Connection": "Verifica Connessione", "Test_Desktop_Notifications": "Verifica Notifiche Desktop", + "test-admin-options_description": "Autorizzazione a testare le opzioni del pannello di amministrazione, come il login LDAP.", + "test-push-notifications": "Test delle notifiche push", + "test-push-notifications_description": "Autorizzazione a testare le notifiche push", "Thank_you_for_your_feedback": "Grazie per il tuo feedback", "The_application_name_is_required": "Il nome dell'applicazione è richiesta", "The_channel_name_is_required": "Il nome del canale è richiesto", @@ -2509,6 +2974,7 @@ "This_conversation_is_already_closed": "Questa conversazione sarà chiusa.", "This_email_has_already_been_used_and_has_not_been_verified__Please_change_your_password": "Questa email è stata già utilizzata e non é stata verificata. Cambia la tua password.", "This_is_a_desktop_notification": "Questa è una notifica desktop", + "Zapier_integration_has_been_deprecated": "L'integrazione Zapier è stata deprecata, potrebbe non funzionare come previsto e non riceverà aggiornamenti", "This_is_a_push_test_messsage": "Questa è una prova di notifica push", "This_room_has_been_archived_by__username_": "Questo canale è stato archiviato da {{username}}", "This_room_has_been_unarchived_by__username_": "Questo canale è stato disarchiviato da {{username}}", @@ -2537,8 +3003,10 @@ "Tokens_Required_Input_Error": "Token digitati non validi.", "Tokens_Required_Input_Placeholder": "Nomi di asset di token", "Topic": "Argomento", + "Top_5_agents_with_the_most_conversations": "I 5 agenti con il maggior numero di conversazioni", "Total_Discussions": "Discussioni totali", "Total_messages": "Messaggi totali", + "Total_rooms": "Totale stanze", "Total_Threads": "Thread totali", "TOTP Invalid [totp-invalid]": "Codice o password non validi", "totp-invalid": "Codice o password non validi", @@ -2559,11 +3027,13 @@ "Turn_OFF": "Spegni", "Turn_ON": "Accendere", "Two-factor_authentication": "Autenticazione a due fattori", + "Two-factor_authentication_via_TOTP": "Autenticazione a due fattori", "Two-factor_authentication_disabled": "Autenticazione a due fattori disabilitata", "Two-factor_authentication_enabled": "Autenticazione a due fattori abilitata", "Two-factor_authentication_is_currently_disabled": "L'autenticazione a due fattori è attualmente disabilitata", "Two-factor_authentication_native_mobile_app_warning": "ATTENZIONE: una volta abilitato, non potrai accedere alle app native native (Rocket.Chat +) usando la tua password fino a quando non implementeranno la 2FA.", "Type": "Tipo", + "Types": "Tipi", "Type_your_email": "Inserire la propria email", "Type_your_job_title": "Digita il titolo del tuo lavoro", "Type_your_message": "Inserisci la tua messaggio", @@ -2587,6 +3057,8 @@ "Unfavorite": "Rimuovi preferito", "Unignore": "Non ignorare", "Uninstall": "Disinstallare", + "Units": "Unità", + "Unknown_User": "Utente sconosciuto", "Unmute_someone_in_room": "Smuta a qualcuno nel canale", "Unmute_user": "Togli il muto all'utente", "Unnamed": "Senza nome", @@ -2603,6 +3075,7 @@ "Unstar_Message": "Non evidenziare messaggio", "Update_your_RocketChat": "Aggiorna il tuo Rocket.Chat", "Updated_at": "Aggiornato a", + "Uploads": "Upload", "Upload_file_description": "Descrizione file", "Upload_file_name": "Nome file", "Upload_file_question": "Caricare il file?", @@ -2623,6 +3096,7 @@ "Use_url_for_avatar": "Utilizzare una URL per l'avatar", "Use_User_Preferences_or_Global_Settings": "Usa le Preferenze Utente o Globali", "User": "Utente", + "User_menu": "Menu utente", "User__username__is_now_a_leader_of__room_name_": "L'utente {{username}} è ora un leader di {{room_name}}", "User__username__is_now_a_moderator_of__room_name_": "L'Utente {{username}} è ora un moderatore di {{room_name}}", "User__username__is_now_an_owner_of__room_name_": "L'Utente {{username}} è ora proprietario di {{room_name}}", @@ -2649,6 +3123,7 @@ "User_is_now_an_admin": "L'utente è ora un amministratore", "User_is_unblocked": "Utente è sbloccato", "User_joined_channel": "È entrato nel canale.", + "User_joined_the_channel": "è entrato nel canale", "User_left": "Ha lasciato il canale.", "User_logged_out": "L'utente è disconnesso ", "User_management": "Gestione utenti", @@ -2665,6 +3140,7 @@ "User_sent_a_message_to_you": "{{username}} ti ha inviato un messaggio", "user_sent_an_attachment": "{{user}} ha inviato un allegato", "User_Settings": "Impostazioni utente", + "User_started_a_new_conversation": "{{username}} ha iniziato una nuova conversazione", "User_unmuted_by": "Utente {{user_unmuted}} non mutato da {{user_by}}.", "User_unmuted_in_room": "Utente smutato nel canale", "User_updated_successfully": "Utente aggiornato con successo", @@ -2698,7 +3174,9 @@ "Username_is_already_in_here": "`@%s` è già qui.", "Username_Placeholder": "Si prega di inserire nomi utente ...", "Username_title": "Registra nome utente", + "Username_has_been_updated": "Il nome utente è stato aggiornato", "Username_wants_to_start_otr_Do_you_want_to_accept": "{{username}} vuole iniziare OTR. Vuoi accettare?", + "Username_name_email": "Nome utente, nome o e-mail", "Users": "Utenti", "Users_added": "L'utente è stato aggiunto", "Users_in_role": "Utenti nel ruolo", @@ -2715,10 +3193,14 @@ "Verify": "✓ Verifica", "Verify_your_email": "Verifica il tuo indirizzo email", "Version": "Versione", + "Version_version": "Versione {{version}}", "Video_Chat_Window": "Chat Video", "Video_Conference": "Video conferenza", "Video_message": "Messaggio video", "Videocall_declined": "Chiamata video rifiutata", + "video_direct_ended_by": "**{{username}}** _ha terminato una chiamata_", + "VideoConf_Mobile_Ringing": "Abilita la suoneria mobile", + "Video_record": "Registrazione video", "View_mode": "Aspetto", "View_All": "Vedi tutto", "View_Logs": "Visualizza log", @@ -2751,6 +3233,8 @@ "view-p-room_description": "Autorizzazione a vedere canali privati", "view-privileged-setting": "Visualizza impostazioni privilegiate", "view-privileged-setting_description": "Autorizzazione a visualizzare le impostazioni", + "view-moderation-console": "Visualizza la console di moderazione", + "view-moderation-console_description": "Autorizzazione a visualizzare la console di moderazione del server", "view-room-administration": "Visualizza l'amministrazione della camera", "view-room-administration_description": "Autorizzazione a visualizzare statistiche pubbliche, private e dirette dei messaggi. Non include la possibilità di visualizzare conversazioni o archivi", "view-statistics": "Visualizza statistiche", @@ -2760,8 +3244,10 @@ "Viewing_room_administration": "In fase di visualizzazione del canale di amministrazione", "Visibility": "Visibilità", "Visible": "Visibile", + "Visible_To_Workspace": "Visibile nell'area di lavoro", "Visitor": "Visitatore", "Visitor_Info": "Info visitatori", + "Visitor_not_found": "Visitatore non trovato", "Visitor_Navigation": "Navigazione Visitatori", "Visitor_page_URL": "URL Pagina Visitatori", "Visitor_time_on_site": "Tempo dei visitatori sul sito", @@ -2770,10 +3256,13 @@ "We_are_offline_Sorry_for_the_inconvenience": "Non siamo in linea. Ci dispiace per l'inconveniente.", "We_have_sent_password_email": "Ti abbiamo inviato un'email con le istruzioni per reimpostare la password. Se non la ricevi in breve tempo, si prega di ritornare e riprovare.", "We_have_sent_registration_email": "Ti abbiamo inviato una email per confermare la registrazione. Se non la ricevi entro pochi minuti, torna e prova di nuovo.", + "WebDAV_Integration_Not_Allowed": "Integrazione WebDAV non consentita", "Webdav_Password": "Password WebDAV", "Webdav_Server_URL": "URL di accesso al server WebDAV", "Webdav_Username": "Nome utente WebDAV", + "webdav-server-not-found": "Server WebDAV non trovato", "Webhook_URL": "URL Webhook", + "Webhook_URL_not_set": "L'URL del webhook non è impostato", "Webhooks": "Webhooks", "WebRTC_direct_audio_call_from_%s": "Chiamata audio diretta da%s", "WebRTC_direct_video_call_from_%s": "Videochiamata diretta da%s", @@ -2788,9 +3277,12 @@ "Website": "Sito web", "Wednesday": "Mercoledì", "Welcome": "Benvenuto %s.", + "Welcome_to": "Benvenuti a [Nome_del_sito]", + "Welcome_to_workspace": "Benvenuti a {{Site_Name}}", "Welcome_to_the": "Benvenuto a", "Why_do_you_want_to_report_question_mark": "Perché vuoi segnalare?", "will_be_able_to": "potranno", + "Without_SLA": "Senza SLA", "Worldwide": "In tutto il mondo", "Would_you_like_to_return_the_inquiry": "Vuoi tornare alla richiesta", "Yes": "Sì", @@ -2806,6 +3298,7 @@ "yesterday": "ieri", "Yesterday": "Ieri", "You": "Tu", + "You_reacted_with": "Hai reagito con {{emoji}}", "you_are_in_preview_mode_of": "Sei in modalità di anteprima del canale # {{room_name}}", "You_are_logged_in_as": "Sei loggato come", "You_are_not_authorized_to_view_this_page": "Non sei autorizzato a vedere questa pagina.", @@ -2845,8 +3338,11 @@ "registration.page.registration.waitActivationWarning": "Prima di poter effettuare il login, il tuo account deve essere attivato manualmente da un amministratore.", "registration.page.login.forgot": "Password dimenticata", "registration.page.resetPassword.sent": "Se questa email è registrata, invieremo istruzioni su come reimpostare la propria password. Se non la ricevi in breve tempo, si prega di ritornare e riprovare.", + "registration.page.guest.continueAsGuest": "Continua come ospite", + "registration.component.welcome": "Benvenuti nello spazio di lavoro di <1>Rocket.Chat ", "registration.component.login": "Login", "registration.component.login.userNotFound": "Utente non trovato", + "registration.component.login.incorrectPassword": "Password errata", "registration.component.resetPassword": "Reimposta password", "registration.component.form.emailOrUsername": "Email o nome utente", "registration.component.form.username": "Nome utente", @@ -2855,6 +3351,7 @@ "registration.component.form.usernameAlreadyExists": "Il nome utente esiste già. Si prega di provare un altro nome utente.", "registration.component.form.invalidEmail": "L'email inserita non è valida", "registration.component.form.email": "Email", + "registration.component.form.emailPlaceholder": "example@example.com", "registration.component.form.password": "Password", "registration.component.form.divider": "o", "registration.component.form.submit": "Invia", @@ -2862,6 +3359,42 @@ "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", + "subscription.callout.allPremiumCapabilitiesDisabled": "Tutte le funzionalità premium disattivate", + "subscription.callout.privateApps": "applicazioni private installate", + "subscription.callout.marketplaceApps": "applicazioni del marketplace installate", + "subscription.callout.monthlyActiveContacts": "contatti attivi mensili", + "Private_apps_limit_reached": "Limite di app private raggiunto", + "Private_apps_limit_exceeded": "Limite di app private superato", + "Time": "Tempo", + "This_attachment_is_not_supported": "Formato dell'allegato non supportato", + "Send_transcript": "Invia la trascrizione", + "Service_status": "Stato del servizio", + "User_Status": "Stato dell'utente", + "Active_connections": "Connessioni attive", + "Service_disabled": "Il servizio è ora disattivato", + "Security_code": "Codice di sicurezza", + "Workspace_registered": "Spazio di lavoro registrato", + "Workspace_not_connected": "Spazio di lavoro non collegato", + "Token_Not_Recognized": "Token non riconosciuto", + "RegisterWorkspace_Registered_Description": "Questi servizi sono disponibili", + "RegisterWorkspace_NotRegistered_Title": "Spazio di lavoro non registrato", + "RegisterWorkspace_NotConnected_Title": "Spazio di lavoro disconnesso", + "RegisterWorkspace_Disconnect_Error": "Si è verificato un errore di disconnessione", + "RegisterWorkspace_Features_ThirdPartyLogin_Title": "Login di terze parti", + "RegisterWorkspace_Features_ThirdPartyLogin_Disconnect": "Le opzioni di login di terze parti non saranno più disponibili.", + "RegisterWorkspace_Syncing_Complete": "Sincronizzazione completata", + "RegisterWorkspace_Connection_Error": "Si è verificato un errore di connessione", "Enterprise": "impresa", - "UpgradeToGetMore_engagement-dashboard_Title": "Analytics" + "Operating_withing_plan_limits": "Attualmente entro i limiti del piano attivo", + "Workspace_not_registered": "Spazio di lavoro non registrato", + "Users_Connected": "Utenti connessi", + "Version_supported_until": "Versione <1>supportata fino a {{date}}", + "Subscription": "Abbonamento", + "ActiveSessionsPeak": "Picco di sessioni attive", + "ActiveSessions_available": "sessioni disponibili", + "Private_apps": "Applicazioni private", + "UpgradeToGetMore_engagement-dashboard_Title": "Analytics", + "UpgradeToGetMore_custom-roles_Title": "Ruoli personalizzati", + "Video_call_manager": "Gestore di videochiamate", + "Sync_license_update_Callout_Title": "Stiamo aggiornando la vostra licenza" } \ 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 bdf03e52249f..4aca5b5dec57 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json @@ -4255,6 +4255,7 @@ "Turn_off_video": "ビデオをオフ", "Two Factor Authentication": "2要素認証", "Two-factor_authentication": "TOTPによる2要素認証", + "Two-factor_authentication_via_TOTP": "TOTPによる2要素認証", "Two-factor_authentication_disabled": "2要素認証が無効です", "Two-factor_authentication_email": "メールによる2要素認証", "Two-factor_authentication_email_is_currently_disabled": "メールによる2要素認証は現在無効になっています", 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 0da6ec549547..5b775db06eb6 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json @@ -3308,6 +3308,7 @@ "Turn_ON": "ჩართვა", "Two Factor Authentication": "ორ ფაქტორიანი ავტენტიფიკაცია", "Two-factor_authentication": "ორ ფაქტორიანი ავტენტიფიკაცია TOTP-ით", + "Two-factor_authentication_via_TOTP": "ორ ფაქტორიანი ავტენტიფიკაცია TOTP-ით", "Two-factor_authentication_disabled": "ორ ფაქტორიანი ავტენტიფიკაცია გამორთულია", "Two-factor_authentication_email": "ორ ფაქტორიანი ავტენტიფიკაცია ელ.ფოსტით", "Two-factor_authentication_email_is_currently_disabled": "ორ ფაქტორიანი ავტენტიფიკაცია ელ.ფოსტით ამჟამად გამორთულია", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json index 8c14713fefad..fcaf5e71b14a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json @@ -2803,6 +2803,7 @@ "Turn_OFF": "បិទ", "Turn_ON": "បើក", "Two-factor_authentication": "ការផ្ទៀងផ្ទាត់ពីរកត្តា", + "Two-factor_authentication_via_TOTP": "ការផ្ទៀងផ្ទាត់ពីរកត្តា", "Two-factor_authentication_disabled": "ការផ្ទៀងផ្ទាត់ពីរកត្តាត្រូវបានបិទ", "Two-factor_authentication_enabled": "ការផ្ទៀងផ្ទាត់ពីរកត្តាត្រូវបានបើក", "Two-factor_authentication_is_currently_disabled": "ការសម្គាល់អត្តសញ្ញាណកត្តាពីរត្រូវបានបិទនាពេលបច្ចុប្បន្ន", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json index 9a49d6c359df..fe7eb9b0177d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json @@ -3624,6 +3624,7 @@ "Turn_ON": "켜기", "Two Factor Authentication": "2단계 인증(2FA)", "Two-factor_authentication": "2단계 인증(2FA)", + "Two-factor_authentication_via_TOTP": "2단계 인증(2FA)", "Two-factor_authentication_disabled": "2단계 인증(2FA) 사용 안 함", "Two-factor_authentication_email": "이메일을 통한 2단계 인증(2FA)", "Two-factor_authentication_email_is_currently_disabled": "이메일을 통한 2단계 인증(2FA)은 현재 사용할 수 없습니다.", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json index 4c012c61e0d7..b82aff287a0f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json @@ -2456,6 +2456,7 @@ "Turn_OFF": "Temirandin", "Turn_ON": "Vekirin", "Two-factor_authentication": "Çewtiya du-faktorê", + "Two-factor_authentication_via_TOTP": "Çewtiya du-faktorê", "Two-factor_authentication_disabled": "Çewtiya du-faktîf hate qedexekirin", "Two-factor_authentication_enabled": "Guherîna du-faktorê çalak kirin", "Two-factor_authentication_is_currently_disabled": "Çewtiya duyem-faktîk niha hate qedexekirin", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json index 7b9409f9e83b..4fc5eeb0c766 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json @@ -2500,6 +2500,7 @@ "Turn_OFF": "ປິດ", "Turn_ON": "ເປີດ", "Two-factor_authentication": "ການກວດສອບສອງປັດໄຈ", + "Two-factor_authentication_via_TOTP": "ການກວດສອບສອງປັດໄຈ", "Two-factor_authentication_disabled": "ການກວດສອບສອງປັດໃຈຖືກປະຕິເສດ", "Two-factor_authentication_enabled": "ການກວດສອບສອງປັດໄຈທີ່ຖືກເປີດໃຊ້", "Two-factor_authentication_is_currently_disabled": "ການກວດສອບສອງປັດໄຈແມ່ນຖືກປິດໃຊ້ໃນປະຈຸບັນ", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json index 19457e9cce7c..45eb0f343c17 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json @@ -2519,6 +2519,7 @@ "Turn_OFF": "Išjunk", "Turn_ON": "Įjungti", "Two-factor_authentication": "Dviejų veiksnių autentifikavimas", + "Two-factor_authentication_via_TOTP": "Dviejų veiksnių autentifikavimas", "Two-factor_authentication_disabled": "Dviejų veiksnių autentifikavimas išjungtas", "Two-factor_authentication_enabled": "Dviejų veiksnių autentifikavimas įjungtas", "Two-factor_authentication_is_currently_disabled": "Dviejų veiksnių autentifikavimas šiuo metu yra išjungtas", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json index 2d5d684a027f..50fd40831e5f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json @@ -2471,6 +2471,7 @@ "Turn_OFF": "Izslēgt", "Turn_ON": "Ieslēgt", "Two-factor_authentication": "Divu faktoru autentifikācija", + "Two-factor_authentication_via_TOTP": "Divu faktoru autentifikācija", "Two-factor_authentication_disabled": "Divu faktoru autentifikācija ir atspējota", "Two-factor_authentication_enabled": "Divu faktoru autentifikācija ir iespējota", "Two-factor_authentication_is_currently_disabled": "Divu faktoru autentifikācija pašlaik ir atspējota", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json index b58eace8ac25..8520175e2189 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json @@ -2457,6 +2457,7 @@ "Turn_OFF": "Хаах", "Turn_ON": "Асаах", "Two-factor_authentication": "Хоёр хүчин зүйлийн баталгаажилт", + "Two-factor_authentication_via_TOTP": "Хоёр хүчин зүйлийн баталгаажилт", "Two-factor_authentication_disabled": "Хоёр хүчин зүйлийн баталгаажуулалт идэвхгүй", "Two-factor_authentication_enabled": "Хоёр хүчин зүйлийн баталгаажуулалт идэвхжсэн", "Two-factor_authentication_is_currently_disabled": "Хоёр хүчин зүйл таньж баталгаажуулах боломжгүй байна", 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 1ef28a41a280..2c14909ec239 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json @@ -2470,6 +2470,7 @@ "Turn_OFF": "Matikan", "Turn_ON": "Hidupkan", "Two-factor_authentication": "Pengesahan dua faktor", + "Two-factor_authentication_via_TOTP": "Pengesahan dua faktor", "Two-factor_authentication_disabled": "Pengesahan dua faktor dilumpuhkan", "Two-factor_authentication_enabled": "Pengesahan dua faktor didayakan", "Two-factor_authentication_is_currently_disabled": "Pengesahan dua faktor kini dilumpuhkan", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json index e7e78719fd12..29a69550e75e 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json @@ -4293,6 +4293,7 @@ "Turn_off_video": "Video uitschakelen", "Two Factor Authentication": "Twee-factorenauthenticatie", "Two-factor_authentication": "Tweefactorauthenticatie via TOTP", + "Two-factor_authentication_via_TOTP": "Tweefactorauthenticatie via TOTP", "Two-factor_authentication_disabled": "Tweefactorauthenticatie uitgeschakeld", "Two-factor_authentication_email": "Tweefactorauthenticatie via e-mail", "Two-factor_authentication_email_is_currently_disabled": "Tweefactorauthentificatie via e-mail is momenteel uitgeschakeld", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json index cd612458dec7..2c27c0fca972 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json @@ -2872,8 +2872,8 @@ "meteor_status_connecting": "Kobler til...", "meteor_status_failed": "Servertilkoblingen mislyktes", "meteor_status_offline": "Frakoblet modus.", - "meteor_status_reconnect_in_other": "prøver igjen om {{count}} sekunder...", "meteor_status_reconnect_in_one": "prøver igjen om {{count}} sekunder...", + "meteor_status_reconnect_in_other": "prøver igjen om {{count}} sekunder...", "meteor_status_try_now_offline": "Koble til igjen", "meteor_status_try_now_waiting": "Prøv nå", "meteor_status_waiting": "Venter på serverforbindelse,", @@ -2905,6 +2905,7 @@ "Moderation_User_deactivated": "Bruker deaktivert", "Moderation_Delete_all_messages": "Slett alle meldinger", "Moderation_Duplicate_messages": "Dupliserte meldinger", + "Moderation_Reports": "Rapporter", "Moderation_Reported_message": "Rapportert melding", "Moderation_Message_already_deleted": "Meldingen er allerede slettet", "Moderation_Reset_user_avatar": "Tilbakestill brukeravatar", @@ -4098,6 +4099,7 @@ "Turn_off_video": "Slå av video", "Two Factor Authentication": "Tofaktorautentisering", "Two-factor_authentication": "Tofaktorautentisering", + "Two-factor_authentication_via_TOTP": "Tofaktorautentisering", "Two-factor_authentication_disabled": "Tofaktorautentisering deaktivert", "Two-factor_authentication_email": "Tofaktorautentisering via e-post", "Two-factor_authentication_email_is_currently_disabled": "Tofaktorautentisering via e-post er deaktivert for øyeblikket ", @@ -4561,4 +4563,4 @@ "free_per_month_user": "$0 per måned per bruker", "UpgradeToGetMore_engagement-dashboard_Title": "Analytics", "Buy_more": "Kjøp mer" -} +} \ 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 1e33996fb5df..1b4b1e7721d7 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json @@ -1,17 +1,17 @@ { "500": "Wewnętrzny błąd serwera", "__count__empty_rooms_will_be_removed_automatically": "Liczba pokojów do automatycznego usunięcia: {{count}}.", - "__count__empty_rooms_will_be_removed_automatically__rooms__": "Puste pokoje ({{count}}) zostaną automatycznie usunięte:
          {{rooms}}.", "__count__message_pruned_few": "{{count}} wiadomość(i) usuniętych", + "__count__empty_rooms_will_be_removed_automatically__rooms__": "Puste pokoje ({{count}}) zostaną automatycznie usunięte:
          {{rooms}}.", "__count__message_pruned_one": "{{count}} wiadomość(i) usuniętych", - "__count__message_pruned_many": "{{count}} wiadomość(i) usuniętych", "__count__message_pruned_other": "{{count}} wiadomość(i) usuniętych", + "__count__message_pruned_many": "{{count}} wiadomość(i) usuniętych", "__usersCount__member_joined_few": "+ {{count}} członków dołączyło", "__usersCount__member_joined_one": "+ {{count}} członek dołączył", "__usersCount__member_joined_other": "+ {{count}} członków dołączyło", - "__usersCount__member_joined_many": "+ {{count}} członków dołączyło", "__usersCount__people_will_be_invited": "{{usersCount}} ludzi zostanie zostanie zaproszonych", "__username__is_no_longer__role__defined_by__user_by_": "Użytkownik {{username}} nie ma już roli {{role}}; zmienił to użytkownik {{user_by}}", + "__usersCount__member_joined_many": "+ {{count}} członków dołączyło", "__username__was_set__role__by__user_by_": "Użytkownik {{username}} otrzymał rolę {{role}} od użytkownika {{user_by}}", "This_room_encryption_has_been_enabled_by__username_": "Użytkownik {{username}} włączył szyfrowanie w tym pokoju", "This_room_encryption_has_been_disabled_by__username_": "Użytkownik {{username}} wyłączył szyfrowanie w tym pokoju", @@ -522,6 +522,7 @@ "Apps_License_Message_appId": "Licencja nie została wydana dla tej aplikacji", "Apps_License_Message_bundle": "Licencja wydana dla paczki, która nie zawiera aplikacji", "Apps_License_Message_expire": "Licencja straciła ważność i musi zostać odnowiona", + "Calls_in_queue_many": "{{count}} połączeń w kolejce", "Apps_License_Message_maxSeats": "Licencja nie jest dostosowana do obecnej liczby aktywnych użytkowników. Zwiększ liczbę miejsc", "Apps_License_Message_publicKey": "Wystąpił błąd podczas próby odszyfrowania licencji. Zsynchronizuj przestrzeń roboczą w usługach łączności i spróbuj ponownie", "Apps_License_Message_renewal": "Licencja wygasła i wymaga odnowienia", @@ -573,6 +574,7 @@ "Apps_Permissions_livechat-message_read": "Uzyskaj dostęp do informacji o wiadomościach Livechat", "Apps_Permissions_livechat-message_write": "Zmień informacje o wiadomościach Livechat", "Apps_Permissions_livechat-room_read": "Uzyskaj dostęp do informacji o pokojach Livechat", + "Calls_in_queue_few": "{{count}} połączeń w kolejce", "Apps_Permissions_livechat-room_write": "Zmień informacje o pokojach Livechat", "Apps_Permissions_livechat-department_read": "Uzyskaj dostęp do informacji o działach Livechat", "Apps_Permissions_livechat-department_multiple": "Dostęp do informacji o wielu działach Livechat", @@ -636,9 +638,7 @@ "Auth_Token": "Token uwierzytelniający", "Authentication": "Uwierzytelnianie", "Author": "Autor", - "Calls_in_queue_few": "{{count}} połączeń w kolejce", "Author_Information": "Informacje o autorze", - "Calls_in_queue_many": "{{count}} połączeń w kolejce", "Author_Site": "Strona autora", "Authorization_URL": "Adres URL uwierzytelniania", "Authorize": "Autoryzuj", @@ -2130,6 +2130,7 @@ "Finish": "Koniec", "Finish_Registration": "Zakończ rejestrację", "First_Channel_After_Login": "Pierwszy Channel po zalogowaniu", + "message_counter_many": "{{count}} wiadomości", "First_response_time": "Czas pierwszej reakcji", "Flags": "Flagi", "Follow_message": "Śledź wiadomość", @@ -2192,6 +2193,7 @@ "get-password-policy-mustContainAtLeastOneUppercase": "Hasło powinno zawierać co najmniej jedną wielką literę", "get-server-info": "Pobierz info o serwerze", "get-server-info_description": "Pozwolenie na uzyskanie informacji o serwerze", + "meteor_status_reconnect_in_many": "spróbuj jeszcze raz za {{count}} sekund...", "github_no_public_email": "Nie posiadasz publicznego konta e-mail przypisanego do swojego profilu GitHub.", "github_HEAD": "HEAD", "Give_a_unique_name_for_the_custom_oauth": "Podaj unikalną nazwę dla własnego OAuth", @@ -2295,6 +2297,7 @@ "Ignore_Two_Factor_Authentication": "Ignorowanie uwierzytelniania dwuskładnikowego", "Images": "Obrazki", "IMAP_intercepter_already_running": "Intercom protokołu IMAP już działa", + "message_counter_few": "{{count}} wiadomości", "IMAP_intercepter_Not_running": "Intercom protokołu IMAP Nie działa", "Impersonate_next_agent_from_queue": "Podszywaj się pod kolejnego agenta z kolejki", "Impersonate_user": "Podszywać się pod użytkownika", @@ -2370,6 +2373,7 @@ "Install": "Zainstaluj", "Install_Extension": "Zainstaluj rozszerzenie", "Install_FxOs": "Zainstaluj Rocket.Chat w Firefoksie", + "meteor_status_reconnect_in_few": "spróbuj jeszcze raz za {{count}} sekund...", "Install_FxOs_done": "Świetnie! Możesz teraz włączać Rocket.Chat poprzez ikonę na ekranie głównym. Życzymy miłego korzystania z Rocket.Chat!", "Install_FxOs_error": "Niestety, coś nie zadziałało! Wystąpił następujący błąd:", "Install_FxOs_follow_instructions": "Potwierdź instalowanie aplikacji na twoim urządzeniu (gdy wyskoczy pytanie naciśnij przycisk \"Zainstaluj\").", @@ -2568,9 +2572,7 @@ "Language_Not_set": "Brak konkretów", "Language_Polish": "Polski", "Language_Portuguese": "Portugalski", - "message_counter_few": "{{count}} wiadomości", "Language_Romanian": "Rumuński", - "message_counter_many": "{{count}} wiadomości", "Language_Russian": "Rosyjski", "Language_Slovak": "Słowacki", "Language_Slovenian": "Słoweński", @@ -2646,8 +2648,6 @@ "LDAP_Background_Sync_Import_New_Users": "Synchronizacja tła Importuj nowych użytkowników", "LDAP_Background_Sync_Import_New_Users_Description": "Zaimportuje wszystkich użytkowników (w oparciu o kryteria filtru), które istnieją w LDAP i nie istnieje w Rocket.Chat", "LDAP_Background_Sync_Interval": "Interwał synchronizacji tła", - "meteor_status_reconnect_in_few": "spróbuj jeszcze raz za {{count}} sekund...", - "meteor_status_reconnect_in_many": "spróbuj jeszcze raz za {{count}} sekund...", "LDAP_Background_Sync_Interval_Description": "Odstęp między synchronizacjami. Przykład \"co 24 godziny\" lub \"pierwszego dnia tygodnia\", więcej przykładów w [Cron Text Parser] (http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Aktualizacja synchronizacji w tle Istniejących użytkowników", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Zsynchronizuje awatar, pola, nazwę użytkownika itp. (W zależności od konfiguracji) wszystkich użytkowników już zaimportowanych z LDAP na każdy ** Interwał synchronizacji **", @@ -4680,6 +4680,7 @@ "Turn_off_video": "Wyłącz wideo", "Two Factor Authentication": "Uwierzytelnianie dwuetapowe", "Two-factor_authentication": "Uwierzytelnianie dwuskładnikowe", + "Two-factor_authentication_via_TOTP": "Uwierzytelnianie dwuskładnikowe", "Two-factor_authentication_disabled": "Wyłączono uwierzytelnianie dwuskładnikowe", "Two-factor_authentication_email": "Dwustopniowa autoryzacja poprzez email", "Two-factor_authentication_email_is_currently_disabled": "Dwustopniowa autoryzacja poprzez email jest aktualnie wyłączona", 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 22ca1bc28ffd..982f90b04157 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -4,16 +4,16 @@ "__count__empty_rooms_will_be_removed_automatically": "{{count}} salas vazias serão removidas automaticamente.", "__count__empty_rooms_will_be_removed_automatically__rooms__": "{{count}} salas vazias serão removidas automaticamente:
          {{rooms}}.", "__count__message_pruned_one": "{{count}} mensagem apagada", - "__count__message_pruned_many": "{{count}} mensagens apagadas", "__count__message_pruned_other": "{{count}} mensagens apagadas", + "__count__message_pruned_many": "{{count}} mensagens apagadas", "__count__conversations__period__": "{{count}} conversas, {{period}}", "__count__tags__and__count__conversations__period__": "{{count}} tags e {{conversations}} conversas, {{period}}", "__departments__departments_and__count__conversations__period__": "{{departments}} departmentos e {{count}} conversas, {{period}}", "__usersCount__member_joined_one": "+ um membro entrou", "__usersCount__member_joined_other": "+ {{count}} membros entraram", - "__usersCount__member_joined_many": "+ {{count}} membros entraram", "__usersCount__people_will_be_invited": "{{usersCount}} usuários vão ser convidados", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} não pertence mais a {{role}}, por {{user_by}}", + "__usersCount__member_joined_many": "+ {{count}} membros entraram", "__username__was_set__role__by__user_by_": "{{username}} foi definido como {{role}} por {{user_by}}", "__count__without__department__": "{{count}} sem departamento", "__count__without__tags__": "{{count}} sem tags", @@ -521,6 +521,7 @@ "Apps_License_Message_appId": "A licença foi gerada para um app diferente", "Apps_License_Message_bundle": "A licença foi gerada para um Bundle que não contém este app", "Apps_License_Message_expire": "A licença não é mais válida e precisa ser renovada", + "Calls_in_queue_many": "{{count}} chamadas na fila", "Apps_License_Message_maxSeats": "A licença não comporta a quantidade atual de usuários ativos. Aumente o número de lugares.", "Apps_License_Message_publicKey": "Ocorreu um erro ao tentar descriptografar a licença. Sincronize seu espaço de trabalho em Serviços de conectividade e tente novamente", "Apps_License_Message_renewal": "A licença expirou e precisa ser renovada", @@ -621,7 +622,6 @@ "Authentication": "Autenticação", "Author": "Autor", "Author_Information": "Informação sobre o autor", - "Calls_in_queue_many": "{{count}} chamadas na fila", "Author_Site": "Página do autor", "Authorization_URL": "URL de autorização", "Authorize": "Autorizar", @@ -2003,6 +2003,7 @@ "Finish": "Finalizar", "Finish_Registration": "Finalizar registro", "First_Channel_After_Login": "Primeiro canal após o login", + "message_counter_many": "{{count}} mensagens", "First_response_time": "Tempo para primeira resposta", "Flags": "Sinalizações", "Follow_message": "Seguir mensagem", @@ -2064,6 +2065,7 @@ "get-password-policy-mustContainAtLeastOneSpecialCharacter": "A senha deve conter no mínimo um caractere especial", "get-password-policy-mustContainAtLeastOneUppercase": "A senha deve conter no mínimo um caractere maiúsculo", "get-server-info": "Obter informações do servidor", + "meteor_status_reconnect_in_many": "tentando novamente em {{count}} segundos...", "github_no_public_email": "Você não possui um e-mail público em sua conta do GitHub", "github_HEAD": "HEAD", "Give_a_unique_name_for_the_custom_oauth": "Dê um nome exclusivo para o oauth customizado", @@ -2425,7 +2427,6 @@ "Language_Polish": "Polonês", "Language_Portuguese": "Português", "Language_Romanian": "Romeno", - "message_counter_many": "{{count}} mensagens", "Language_Russian": "Russo", "Language_Slovak": "Eslovaco", "Language_Slovenian": "Esloveno", @@ -2502,7 +2503,6 @@ "LDAP_Background_Sync_Import_New_Users": "Sincronização de fundo da importação de novos usuários", "LDAP_Background_Sync_Import_New_Users_Description": "Importará todos os usuários (com base em seus critérios de filtro) que existem no LDAP e não existe em Rocket.Chat", "LDAP_Background_Sync_Interval": "Intervalo de sincronização de fundo", - "meteor_status_reconnect_in_many": "tentando novamente em {{count}} segundos...", "LDAP_Background_Sync_Interval_Description": "O intervalo entre as sincronizações. Exemplo de \"a cada 24 horas\" ou \"no primeiro dia da semana\", mais exemplos em [Cron Text Parser] (http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Atualização da sincronização de plano de fundo de usuários existentes", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Vai sincronizar o avatar, os campos, o nome de usuário, etc. (com base na sua configuração) de todos os usuários já importados do LDAP em cada ** intervalo de sincronização **", @@ -4391,6 +4391,7 @@ "Turn_off_video": "Desligar o vídeo", "Two Factor Authentication": "Autenticação de dois fatores", "Two-factor_authentication": "Autenticação de dois fatores por TOTP", + "Two-factor_authentication_via_TOTP": "Autenticação de dois fatores por TOTP", "Two-factor_authentication_disabled": "Autenticação de dois fatores desativada", "Two-factor_authentication_email": "Autenticação de dois fatores por e-mail", "Two-factor_authentication_email_is_currently_disabled": "A autenticação de dois fatores por e-mail está atualmente desativada", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json index 1b932ba51cb1..0d8b0884a477 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json @@ -4,6 +4,7 @@ "__count__empty_rooms_will_be_removed_automatically__rooms__": "{{count}} salas vazias serão removidas automáticamente:
          {{rooms}}.", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} já não pertence a {{role}}, por {{user_by}}", "__username__was_set__role__by__user_by_": "{{username}} foi definido como {{role}} por {{user_by}}", + "disabled": "desativado", "@username": "@username", "@username_message": "@username ", "#channel": "#canal", @@ -2850,6 +2851,7 @@ "Turn_ON": "Ligar", "Two Factor Authentication": "Autenticação de dois fatores", "Two-factor_authentication": "Autenticação em dois passos", + "Two-factor_authentication_via_TOTP": "Autenticação em dois passos", "Two-factor_authentication_disabled": "Autenticação em dois passos desactivada", "Two-factor_authentication_enabled": "Autenticação em dois passos activada", "Two-factor_authentication_is_currently_disabled": "A autenticação em dois passos está actualmente desactivada", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json index 2593f37615be..ab8234afc67b 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json @@ -2462,6 +2462,7 @@ "Turn_OFF": "Opriți", "Turn_ON": "Aprinde", "Two-factor_authentication": "Autentificare în două factori", + "Two-factor_authentication_via_TOTP": "Autentificare în două factori", "Two-factor_authentication_disabled": "Autentificarea cu două factori este dezactivată", "Two-factor_authentication_enabled": "Autentificare cu două factori activată", "Two-factor_authentication_is_currently_disabled": "Verificarea în doi factori este dezactivată în prezent", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json index daf957d03060..3cb8c39659b7 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json @@ -1,17 +1,17 @@ { "500": "Внутренняя ошибка сервера", "__count__empty_rooms_will_be_removed_automatically": "{{count}}пустые комнаты будут удалены автоматически.", - "__count__empty_rooms_will_be_removed_automatically__rooms__": "{{count}} пустых чатов будет удалено автоматически:
          {{rooms}}.", "__count__message_pruned_few": "{{count}} сообщений удалено", + "__count__empty_rooms_will_be_removed_automatically__rooms__": "{{count}} пустых чатов будет удалено автоматически:
          {{rooms}}.", "__count__message_pruned_one": "{{count}} сообщение удалено", - "__count__message_pruned_many": "{{count}} сообщений удалено", "__count__message_pruned_other": "{{count}} сообщений удалено", + "__count__message_pruned_many": "{{count}} сообщений удалено", "__usersCount__member_joined_few": "+ {{count}} участников присоединилось", "__usersCount__member_joined_one": "+ {{count}} участников присоединилось", "__usersCount__member_joined_other": "+ {{count}} участников присоединилось", - "__usersCount__member_joined_many": "+ {{count}} участников присоединилось", "__usersCount__people_will_be_invited": "{{usersCount}} человек будет приглашено", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} больше не {{role}} по решению {{user_by}}", + "__usersCount__member_joined_many": "+ {{count}} участников присоединилось", "__username__was_set__role__by__user_by_": "{{username}} был установлен {{role}} по решению {{user_by}}", "This_room_encryption_has_been_enabled_by__username_": "Шифрование этой комнаты было включено {{username}}", "This_room_encryption_has_been_disabled_by__username_": "Шифрование этой комнаты было отключено {{username}}", @@ -446,12 +446,16 @@ "API_GitHub_Enterprise_URL_Description": "Пример: `https://domain.com` (без завершающего слеша)", "API_Gitlab_URL": "GitLab URL", "API_Personal_Access_Token_Generated": "Идентификатор персонального доступа успешно сгенерирован", + "Apps_Count_Enabled_few": "{{count}} приложений(-я) включено", "API_Personal_Access_Token_Generated_Text_Token_s_UserId_s": "Пожалуйста, аккуратно сохраните токен, так как вы больше не сможете его просматривать.
          Токен: {{token}}
          Ваш Id пользователя: {{userId}} ", + "Apps_Count_Enabled_many": "{{count}} приложений(-я) включено", "API_Personal_Access_Token_Name": "Идентификатор имени персонального доступа", "API_Personal_Access_Tokens_Regenerate_It": "Пересоздать токен", + "Private_Apps_Count_Enabled_few": "{{count}} приватных приложений включено", "API_Personal_Access_Tokens_Regenerate_Modal": "Если вы потеряли или забыли свой токен, вы можете его восстановить, но помните, что все приложения, использующие этот токен, должны быть обновлены", "API_Personal_Access_Tokens_Remove_Modal": "Вы действительно хотите удалить этот токен доступа?", "API_Personal_Access_Tokens_To_REST_API": "Личные токены доступа к rest API", + "Private_Apps_Count_Enabled_many": "{{count}} приватных приложений включено", "API_Rate_Limiter": "Ограничение частоты запросов к API", "API_Shield_Types": "Типы бейджей", "API_Shield_Types_Description": "Типы бейджей в виде списка с разделением запятой, выберите `online`, `channel` либо используйте `*` для выбора всех", @@ -477,13 +481,9 @@ "App_Information": "Информация о приложении", "Apps_context_enterprise": "Организация", "App_Installation": "Установка приложения", - "Apps_Count_Enabled_few": "{{count}} приложений(-я) включено", - "Apps_Count_Enabled_many": "{{count}} приложений(-я) включено", "App_not_enabled": "Приложение не включено", - "Private_Apps_Count_Enabled_few": "{{count}} приватных приложений включено", "App_not_found": "Приложение не найдено", "App_status_auto_enabled": "Включено", - "Private_Apps_Count_Enabled_many": "{{count}} приватных приложений включено", "App_status_constructed": "построенный", "App_status_disabled": "Отключено", "App_status_error_disabled": "Отключено: неизвестная ошибка", @@ -551,6 +551,7 @@ "Apps_License_Message_appId": "Лицензия на это приложение не выдавалась", "Apps_License_Message_bundle": "Лицензия выдана на пакет, не содержащий приложение", "Apps_License_Message_expire": "Лицензия больше не действительна и нуждается в продлении", + "Calls_in_queue_many": "{{count}} Звонков в очереди", "Apps_License_Message_maxSeats": "Лицензия не рассчитана на текущее количество активных пользователей. Пожалуйста, увеличьте количество мест", "Apps_License_Message_publicKey": "Произошла ошибка при попытке расшифровать лицензию. Пожалуйста, синхронизируйте рабочее пространство в службах подключения и повторите попытку", "Apps_License_Message_renewal": "Срок действия лицензии истек и ее необходимо продлить", @@ -602,6 +603,7 @@ "Apps_Permissions_livechat-message_read": "Доступ к информации о сообщениях Livechat", "Apps_Permissions_livechat-message_write": "Изменение информации о сообщениях Livechat", "Apps_Permissions_livechat-room_read": "Доступ к информации о чате Livechat", + "Calls_in_queue_few": "{{count}} Звонков в очереди", "Apps_Permissions_livechat-room_write": "Изменение информации о чате Livechat", "Apps_Permissions_livechat-department_read": "Доступ к информации отдела Livechat", "Apps_Permissions_livechat-department_multiple": "Доступ к информации нескольких отделов Livechat", @@ -666,9 +668,7 @@ "Auth_Token": "Токен авторизации", "Authentication": "Аутентификация", "Author": "Автор", - "Calls_in_queue_few": "{{count}} Звонков в очереди", "Author_Information": "Информация об авторе", - "Calls_in_queue_many": "{{count}} Звонков в очереди", "Author_Site": "Автор", "Authorization_URL": "Авторизация URL-адреса", "Authorize": "Авторизовать", @@ -2104,6 +2104,7 @@ "Finish": "Завершить", "Finish_Registration": "Завершение регистрации", "First_Channel_After_Login": "Открыть канал после авторизации", + "message_counter_many": "{{count}} сообщения", "First_response_time": "Время первого ответа", "Flags": "Флаги", "Follow_message": "Подписаться на сообщение", @@ -2162,6 +2163,7 @@ "get-password-policy-mustContainAtLeastOneSpecialCharacter": "Пароль должен содержать как минимум один специальный символ", "get-password-policy-mustContainAtLeastOneUppercase": "Пароль должен содержать как минимум одну заглавную букву", "get-server-info": "Получить информацию о сервере", + "meteor_status_reconnect_in_many": "пытается снова через {{count}} секунд ...", "github_no_public_email": "В вашей учетной записи GitHub отсутствует публично доступный адрес электронной почты", "github_HEAD": "HEAD", "Give_a_unique_name_for_the_custom_oauth": "Выберите имя OAuth сервиса", @@ -2259,6 +2261,7 @@ "Ignore_Two_Factor_Authentication": "Игнорировать двухфакторную авторизацию", "Images": "Изображения", "IMAP_intercepter_already_running": "Перехватчик IMAP уже запущен", + "message_counter_few": "{{count}} сообщения", "IMAP_intercepter_Not_running": "Перехватчик IMAP не запущен", "Impersonate_next_agent_from_queue": "Выполните олицетворение следующего агента из очереди", "Impersonate_user": "Представляться пользователем", @@ -2334,6 +2337,7 @@ "Install": "Устанавить", "Install_Extension": "Установить расширение", "Install_FxOs": "Установить Rocket.Chat на Firefox", + "meteor_status_reconnect_in_few": "пытается снова через {{count}} секунд ...", "Install_FxOs_done": "Отлично! Теперь вы можете использовать Rocket.Chat через иконку на вашем рабочем столе. Развлекайтесь вместе с Rocket.Chat!", "Install_FxOs_error": "Извините, что-то пошло не так! Появилась следующая ошибка:", "Install_FxOs_follow_instructions": "Подтвердите установку приложения на ваше устройство (при запросе нажмите \"Установить\").", @@ -2525,9 +2529,7 @@ "Language_Not_set": "Нет конкретных", "Language_Polish": "Польский", "Language_Portuguese": "Португальский", - "message_counter_few": "{{count}} сообщения", "Language_Romanian": "Румынский", - "message_counter_many": "{{count}} сообщения", "Language_Russian": "Русский", "Language_Slovak": "Словацкий", "Language_Slovenian": "Словенский", @@ -2600,8 +2602,6 @@ "LDAP_Background_Sync_Import_New_Users": "Фоновая синхронизация импортирует новых пользователей", "LDAP_Background_Sync_Import_New_Users_Description": "Импортирует всех пользователей (на основе критериев вашего фильтра), которые существуют в LDAP, и не существует в Rocket.Chat", "LDAP_Background_Sync_Interval": "Интервал фоновой синхронизации", - "meteor_status_reconnect_in_few": "пытается снова через {{count}} секунд ...", - "meteor_status_reconnect_in_many": "пытается снова через {{count}} секунд ...", "LDAP_Background_Sync_Interval_Description": "Интервал между синхронизациями. Пример: `каждые 24 часа` или `в первый день недели`, больше примеров в [Cron Text Parser] (http://bunkat.github.io/later/parsers.html#text)", "LDAP_Background_Sync_Keep_Existant_Users_Updated": "Фоновая синхронизация обновляет сущестующих пользователей", "LDAP_Background_Sync_Keep_Existant_Users_Updated_Description": "Будут синхронизироваться аватар, поля, логин итд (на основе вашей конфигурации) всех пользователей уже импортированных из LDAP каждый **Интервал синхронизации**", @@ -4482,6 +4482,7 @@ "Turn_off_video": "Выключить видео", "Two Factor Authentication": "Двухфакторная аутентификация", "Two-factor_authentication": "Двухфакторная аутентификация", + "Two-factor_authentication_via_TOTP": "Двухфакторная аутентификация", "Two-factor_authentication_disabled": "Двухфакторная аутентификация выключена", "Two-factor_authentication_email": "Двухфакторная аутентификация по электронной почте", "Two-factor_authentication_email_is_currently_disabled": "Двухфакторная аутентификация по Email в настоящее время отключена", 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 f71652a80321..ed0b76c0e3b2 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json @@ -2472,6 +2472,7 @@ "Turn_OFF": "Vypnúť", "Turn_ON": "Zapnúť", "Two-factor_authentication": "Dvojfaktorové overenie", + "Two-factor_authentication_via_TOTP": "Dvojfaktorové overenie", "Two-factor_authentication_disabled": "Zablokovanie dvoch faktorov", "Two-factor_authentication_enabled": "Dvojfaktorové overovanie povolené", "Two-factor_authentication_is_currently_disabled": "Dvojfaktorové overenie je momentálne zakázané", 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 e604aacaf83d..781710d36d50 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json @@ -2452,6 +2452,7 @@ "Turn_OFF": "Izklopi", "Turn_ON": "Vklopi", "Two-factor_authentication": "Dvojno preverjanje pristnosti", + "Two-factor_authentication_via_TOTP": "Dvojno preverjanje pristnosti", "Two-factor_authentication_disabled": "Dvojno preverjanje pristnosti je onemogočeno", "Two-factor_authentication_enabled": "Dvojno preverjanje pristnosti je omogočeno", "Two-factor_authentication_is_currently_disabled": "Dvojno preverjanje pristnosti je trenutno onemogočeno", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json index e00b6e61ed54..202d02670595 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json @@ -2462,6 +2462,7 @@ "Turn_OFF": "Fillo", "Turn_ON": "Ndez", "Two-factor_authentication": "Authentication me dy faktorë", + "Two-factor_authentication_via_TOTP": "Authentication me dy faktorë", "Two-factor_authentication_disabled": "Autentifikimi me dy faktorë është i paaftë", "Two-factor_authentication_enabled": "Aktivizimi i legalizimit me dy faktorë", "Two-factor_authentication_is_currently_disabled": "Vertetimi me dy faktorë është aktualisht i çaktivizuar", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json index 983a5a8668d4..80c9b9ae65fb 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json @@ -2285,6 +2285,7 @@ "Turn_OFF": "Искључи", "Turn_ON": "Укључити", "Two-factor_authentication": "Два-факторска аутентикација", + "Two-factor_authentication_via_TOTP": "Два-факторска аутентикација", "Two-factor_authentication_disabled": "Два-факторска аутентификација је онемогућена", "Two-factor_authentication_enabled": "Два-факторска аутентификација је омогућена", "Two-factor_authentication_is_currently_disabled": "Два-факторска аутентификација је тренутно онемогућена", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json index 03e5fe291668..d0a7678ab920 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json @@ -4936,6 +4936,7 @@ "Turn_off_video": "Stäng av video", "Two Factor Authentication": "Tvåfaktorsautentisering", "Two-factor_authentication": "Tvåfaktorsautentisering via TOTP", + "Two-factor_authentication_via_TOTP": "Tvåfaktorsautentisering", "Two-factor_authentication_disabled": "Tvåfaktorautentisering inaktiverad", "Two-factor_authentication_email": "Tvåfaktorsautentisering via e-post", "Two-factor_authentication_email_is_currently_disabled": "Tvåfaktorsautentisering via e-post är inaktiverat", 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 7702bf7f2dcd..b31adfdda065 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json @@ -2463,6 +2463,7 @@ "Turn_OFF": "அணைக்க", "Turn_ON": "திரும்பவும்", "Two-factor_authentication": "இரண்டு காரணி அங்கீகாரம்", + "Two-factor_authentication_via_TOTP": "இரண்டு காரணி அங்கீகாரம்", "Two-factor_authentication_disabled": "இரண்டு-காரணி அங்கீகாரம் முடக்கப்பட்டது", "Two-factor_authentication_enabled": "இரண்டு-காரணி அங்கீகாரம் இயக்கப்பட்டது", "Two-factor_authentication_is_currently_disabled": "இரண்டு-காரணி அங்கீகாரம் தற்போது முடக்கப்பட்டுள்ளது", 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 5707ac5c4ea4..1c786cf47aa9 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json @@ -2454,6 +2454,7 @@ "Turn_OFF": "ปิด", "Turn_ON": "เปิด", "Two-factor_authentication": "การตรวจสอบสิทธิ์แบบสองปัจจัย", + "Two-factor_authentication_via_TOTP": "การตรวจสอบสิทธิ์แบบสองปัจจัย", "Two-factor_authentication_disabled": "การปิดใช้งานการพิสูจน์ตัวตนแบบสองปัจจัย", "Two-factor_authentication_enabled": "การเปิดใช้งานการตรวจสอบสิทธิ์แบบสองปัจจัย", "Two-factor_authentication_is_currently_disabled": "การตรวจสอบสิทธิ์แบบสองปัจจัยถูกปิดใช้อยู่ในขณะนี้", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json index b933b54e8dee..3986ee173be0 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json @@ -2928,6 +2928,7 @@ "Turn_ON": "Aç", "Two Factor Authentication": "İki Aşamalı Kimlik Doğrulama", "Two-factor_authentication": "İki aşamalı kimlik doğrulama", + "Two-factor_authentication_via_TOTP": "İki aşamalı kimlik doğrulama", "Two-factor_authentication_disabled": "İki aşamalı kimlik doğrulama devre dışı", "Two-factor_authentication_enabled": "İki aşamalı kimlik doğrulama etkin", "Two-factor_authentication_is_currently_disabled": "İki aşamalı kimlik doğrulama şu anda devre dışı", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json index ca6bb2ecb72a..d09362b93dcc 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json @@ -1549,6 +1549,7 @@ "Filters": "Фільтри", "Financial_Services": "Фінансові послуги", "First_Channel_After_Login": "Перший канал після входу", + "message_counter_many": "{{count}} повідомлень", "First_response_time": "Час першої відповіді", "Flags": "Прапори", "Follow_message": "Відслідковувати повідомлення", @@ -1668,6 +1669,7 @@ "Ignored": "Ігнорується", "Images": "Зображення", "IMAP_intercepter_already_running": "Інтерфейтер IMAP вже працює", + "message_counter_few": "{{count}} повідомлень", "IMAP_intercepter_Not_running": "Інтерфейс IMAP не працює", "Impersonate_next_agent_from_queue": "Використовуйте наступного представника з черги", "Impersonate_user": "Уособлення користувача", @@ -1891,8 +1893,6 @@ "Language_Not_set": "Немає специфіки", "Language_Polish": "Польська", "Language_Portuguese": "Португальська", - "message_counter_few": "{{count}} повідомлень", - "message_counter_many": "{{count}} повідомлень", "Language_Russian": "Російська", "Language_Spanish": "Іспанська", "Language_Version": "Українська версія", @@ -3024,6 +3024,7 @@ "Turn_ON": "Ввімкнути", "Two Factor Authentication": "Двофакторна аутентифікація", "Two-factor_authentication": "Двофакторна аутентифікація", + "Two-factor_authentication_via_TOTP": "Двофакторна аутентифікація", "Two-factor_authentication_disabled": "Двохфакторна автентифікація вимкнена", "Two-factor_authentication_enabled": "Двохфакторна аутентифікація включена", "Two-factor_authentication_is_currently_disabled": "Двофакторна автентифікація наразі відключена", 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 bea35acf1daa..002d257306b0 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json @@ -2562,6 +2562,7 @@ "Turn_OFF": "Tắt", "Turn_ON": "Bật", "Two-factor_authentication": "Xác thực hai yếu tố", + "Two-factor_authentication_via_TOTP": "Xác thực hai yếu tố", "Two-factor_authentication_disabled": "Xác thực hai yếu tố bị vô hiệu hoá", "Two-factor_authentication_enabled": "Xác thực hai yếu tố được kích hoạt", "Two-factor_authentication_is_currently_disabled": "Xác thực hai yếu tố hiện đang bị vô hiệu hóa", 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 f6515407896d..1b4f84a32d60 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json @@ -2487,6 +2487,7 @@ "Turn_OFF": "关掉", "Turn_ON": "打开", "Two-factor_authentication": "双因素认证", + "Two-factor_authentication_via_TOTP": "双因素认证", "Two-factor_authentication_disabled": "双因素身份验证被禁用", "Two-factor_authentication_enabled": "启用双因素身份验证", "Two-factor_authentication_is_currently_disabled": "双因素认证目前被禁用", 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 a3d0b0e9cc7c..daab1ff46180 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json @@ -4067,6 +4067,7 @@ "Turn_ON": "打開", "Two Factor Authentication": "2步驟驗證", "Two-factor_authentication": "透過 TOTP 2步驟驗證", + "Two-factor_authentication_via_TOTP": "透過 TOTP 2步驟驗證", "Two-factor_authentication_disabled": "2步驟驗證被停用", "Two-factor_authentication_email": "通過電子郵件進行2步驟驗證", "Two-factor_authentication_email_is_currently_disabled": "目前已停用透過電子郵件進行的2步驟驗證", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json index c860b2b37ef3..de49283fd471 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json @@ -3717,6 +3717,7 @@ "Turn_ON": "打开", "Two Factor Authentication": "两步验证", "Two-factor_authentication": "基于 TOTP 的两步验证", + "Two-factor_authentication_via_TOTP": "基于 TOTP 的两步验证", "Two-factor_authentication_disabled": "两步验证被禁用", "Two-factor_authentication_email": "基于邮件的两步验证", "Two-factor_authentication_email_is_currently_disabled": "基于邮件的两步验证当前被禁用", From be9dc386ac9ee7f5d77efa88d8f7883698c3ff25 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 21 Feb 2024 13:59:44 -0300 Subject: [PATCH 056/207] chore: add missing packages to ddp-client (#31801) --- ee/packages/ddp-client/package.json | 5 +- yarn.lock | 82 +++++++++++++++++++++++++---- 2 files changed, 76 insertions(+), 11 deletions(-) diff --git a/ee/packages/ddp-client/package.json b/ee/packages/ddp-client/package.json index b71679329f27..b44e7b46b005 100644 --- a/ee/packages/ddp-client/package.json +++ b/ee/packages/ddp-client/package.json @@ -5,13 +5,14 @@ "devDependencies": { "@swc/core": "^1.3.95", "@swc/jest": "^0.2.29", - "@types/jest": "~29.5.7", + "@types/jest": "^29.5.12", "@types/ws": "^8.5.8", "eslint": "~8.45.0", "jest": "~29.6.4", "jest-environment-jsdom": "~29.6.4", "jest-websocket-mock": "^2.4.0", - "typescript": "~5.3.2", + "ts-jest": "^29.1.2", + "typescript": "^5.3.3", "ws": "^8.13.0" }, "peerDependencies": { diff --git a/yarn.lock b/yarn.lock index 307f3e2a787e..a82a88ee4157 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9090,13 +9090,14 @@ __metadata: "@rocket.chat/rest-typings": "workspace:^" "@swc/core": ^1.3.95 "@swc/jest": ^0.2.29 - "@types/jest": ~29.5.7 + "@types/jest": ^29.5.12 "@types/ws": ^8.5.8 eslint: ~8.45.0 jest: ~29.6.4 jest-environment-jsdom: ~29.6.4 jest-websocket-mock: ^2.4.0 - typescript: ~5.3.2 + ts-jest: ^29.1.2 + typescript: ^5.3.3 ws: ^8.13.0 peerDependencies: "@rocket.chat/emitter": "*" @@ -9350,9 +9351,9 @@ __metadata: "@rocket.chat/prettier-config": "*" "@rocket.chat/styled": "*" "@rocket.chat/ui-avatar": "*" - "@rocket.chat/ui-contexts": 4.0.0 + "@rocket.chat/ui-contexts": 4.0.1 "@rocket.chat/ui-kit": 0.33.0 - "@rocket.chat/ui-video-conf": 4.0.0 + "@rocket.chat/ui-video-conf": 4.0.1 "@tanstack/react-query": "*" react: "*" react-dom: "*" @@ -9440,8 +9441,8 @@ __metadata: "@rocket.chat/fuselage-tokens": "*" "@rocket.chat/message-parser": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-client": 4.0.0 - "@rocket.chat/ui-contexts": 4.0.0 + "@rocket.chat/ui-client": 4.0.1 + "@rocket.chat/ui-contexts": 4.0.1 katex: "*" react: "*" languageName: unknown @@ -10648,7 +10649,7 @@ __metadata: "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/icons": "*" - "@rocket.chat/ui-contexts": 4.0.0 + "@rocket.chat/ui-contexts": 4.0.1 react: ~17.0.2 languageName: unknown linkType: soft @@ -10825,7 +10826,7 @@ __metadata: "@rocket.chat/icons": "*" "@rocket.chat/styled": "*" "@rocket.chat/ui-avatar": "*" - "@rocket.chat/ui-contexts": 4.0.0 + "@rocket.chat/ui-contexts": 4.0.1 react: ^17.0.2 react-dom: ^17.0.2 languageName: unknown @@ -10915,7 +10916,7 @@ __metadata: peerDependencies: "@rocket.chat/layout": "*" "@rocket.chat/tools": 0.2.1 - "@rocket.chat/ui-contexts": 4.0.0 + "@rocket.chat/ui-contexts": 4.0.1 "@tanstack/react-query": "*" react: "*" react-hook-form: "*" @@ -14036,6 +14037,16 @@ __metadata: languageName: node linkType: hard +"@types/jest@npm:^29.5.12": + version: 29.5.12 + resolution: "@types/jest@npm:29.5.12" + dependencies: + expect: ^29.0.0 + pretty-format: ^29.0.0 + checksum: 19b1efdeed9d9a60a81edc8226cdeae5af7479e493eaed273e01243891c9651f7b8b4c08fc633a7d0d1d379b091c4179bbaa0807af62542325fd72f2dd17ce1c + languageName: node + linkType: hard + "@types/jest@npm:~29.5.0": version: 29.5.10 resolution: "@types/jest@npm:29.5.10" @@ -39860,6 +39871,39 @@ __metadata: languageName: node linkType: hard +"ts-jest@npm:^29.1.2": + version: 29.1.2 + resolution: "ts-jest@npm:29.1.2" + dependencies: + bs-logger: 0.x + fast-json-stable-stringify: 2.x + jest-util: ^29.0.0 + json5: ^2.2.3 + lodash.memoize: 4.x + make-error: 1.x + semver: ^7.5.3 + yargs-parser: ^21.0.1 + peerDependencies: + "@babel/core": ">=7.0.0-beta.0 <8" + "@jest/types": ^29.0.0 + babel-jest: ^29.0.0 + jest: ^29.0.0 + typescript: ">=4.3 <6" + peerDependenciesMeta: + "@babel/core": + optional: true + "@jest/types": + optional: true + babel-jest: + optional: true + esbuild: + optional: true + bin: + ts-jest: cli.js + checksum: a0ce0affc1b716c78c9ab55837829c42cb04b753d174a5c796bb1ddf9f0379fc20647b76fbe30edb30d9b23181908138d6b4c51ef2ae5e187b66635c295cefd5 + languageName: node + linkType: hard + "ts-loader@npm:~9.4.2": version: 9.4.4 resolution: "ts-loader@npm:9.4.4" @@ -40399,6 +40443,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:^5.3.3": + version: 5.3.3 + resolution: "typescript@npm:5.3.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 2007ccb6e51bbbf6fde0a78099efe04dc1c3dfbdff04ca3b6a8bc717991862b39fd6126c0c3ebf2d2d98ac5e960bcaa873826bb2bb241f14277034148f41f6a2 + languageName: node + linkType: hard + "typescript@patch:typescript@^5.3.2#~builtin, typescript@patch:typescript@~5.3.2#~builtin": version: 5.3.2 resolution: "typescript@patch:typescript@npm%3A5.3.2#~builtin::version=5.3.2&hash=85af82" @@ -40409,6 +40463,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@^5.3.3#~builtin": + version: 5.3.3 + resolution: "typescript@patch:typescript@npm%3A5.3.3#~builtin::version=5.3.3&hash=85af82" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: f61375590b3162599f0f0d5b8737877ac0a7bc52761dbb585d67e7b8753a3a4c42d9a554c4cc929f591ffcf3a2b0602f65ae3ce74714fd5652623a816862b610 + languageName: node + linkType: hard + "typia@npm:5.3.3, typia@npm:^5.3.3": version: 5.3.3 resolution: "typia@npm:5.3.3" From f0bb6803b04a07694e2b89f0fdad18e02b5479c9 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 21 Feb 2024 16:27:54 -0300 Subject: [PATCH 057/207] chore: move message-parser and peggy-loader to main repo (#31796) --- apps/meteor/ee/server/services/package.json | 2 +- apps/meteor/package.json | 2 +- ee/apps/account-service/Dockerfile | 7 + ee/apps/authorization-service/Dockerfile | 7 + ee/apps/ddp-streamer/Dockerfile | 7 + ee/apps/omnichannel-transcript/Dockerfile | 7 + ee/apps/presence-service/Dockerfile | 7 + ee/apps/queue-worker/Dockerfile | 7 + ee/apps/stream-hub-service/Dockerfile | 7 + ee/packages/ddp-client/package.json | 2 +- packages/core-services/package.json | 2 +- packages/core-typings/package.json | 2 +- packages/gazzodown/package.json | 2 +- packages/livechat/package.json | 2 +- packages/message-parser/.eslintignore | 2 + packages/message-parser/.eslintrc.js | 12 + packages/message-parser/.gitignore | 2 + packages/message-parser/.prettierrc.js | 1 + packages/message-parser/CHANGELOG.md | 71 + packages/message-parser/README.md | 57 + packages/message-parser/babel.config.js | 18 + packages/message-parser/jest.config.js | 12 + .../message-parser/loaders/pegtransform.js | 10 + packages/message-parser/messageParser.js | 7 + packages/message-parser/package.json | 78 + packages/message-parser/src/definitions.ts | 249 +++ packages/message-parser/src/grammar.pegjs | 705 ++++++++ packages/message-parser/src/guards.ts | 10 + packages/message-parser/src/index.ts | 26 + packages/message-parser/src/typings/peg.d.ts | 7 + packages/message-parser/src/utils.ts | 235 +++ packages/message-parser/tests/any.test.ts | 13 + .../message-parser/tests/blockquotes.test.ts | 40 + .../message-parser/tests/codeFence.test.ts | 83 + packages/message-parser/tests/color.test.ts | 34 + packages/message-parser/tests/email.test.ts | 144 ++ packages/message-parser/tests/emoji.test.ts | 90 + .../message-parser/tests/emoticons.test.ts | 148 ++ .../message-parser/tests/emphasis.test.ts | 174 ++ packages/message-parser/tests/escaped.test.ts | 38 + packages/message-parser/tests/heading.test.ts | 58 + packages/message-parser/tests/image.test.ts | 19 + .../message-parser/tests/inlineCode.test.ts | 27 + .../tests/inlineCodeStrike.test.ts | 46 + packages/message-parser/tests/katex.test.ts | 33 + .../message-parser/tests/lineBreak.test.ts | 35 + packages/message-parser/tests/link.test.ts | 589 +++++++ packages/message-parser/tests/mention.test.ts | 34 + .../message-parser/tests/orderedList.test.ts | 25 + .../message-parser/tests/phoneNumber.test.ts | 63 + .../tests/strikethrough.test.ts | 122 ++ .../tests/strongEmphasis.test.ts | 156 ++ packages/message-parser/tests/tasks.test.ts | 49 + .../tests/unorderedList.test.ts | 78 + packages/message-parser/tests/url.test.ts | 362 ++++ packages/message-parser/tsconfig-bundle.json | 7 + packages/message-parser/tsconfig.json | 18 + packages/message-parser/typedoc.json | 4 + packages/message-parser/webpack.config.js | 86 + packages/peggy-loader/.eslintignore | 2 + packages/peggy-loader/.eslintrc.json | 8 + packages/peggy-loader/.gitignore | 1 + packages/peggy-loader/.prettierignore | 2 + packages/peggy-loader/.prettierrc.js | 1 + packages/peggy-loader/CHANGELOG.md | 43 + packages/peggy-loader/README.md | 89 + packages/peggy-loader/jest.config.js | 5 + packages/peggy-loader/package.json | 57 + packages/peggy-loader/src/index.ts | 39 + packages/peggy-loader/tsconfig-cjs.json | 7 + packages/peggy-loader/tsconfig.json | 16 + packages/rest-typings/package.json | 2 +- packages/uikit-playground/vite.config.ts | 4 +- yarn.lock | 1483 +++++++---------- 74 files changed, 4969 insertions(+), 930 deletions(-) create mode 100644 packages/message-parser/.eslintignore create mode 100644 packages/message-parser/.eslintrc.js create mode 100644 packages/message-parser/.gitignore create mode 100644 packages/message-parser/.prettierrc.js create mode 100644 packages/message-parser/CHANGELOG.md create mode 100644 packages/message-parser/README.md create mode 100644 packages/message-parser/babel.config.js create mode 100644 packages/message-parser/jest.config.js create mode 100644 packages/message-parser/loaders/pegtransform.js create mode 100644 packages/message-parser/messageParser.js create mode 100644 packages/message-parser/package.json create mode 100644 packages/message-parser/src/definitions.ts create mode 100644 packages/message-parser/src/grammar.pegjs create mode 100644 packages/message-parser/src/guards.ts create mode 100644 packages/message-parser/src/index.ts create mode 100644 packages/message-parser/src/typings/peg.d.ts create mode 100644 packages/message-parser/src/utils.ts create mode 100644 packages/message-parser/tests/any.test.ts create mode 100644 packages/message-parser/tests/blockquotes.test.ts create mode 100644 packages/message-parser/tests/codeFence.test.ts create mode 100644 packages/message-parser/tests/color.test.ts create mode 100644 packages/message-parser/tests/email.test.ts create mode 100644 packages/message-parser/tests/emoji.test.ts create mode 100644 packages/message-parser/tests/emoticons.test.ts create mode 100644 packages/message-parser/tests/emphasis.test.ts create mode 100644 packages/message-parser/tests/escaped.test.ts create mode 100644 packages/message-parser/tests/heading.test.ts create mode 100644 packages/message-parser/tests/image.test.ts create mode 100644 packages/message-parser/tests/inlineCode.test.ts create mode 100644 packages/message-parser/tests/inlineCodeStrike.test.ts create mode 100644 packages/message-parser/tests/katex.test.ts create mode 100644 packages/message-parser/tests/lineBreak.test.ts create mode 100644 packages/message-parser/tests/link.test.ts create mode 100644 packages/message-parser/tests/mention.test.ts create mode 100644 packages/message-parser/tests/orderedList.test.ts create mode 100644 packages/message-parser/tests/phoneNumber.test.ts create mode 100644 packages/message-parser/tests/strikethrough.test.ts create mode 100644 packages/message-parser/tests/strongEmphasis.test.ts create mode 100644 packages/message-parser/tests/tasks.test.ts create mode 100644 packages/message-parser/tests/unorderedList.test.ts create mode 100644 packages/message-parser/tests/url.test.ts create mode 100644 packages/message-parser/tsconfig-bundle.json create mode 100644 packages/message-parser/tsconfig.json create mode 100644 packages/message-parser/typedoc.json create mode 100644 packages/message-parser/webpack.config.js create mode 100644 packages/peggy-loader/.eslintignore create mode 100644 packages/peggy-loader/.eslintrc.json create mode 100644 packages/peggy-loader/.gitignore create mode 100644 packages/peggy-loader/.prettierignore create mode 100644 packages/peggy-loader/.prettierrc.js create mode 100644 packages/peggy-loader/CHANGELOG.md create mode 100644 packages/peggy-loader/README.md create mode 100644 packages/peggy-loader/jest.config.js create mode 100644 packages/peggy-loader/package.json create mode 100644 packages/peggy-loader/src/index.ts create mode 100644 packages/peggy-loader/tsconfig-cjs.json create mode 100644 packages/peggy-loader/tsconfig.json diff --git a/apps/meteor/ee/server/services/package.json b/apps/meteor/ee/server/services/package.json index b8fe731d1ab5..00a425ac60ef 100644 --- a/apps/meteor/ee/server/services/package.json +++ b/apps/meteor/ee/server/services/package.json @@ -22,7 +22,7 @@ "@rocket.chat/core-services": "workspace:^", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/emitter": "~0.31.25", - "@rocket.chat/message-parser": "~0.31.28", + "@rocket.chat/message-parser": "workspace:^", "@rocket.chat/model-typings": "workspace:^", "@rocket.chat/models": "workspace:^", "@rocket.chat/rest-typings": "workspace:^", diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 5cf08d8b2b0e..de5e4eacb9b8 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -257,7 +257,7 @@ "@rocket.chat/logger": "workspace:^", "@rocket.chat/logo": "^0.31.29", "@rocket.chat/memo": "~0.31.25", - "@rocket.chat/message-parser": "~0.31.28", + "@rocket.chat/message-parser": "workspace:^", "@rocket.chat/model-typings": "workspace:^", "@rocket.chat/models": "workspace:^", "@rocket.chat/mp3-encoder": "0.24.0", diff --git a/ee/apps/account-service/Dockerfile b/ee/apps/account-service/Dockerfile index a4b34c465100..c662d8765300 100644 --- a/ee/apps/account-service/Dockerfile +++ b/ee/apps/account-service/Dockerfile @@ -16,6 +16,13 @@ COPY ./packages/core-typings/dist packages/core-typings/dist COPY ./packages/rest-typings/package.json packages/rest-typings/package.json COPY ./packages/rest-typings/dist packages/rest-typings/dist +COPY ./packages/message-parser/package.json packages/message-parser/package.json +COPY ./packages/message-parser/dist packages/message-parser/dist +COPY ./packages/message-parser/messageParser.js packages/message-parser/messageParser.js + +COPY ./packages/peggy-loader/package.json packages/peggy-loader/package.json +COPY ./packages/peggy-loader/dist packages/peggy-loader/dist + COPY ./packages/model-typings/package.json packages/model-typings/package.json COPY ./packages/model-typings/dist packages/model-typings/dist diff --git a/ee/apps/authorization-service/Dockerfile b/ee/apps/authorization-service/Dockerfile index a4b34c465100..c662d8765300 100644 --- a/ee/apps/authorization-service/Dockerfile +++ b/ee/apps/authorization-service/Dockerfile @@ -16,6 +16,13 @@ COPY ./packages/core-typings/dist packages/core-typings/dist COPY ./packages/rest-typings/package.json packages/rest-typings/package.json COPY ./packages/rest-typings/dist packages/rest-typings/dist +COPY ./packages/message-parser/package.json packages/message-parser/package.json +COPY ./packages/message-parser/dist packages/message-parser/dist +COPY ./packages/message-parser/messageParser.js packages/message-parser/messageParser.js + +COPY ./packages/peggy-loader/package.json packages/peggy-loader/package.json +COPY ./packages/peggy-loader/dist packages/peggy-loader/dist + COPY ./packages/model-typings/package.json packages/model-typings/package.json COPY ./packages/model-typings/dist packages/model-typings/dist diff --git a/ee/apps/ddp-streamer/Dockerfile b/ee/apps/ddp-streamer/Dockerfile index 686a6c4183a9..893fdcff858b 100644 --- a/ee/apps/ddp-streamer/Dockerfile +++ b/ee/apps/ddp-streamer/Dockerfile @@ -16,6 +16,13 @@ COPY ./packages/core-typings/dist packages/core-typings/dist COPY ./packages/rest-typings/package.json packages/rest-typings/package.json COPY ./packages/rest-typings/dist packages/rest-typings/dist +COPY ./packages/message-parser/package.json packages/message-parser/package.json +COPY ./packages/message-parser/dist packages/message-parser/dist +COPY ./packages/message-parser/messageParser.js packages/message-parser/messageParser.js + +COPY ./packages/peggy-loader/package.json packages/peggy-loader/package.json +COPY ./packages/peggy-loader/dist packages/peggy-loader/dist + COPY ./packages/password-policies/package.json packages/password-policies/package.json COPY ./packages/password-policies/dist packages/password-policies/dist diff --git a/ee/apps/omnichannel-transcript/Dockerfile b/ee/apps/omnichannel-transcript/Dockerfile index c2c33aa5017c..6a93a8e5e8be 100644 --- a/ee/apps/omnichannel-transcript/Dockerfile +++ b/ee/apps/omnichannel-transcript/Dockerfile @@ -16,6 +16,13 @@ COPY ./packages/core-typings/dist packages/core-typings/dist COPY ./packages/rest-typings/package.json packages/rest-typings/package.json COPY ./packages/rest-typings/dist packages/rest-typings/dist +COPY ./packages/message-parser/package.json packages/message-parser/package.json +COPY ./packages/message-parser/dist packages/message-parser/dist +COPY ./packages/message-parser/messageParser.js packages/message-parser/messageParser.js + +COPY ./packages/peggy-loader/package.json packages/peggy-loader/package.json +COPY ./packages/peggy-loader/dist packages/peggy-loader/dist + COPY ./packages/model-typings/package.json packages/model-typings/package.json COPY ./packages/model-typings/dist packages/model-typings/dist diff --git a/ee/apps/presence-service/Dockerfile b/ee/apps/presence-service/Dockerfile index 19a5cb570766..a6b6e641a39d 100644 --- a/ee/apps/presence-service/Dockerfile +++ b/ee/apps/presence-service/Dockerfile @@ -19,6 +19,13 @@ COPY ./packages/core-typings/dist packages/core-typings/dist COPY ./packages/rest-typings/package.json packages/rest-typings/package.json COPY ./packages/rest-typings/dist packages/rest-typings/dist +COPY ./packages/message-parser/package.json packages/message-parser/package.json +COPY ./packages/message-parser/dist packages/message-parser/dist +COPY ./packages/message-parser/messageParser.js packages/message-parser/messageParser.js + +COPY ./packages/peggy-loader/package.json packages/peggy-loader/package.json +COPY ./packages/peggy-loader/dist packages/peggy-loader/dist + COPY ./packages/model-typings/package.json packages/model-typings/package.json COPY ./packages/model-typings/dist packages/model-typings/dist diff --git a/ee/apps/queue-worker/Dockerfile b/ee/apps/queue-worker/Dockerfile index c2c33aa5017c..6a93a8e5e8be 100644 --- a/ee/apps/queue-worker/Dockerfile +++ b/ee/apps/queue-worker/Dockerfile @@ -16,6 +16,13 @@ COPY ./packages/core-typings/dist packages/core-typings/dist COPY ./packages/rest-typings/package.json packages/rest-typings/package.json COPY ./packages/rest-typings/dist packages/rest-typings/dist +COPY ./packages/message-parser/package.json packages/message-parser/package.json +COPY ./packages/message-parser/dist packages/message-parser/dist +COPY ./packages/message-parser/messageParser.js packages/message-parser/messageParser.js + +COPY ./packages/peggy-loader/package.json packages/peggy-loader/package.json +COPY ./packages/peggy-loader/dist packages/peggy-loader/dist + COPY ./packages/model-typings/package.json packages/model-typings/package.json COPY ./packages/model-typings/dist packages/model-typings/dist diff --git a/ee/apps/stream-hub-service/Dockerfile b/ee/apps/stream-hub-service/Dockerfile index a4b34c465100..c662d8765300 100644 --- a/ee/apps/stream-hub-service/Dockerfile +++ b/ee/apps/stream-hub-service/Dockerfile @@ -16,6 +16,13 @@ COPY ./packages/core-typings/dist packages/core-typings/dist COPY ./packages/rest-typings/package.json packages/rest-typings/package.json COPY ./packages/rest-typings/dist packages/rest-typings/dist +COPY ./packages/message-parser/package.json packages/message-parser/package.json +COPY ./packages/message-parser/dist packages/message-parser/dist +COPY ./packages/message-parser/messageParser.js packages/message-parser/messageParser.js + +COPY ./packages/peggy-loader/package.json packages/peggy-loader/package.json +COPY ./packages/peggy-loader/dist packages/peggy-loader/dist + COPY ./packages/model-typings/package.json packages/model-typings/package.json COPY ./packages/model-typings/dist packages/model-typings/dist diff --git a/ee/packages/ddp-client/package.json b/ee/packages/ddp-client/package.json index b44e7b46b005..8ae25f9b443b 100644 --- a/ee/packages/ddp-client/package.json +++ b/ee/packages/ddp-client/package.json @@ -12,7 +12,7 @@ "jest-environment-jsdom": "~29.6.4", "jest-websocket-mock": "^2.4.0", "ts-jest": "^29.1.2", - "typescript": "^5.3.3", + "typescript": "^5.3.2", "ws": "^8.13.0" }, "peerDependencies": { diff --git a/packages/core-services/package.json b/packages/core-services/package.json index fbe4af901821..868775a355f3 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -37,7 +37,7 @@ "@rocket.chat/apps-engine": "1.41.0", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/icons": "^0.33.0", - "@rocket.chat/message-parser": "~0.31.28", + "@rocket.chat/message-parser": "workspace:^", "@rocket.chat/models": "workspace:^", "@rocket.chat/rest-typings": "workspace:^", "@rocket.chat/ui-kit": "workspace:~", diff --git a/packages/core-typings/package.json b/packages/core-typings/package.json index 462a4bb66eda..930fbc77717b 100644 --- a/packages/core-typings/package.json +++ b/packages/core-typings/package.json @@ -25,7 +25,7 @@ "dependencies": { "@rocket.chat/apps-engine": "1.41.0", "@rocket.chat/icons": "^0.33.0", - "@rocket.chat/message-parser": "~0.31.28", + "@rocket.chat/message-parser": "workspace:^", "@rocket.chat/ui-kit": "workspace:~" }, "volta": { diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index 8618d7d8eb25..108329360d05 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -8,7 +8,7 @@ "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/fuselage": "^0.47.0", "@rocket.chat/fuselage-tokens": "~0.32.0", - "@rocket.chat/message-parser": "~0.31.28", + "@rocket.chat/message-parser": "workspace:^", "@rocket.chat/styled": "~0.31.25", "@rocket.chat/ui-client": "workspace:^", "@rocket.chat/ui-contexts": "workspace:^", diff --git a/packages/livechat/package.json b/packages/livechat/package.json index 16fab4481639..c5ce385f0345 100644 --- a/packages/livechat/package.json +++ b/packages/livechat/package.json @@ -95,7 +95,7 @@ }, "dependencies": { "@rocket.chat/gazzodown": "workspace:^", - "@rocket.chat/message-parser": "~0.31.28", + "@rocket.chat/message-parser": "workspace:^", "@rocket.chat/random": "workspace:~", "@rocket.chat/sdk": "^1.0.0-alpha.42", "@rocket.chat/ui-kit": "workspace:~", diff --git a/packages/message-parser/.eslintignore b/packages/message-parser/.eslintignore new file mode 100644 index 000000000000..8225baa4a77d --- /dev/null +++ b/packages/message-parser/.eslintignore @@ -0,0 +1,2 @@ +/node_modules +/dist diff --git a/packages/message-parser/.eslintrc.js b/packages/message-parser/.eslintrc.js new file mode 100644 index 000000000000..e8b738ac42e9 --- /dev/null +++ b/packages/message-parser/.eslintrc.js @@ -0,0 +1,12 @@ +module.exports = { + extends: '@rocket.chat/eslint-config', + env: { + jest: true, + }, + overrides: [ + { + files: ['**/*.ts', '**/*.tsx'], + extends: '@rocket.chat/eslint-config', + }, + ], +}; diff --git a/packages/message-parser/.gitignore b/packages/message-parser/.gitignore new file mode 100644 index 000000000000..8225baa4a77d --- /dev/null +++ b/packages/message-parser/.gitignore @@ -0,0 +1,2 @@ +/node_modules +/dist diff --git a/packages/message-parser/.prettierrc.js b/packages/message-parser/.prettierrc.js new file mode 100644 index 000000000000..b57f474edb93 --- /dev/null +++ b/packages/message-parser/.prettierrc.js @@ -0,0 +1 @@ +module.exports = require('@rocket.chat/prettier-config/fuselage'); diff --git a/packages/message-parser/CHANGELOG.md b/packages/message-parser/CHANGELOG.md new file mode 100644 index 000000000000..1fd31e07f13e --- /dev/null +++ b/packages/message-parser/CHANGELOG.md @@ -0,0 +1,71 @@ +# Change Log + +## 0.31.28 + +### Patch Changes + +- [`7fdfdb1b7`](https://github.com/RocketChat/fuselage/commit/7fdfdb1b7737808585b95cc62c4f9af2bc152b41) Thanks [@dougfabris](https://github.com/dougfabris)! - fix(message-parser): Made changes in grammar.pegjs for the strikedown approach + +## 0.31.27 + +### Patch Changes + +- [`a029dce78`](https://github.com/RocketChat/fuselage/commit/a029dce78935d8bba5cb5b09e251483fe8eabcb3) Thanks [@yash-rajpal](https://github.com/yash-rajpal)! - Stop accepting `[` in link titles + +## 0.31.26 + +### Patch Changes + +- [#1215](https://github.com/RocketChat/fuselage/pull/1215) [`684b73ca3`](https://github.com/RocketChat/fuselage/commit/684b73ca3b1e7c72f21f6dff23bfe46981ba472a) Thanks [@brf153](https://github.com/brf153)! - Added ChannelMention in the markup inside message-parser + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.31.0](https://github.com/RocketChat/fuselage/compare/v0.30.1...v0.31.0) (2021-12-28) + +### Features + +- New hooks for element size tracking ([#413](https://github.com/RocketChat/fuselage/issues/413)) ([8ca682c](https://github.com/RocketChat/fuselage/commit/8ca682c636d2e4813f7d346cb881513382be63cf)) + +# [0.30.0](https://github.com/RocketChat/fuselage/compare/v0.29.0...v0.30.0) (2021-10-06) + +### Bug Fixes + +- **jest:** Adjust jest and ts-jest dependencies ([#547](https://github.com/RocketChat/fuselage/issues/547)) ([91a4fa1](https://github.com/RocketChat/fuselage/commit/91a4fa1365394001afe1bd46480bda3bafed5505)) +- **message-parser:** ([#546](https://github.com/RocketChat/fuselage/issues/546)) ([faca16f](https://github.com/RocketChat/fuselage/commit/faca16febe517e411dd377cae294f888f1199d40)) +- **message-parser:** Fix Url and Escaped Markdown ([#537](https://github.com/RocketChat/fuselage/issues/537)) ([bc0cbce](https://github.com/RocketChat/fuselage/commit/bc0cbce69589b9a056d797a03b78d7cd06423aaa)) + +# [0.29.0](https://github.com/RocketChat/fuselage/compare/v0.28.0...v0.29.0) (2021-08-31) + +**Note:** Version bump only for package @rocket.chat/message-parser + +# [0.28.0](https://github.com/RocketChat/fuselage/compare/v0.27.0...v0.28.0) (2021-07-30) + +### Features + +- **onboarding-ui:** Administrator information form and Organization information form ([#489](https://github.com/RocketChat/fuselage/issues/489)) ([b289f68](https://github.com/RocketChat/fuselage/commit/b289f68676954b91c792d8d97680314178bf2c60)) +- styled API; monorepo grooming ([#482](https://github.com/RocketChat/fuselage/issues/482)) ([1b6b70c](https://github.com/RocketChat/fuselage/commit/1b6b70cf67ec16927b1566adc2350295a8927223)) + +# [0.27.0](https://github.com/RocketChat/fuselage/compare/v0.26.0...v0.27.0) (2021-06-28) + +### Bug Fixes + +- **eslint:** Add missing ESLint rule for TypeScript ([#470](https://github.com/RocketChat/fuselage/issues/470)) ([cc0d498](https://github.com/RocketChat/fuselage/commit/cc0d4989bf37f7602d1d58d051824f1dd6c096b3)) + +# [0.26.0](https://github.com/RocketChat/fuselage/compare/v0.25.0...v0.26.0) (2021-05-28) + +**Note:** Version bump only for package @rocket.chat/message-parser + +# [0.25.0](https://github.com/RocketChat/fuselage/compare/v0.24.0...v0.25.0) (2021-05-19) + +### Bug Fixes + +- **fuselage:** fix duplicated values on paginated multi select ([#456](https://github.com/RocketChat/fuselage/issues/456)) ([4518a4e](https://github.com/RocketChat/fuselage/commit/4518a4e661cb525d957f6140d59a641a50fc7b20)) +- **message-parser:** Big emoji ([#451](https://github.com/RocketChat/fuselage/issues/451)) ([6d65343](https://github.com/RocketChat/fuselage/commit/6d653433d07edabaee821bd25ad07a5878b59a86)) +- **message-parser:** URL issues ([#448](https://github.com/RocketChat/fuselage/issues/448)) ([8ce6b91](https://github.com/RocketChat/fuselage/commit/8ce6b9110547b5adf3633e87d6bc655114d4cfb4)) +- message-parser Unordered List definition ([#445](https://github.com/RocketChat/fuselage/issues/445)) ([6c659b8](https://github.com/RocketChat/fuselage/commit/6c659b821fd6294eb8033dfe03e42db2dba1ca06)) + +### Features + +- [@rocket](https://github.com/rocket).chat/message-parser ([#443](https://github.com/RocketChat/fuselage/issues/443)) ([4722cdf](https://github.com/RocketChat/fuselage/commit/4722cdff46f5987f335d989be59649c7652bb12a)) +- Peggy loader ([#450](https://github.com/RocketChat/fuselage/issues/450)) ([0496cad](https://github.com/RocketChat/fuselage/commit/0496cad457d76f8a4d6a217209e4a55e315e8365)) diff --git a/packages/message-parser/README.md b/packages/message-parser/README.md new file mode 100644 index 000000000000..8d2d8f4056e3 --- /dev/null +++ b/packages/message-parser/README.md @@ -0,0 +1,57 @@ + + +

          + + Rocket.Chat + +

          + +# `@rocket.chat/message-parser` + +> Rocket.Chat parser for messages + +--- + +[![npm@latest](https://img.shields.io/npm/v/@rocket.chat/message-parser/latest?style=flat-square)](https://www.npmjs.com/package/@rocket.chat/message-parser/v/latest) [![npm@next](https://img.shields.io/npm/v/@rocket.chat/message-parser/next?style=flat-square)](https://www.npmjs.com/package/@rocket.chat/message-parser/v/next) ![npm downloads](https://img.shields.io/npm/dw/@rocket.chat/message-parser?style=flat-square) ![License: MIT](https://img.shields.io/npm/l/@rocket.chat/message-parser?style=flat-square) + +![deps](https://img.shields.io/librariesio/release/npm/@rocket.chat/message-parser?style=flat-square) ![npm bundle size](https://img.shields.io/bundlephobia/min/@rocket.chat/message-parser?style=flat-square) + + + +## Description + +Rocket.Chat grammar with the purpose of parsing the messages of the rocket chat, converting text to an AST tree. + +The grammar provides support for markdown, mentions and emojis. + +## Supported markup + +- quotes +- bold/italic/strike +- ordered lists +- unordered lists +- task lists +- phone numbers +- mentions +- emoji +- colors +- URI's +- mentions users/channels + +## Contributing + + + +Contributions, issues, and feature requests are welcome!
          +Feel free to check the [issues](https://github.com/RocketChat/fuselage/issues). + + + +Whenever you find a grammar-related bug, start by inserting the test case. + +We are open to other tags/markups, as long as they don't generate unexpected behavior. + +## Observations and known issues + +- Nested lists are unsupported +- `URL` rule doesn't allow whitespace, `(`, or `)` diff --git a/packages/message-parser/babel.config.js b/packages/message-parser/babel.config.js new file mode 100644 index 000000000000..8456c191273d --- /dev/null +++ b/packages/message-parser/babel.config.js @@ -0,0 +1,18 @@ +module.exports = { + presets: [ + [ + '@babel/preset-env', + { + targets: { + browsers: [ + 'Chrome >= 59', + 'FireFox >= 44', + 'Safari >= 7', + 'Explorer 11', + 'last 4 Edge versions', + ], + }, + }, + ], + ], +}; diff --git a/packages/message-parser/jest.config.js b/packages/message-parser/jest.config.js new file mode 100644 index 000000000000..114260f819d9 --- /dev/null +++ b/packages/message-parser/jest.config.js @@ -0,0 +1,12 @@ +const path = require('path'); + +module.exports = { + coverageReporters: [], + transform: { + '\\.pegjs$': path.resolve(__dirname, './loaders/pegtransform.js'), + }, + preset: 'ts-jest', + errorOnDeprecated: true, + testMatch: ['/tests/**/*.(spec|test).ts'], + moduleFileExtensions: ['js', 'ts', 'pegjs'], +}; diff --git a/packages/message-parser/loaders/pegtransform.js b/packages/message-parser/loaders/pegtransform.js new file mode 100644 index 000000000000..9df49c61dfea --- /dev/null +++ b/packages/message-parser/loaders/pegtransform.js @@ -0,0 +1,10 @@ +const pegjs = require('peggy'); + +module.exports = { + process: (content) => ({ + code: pegjs.generate(content, { + output: 'source', + format: 'commonjs', + }), + }), +}; diff --git a/packages/message-parser/messageParser.js b/packages/message-parser/messageParser.js new file mode 100644 index 000000000000..c1dc1bda949a --- /dev/null +++ b/packages/message-parser/messageParser.js @@ -0,0 +1,7 @@ +/* eslint-disable import/no-unresolved */ + +if (process.env.NODE_ENV === 'production') { + module.exports = require('./dist/messageParser.production.js'); +} else { + module.exports = require('./dist/messageParser.development.js'); +} diff --git a/packages/message-parser/package.json b/packages/message-parser/package.json new file mode 100644 index 000000000000..5a4ff3ba8ba2 --- /dev/null +++ b/packages/message-parser/package.json @@ -0,0 +1,78 @@ +{ + "name": "@rocket.chat/message-parser", + "description": "Rocket.Chat parser for messages", + "version": "0.31.28", + "author": { + "name": "Rocket.Chat", + "url": "https://rocket.chat/" + }, + "license": "MIT", + "homepage": "https://github.com/RocketChat/fuselage#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/RocketChat/fuselage.git", + "directory": "packages/message-parser" + }, + "bugs": { + "url": "https://github.com/RocketChat/fuselage/issues" + }, + "main": "messageParser.js", + "exports": { + ".": { + "default": "./messageParser.js" + }, + "./index": { + "default": "./messageParser.js" + } + }, + "module": "dist/messageParser.mjs", + "unpkg": "dist/messageParser.umd.js", + "types": "dist/index.d.ts", + "files": [ + "/dist", + "/messageParser.js" + ], + "directories": { + "test": "test" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "run-s .:build:clean .:build:bundle", + ".:build:clean": "rimraf dist", + ".:build:bundle": "webpack", + "test": "jest --runInBand --coverage", + "watch": "jest --watch", + "lint": "eslint src", + "docs": "typedoc" + }, + "devDependencies": { + "@babel/core": "~7.21.4", + "@babel/eslint-parser": "~7.21.3", + "@babel/preset-env": "~7.21.4", + "@rocket.chat/eslint-config": "workspace:^", + "@rocket.chat/peggy-loader": "workspace:~", + "@rocket.chat/prettier-config": "~0.31.25", + "@types/jest": "~29.5.7", + "@types/node": "~14.18.42", + "@typescript-eslint/parser": "~5.58.0", + "babel-loader": "~9.1.2", + "eslint": "~8.45.0", + "jest": "~29.6.4", + "npm-run-all": "^4.1.5", + "peggy": "3.0.2", + "prettier": "~2.8.7", + "prettier-plugin-pegjs": "~0.5.4", + "rimraf": "^3.0.2", + "ts-jest": "~29.1.0", + "ts-loader": "~9.4.2", + "typedoc": "~0.24.1", + "typescript": "~5.0.4", + "webpack": "~5.78.0", + "webpack-cli": "~5.0.1" + }, + "dependencies": { + "tldts": "~5.7.112" + } +} diff --git a/packages/message-parser/src/definitions.ts b/packages/message-parser/src/definitions.ts new file mode 100644 index 000000000000..1db89bd24110 --- /dev/null +++ b/packages/message-parser/src/definitions.ts @@ -0,0 +1,249 @@ +export type Blockquote = { + type: 'BLOCKQUOTE'; + value: Paragraph[]; +}; + +export type OrderedList = { + type: 'ORDERED_LIST'; + value: ListItem[]; +}; + +export type UnorderedList = { + type: 'UNORDERED_LIST'; + value: ListItem[]; +}; + +export type ListItem = { + type: 'LIST_ITEM'; + value: Inlines[]; + number?: number; +}; + +export type Tasks = { + type: 'TASKS'; + value: Task[]; +}; +export type Task = { + type: 'TASK'; + status: boolean; + value: Inlines[]; +}; + +export type CodeLine = { + type: 'CODE_LINE'; + value: Plain; +}; + +export type Color = { + type: 'COLOR'; + value: { + r: number; + g: number; + b: number; + a: number; + }; +}; + +export type BigEmoji = { + type: 'BIG_EMOJI'; + value: [Emoji] | [Emoji, Emoji] | [Emoji, Emoji, Emoji]; +}; + +export type Emoji = + | { + type: 'EMOJI'; + value: Plain; + shortCode: string; + } + | { + type: 'EMOJI'; + value: undefined; + unicode: string; + }; + +export type Code = { + type: 'CODE'; + language: string | undefined; + value: CodeLine[]; +}; + +export type InlineCode = { + type: 'INLINE_CODE'; + value: Plain; +}; + +export type Heading = { + type: 'HEADING'; + level: 1 | 2 | 3 | 4; + value: Plain[]; +}; + +export type Quote = { + type: 'QUOTE'; + value: Paragraph[]; +}; + +export type Markup = Italic | Strike | Bold | Plain | ChannelMention; +export type MarkupExcluding = Exclude; + +export type Bold = { + type: 'BOLD'; + value: Array< + | MarkupExcluding + | Link + | Emoji + | UserMention + | ChannelMention + | InlineCode + >; +}; + +export type Italic = { + type: 'ITALIC'; + value: Array< + | MarkupExcluding + | Link + | Emoji + | UserMention + | ChannelMention + | InlineCode + >; +}; + +export type Strike = { + type: 'STRIKE'; + value: Array< + | MarkupExcluding + | Link + | Emoji + | UserMention + | ChannelMention + | InlineCode + | Italic + >; +}; + +export type Plain = { + type: 'PLAIN_TEXT'; + value: string; +}; + +export type LineBreak = { + type: 'LINE_BREAK'; + value: undefined; +}; + +export type KaTeX = { + type: 'KATEX'; + value: string; +}; + +export type InlineKaTeX = { + type: 'INLINE_KATEX'; + value: string; +}; + +export type Paragraph = { + type: 'PARAGRAPH'; + value: Array>; +}; + +export type Image = { + type: 'IMAGE'; + value: { + src: Plain; + label: Markup; + }; +}; + +export type Link = { + type: 'LINK'; + value: { + src: Plain; + label: Markup | Markup[]; + }; +}; + +export type UserMention = { + type: 'MENTION_USER'; + value: Plain; +}; + +export type ChannelMention = { + type: 'MENTION_CHANNEL'; + value: Plain; +}; + +export type Types = { + BOLD: Bold; + PARAGRAPH: Paragraph; + PLAIN_TEXT: Plain; + ITALIC: Italic; + STRIKE: Strike; + CODE: Code; + CODE_LINE: CodeLine; + INLINE_CODE: InlineCode; + HEADING: Heading; + QUOTE: Quote; + LINK: Link; + MENTION_USER: UserMention; + MENTION_CHANNEL: ChannelMention; + EMOJI: Emoji; + BIG_EMOJI: BigEmoji; + COLOR: Color; + TASKS: Tasks; + TASK: Task; + UNORDERED_LIST: UnorderedList; + ORDERED_LIST: OrderedList; + LIST_ITEM: ListItem; + IMAGE: Image; + LINE_BREAK: LineBreak; +}; + +export type ASTNode = + | BigEmoji + | Bold + | Paragraph + | Plain + | Italic + | Strike + | Code + | CodeLine + | InlineCode + | Heading + | Quote + | Link + | UserMention + | ChannelMention + | Emoji + | Color + | Tasks; + +export type TypesKeys = keyof Types; + +export type Inlines = + | Bold + | Plain + | Italic + | Strike + | InlineCode + | Image + | Link + | UserMention + | ChannelMention + | Emoji + | Color + | InlineKaTeX; + +export type Blocks = + | Code + | Heading + | Quote + | ListItem + | Tasks + | OrderedList + | UnorderedList + | LineBreak + | KaTeX; + +export type Root = Array | [BigEmoji]; diff --git a/packages/message-parser/src/grammar.pegjs b/packages/message-parser/src/grammar.pegjs new file mode 100644 index 000000000000..3445f8e18231 --- /dev/null +++ b/packages/message-parser/src/grammar.pegjs @@ -0,0 +1,705 @@ +{{ + const { + autoEmail, + autoLink, + bigEmoji, + bold, + code, + codeLine, + color, + emoji, + emojiUnicode, + emoticon, + heading, + image, + inlineCode, + inlineKatex, + italic, + katex, + lineBreak, + link, + listItem, + mentionChannel, + mentionUser, + orderedList, + paragraph, + phoneChecker, + plain, + quote, + reducePlainTexts, + strike, + task, + tasks, + unorderedList, + } = require('./utils'); +}} + +Start + = @BigEmoji !. + / (Blocks / Paragraph / EndOfLine { return paragraph([plain('')]); })+ + +/** + * + * Blocks + * + */ +Blocks + = Blockquote + / Code + / Heading + / Tasks + / OrderedList + / UnorderedList + / Katex + / LineBreak + +/** + * + * Blockquote + * e.g: > This is a blockquote + * + */ +Blockquote = b:BlockquoteLine+ { return quote(b); } + +BlockquoteLine = ">" [ \t]* @Paragraph + +/** + * + * Code Chunk + * e.g: + * ```js + * console.log('hello world'); + * ``` + */ +Code = "```" language:CodeLanguage? EndOfLine lines:CodeLine+ EndOfLine "```" { return code(lines, language); } + +CodeLanguage = $[a-zA-Z0-9 \_\-.]+ + +CodeLine + = chunk:CodeChunk { return codeLine(chunk); } + / "\n" chunk:CodeChunk { return codeLine(chunk); } + / "\n" !"```" { return codeLine(plain('')); } + +CodeChunk = text:$(!EndOfLine !"```" .)+ { return plain(text); } + +/** + * + * Heading: h1, h2, h3, h4 + * e.g: + * # Heading 1 + * ## Heading 2 + * ### Heading 3 + * #### Heading 4 + * +*/ +Heading = count:HeadingStart [ \t]+ text:HeadingChunk { return heading([text], count); } + +HeadingStart = value:"#" |1..4| { return value.length; } + +HeadingChunk = text:$(!EndOfLine .)+ { return plain(text); } + +/** + * + * Tasks + * e.g: + * - [x] Task One (checked) + * - [ ] Task two + * - [x] Task three (checked) + * + */ +Tasks = items:Task+ { return tasks(items); } + +Task = "- [" flag:TaskFlag "]" [ \t]+ text:Inline { return task(text, flag); } + +TaskFlag = "x" { return true; } / " " { return false; } + +/** + * + * Ordered List + * e.g: + * 1. Item One + * 2. Item Two + * 3. Item Three + * + */ +OrderedList = items:OrderedListItem+ { return orderedList(items); } + +OrderedListItem = number:Digits "." [ \t]+ text:Inline { return listItem(text, parseInt(number, 10)); } + +/** + * + * Unordered List + * e.g: + * - Item One + * - Item Two + * * Item Three + * * Item Four + * + */ +UnorderedList = items:(UnorderedListHyphenItem+ / UnorderedListAsteriskItem+) { return unorderedList(items); } + +UnorderedListHyphenItem = "-" [ \t]+ text:Inline { return listItem(text); } + +UnorderedListAsteriskItem = "*" [ \t]+ text:UnorderedListItemContent { return listItem(text); } + +UnorderedListItemContent = value:UnorderedListItemContentItem+ !"*" EndOfLine? { return reducePlainTexts(value); } + +UnorderedListItemContentItem = InlineItem / !"*" @Any + +/** + * + * KaTex + * e.g: \[ KATEX_HERE \] OR $$ KATEX_HERE $$ + * $$x = \begin{cases} + * a &\text{if } b \\ + * c &\text{if } d + * \end{cases}$$ + * + */ +Katex = KatexStart content:$(!KatexEnd .)* KatexEnd { return katex(content); } + +KatexStart + = & { return options.katex?.parenthesisSyntax; } "\\[" + / & { return options.katex?.dollarSyntax; } "$$" + +KatexEnd + = & { return options.katex?.parenthesisSyntax; } "\\]" + / & { return options.katex?.dollarSyntax; } "$$" + +KatexInline + = KatexInlineStart content:$(!KatexInlineEnd .)* KatexInlineEnd { + return inlineKatex(content); + } + +KatexInlineStart + = & { return options.katex?.parenthesisSyntax; } "\\(" + / & { return options.katex?.dollarSyntax; } "$" + +KatexInlineEnd + = & { return options.katex?.parenthesisSyntax; } "\\)" + / & { return options.katex?.dollarSyntax; } "$" + +/** + * + * LineBreak + * e.g: \n + * +*/ +LineBreak = Space* EndOfLine { return lineBreak(); } + +/** + * + * Paragraph + * e.g: This is a paragraph +*/ +Paragraph = value:Inline { return paragraph(value); } + +/** + * + * Inline + * +*/ +Inline = value:(InlineItem / Any)+ EndOfLine? { return reducePlainTexts(value); } + +InlineItem = Whitespace + / References + / AutolinkedPhone + / AutolinkedEmail + / AutolinkedURL + / EmphasisWithWhitespace + / Emphasis + / UserMention + / ChannelMention + / Emoji + / InlineCode + / Image + / Emoticon + / Color + / KatexInline + / Escaped + +/** + * + * URL + * e.g: + * Reference: [Rocket.Chat Website](https://rocket.chat), [](https://rocket.chat), + * Image: ![](https://rocket.chat/logo.png) + * + */ +References + = "[" title:LinkTitle* "](" href:LinkRef ")" { return title.length ? link(href, reducePlainTexts(title)) : link(href); } + / "<" href:LinkRef "|" title:LinkTitle2 ">" { return link(href, [plain(title)]); } + +LinkTitle = (Whitespace / EmphasisForReferences) / anyTitle:$(!("](" .) .) { return plain(anyTitle) } + +LinkTitle2 = $([\x20-\x3B\x3D\x3F-\x60\x61-\x7B\x7D-\xFF] / NonASCII)+ + +LinkRef = URL / FilePath / p:Phone { return 'tel:' + p.number; } // TODO: Accept parenthesis + +FilePath = $(URLScheme URLBody+) + +Image = "![" title:Line? "](" href:LinkRef ")" { return title ? image(href, title) : image(href); } + +URL + = $(URLScheme URLAuthority URLBody*) + / $(URLAuthorityHost URLBody*) + +URLScheme = $([A-Za-z0-9+-] |1..32| ":") + +URLBody + = ( + !(Extra+ (Whitespace / EndOfLine) / Whitespace) + (AnyText / [*\[\/\]\^_`{}~(]) + )+ + +URLAuthority = $("//" URLAuthorityUserInfo? URLAuthorityHost) + +URLAuthorityUserInfo = $(URLAuthorityUser (":" URLAuthorityPassword)? "@") + +URLAuthorityUser = $(AlphaDigit / ![@/] Safe)+ + +URLAuthorityPassword = $(AlphaDigit / ![@/] Safe)+ + +URLAuthorityHost = URLAuthorityHostName (":" URLAuthorityPort)? + +URLAuthorityHostName + = DomainName + / $(Digits |4, "."|) // TODO: IPv4 and IPv6 + +URLAuthorityPort + = Digits // TODO: from "0" to "65535" + +DomainName + = "localhost" + / $(DomainNameLabel ("." DomainChar DomainNameLabel*)+) + +DomainNameLabel = $(DomainChar+ ("-" DomainChar+)*) + +DomainChar = !Extra ([\__-] / !Safe) !EndOfLine !Space ![\\/|><%`] . + +/** + * + * Phone + * e.g: 075-63546725 + * + */ +Phone = "+" p:PhoneNumber { return { text: '+' + p.text, number: p.number }; } + +PhoneNumber + = p:PhonePrefix "-" d:Digits { + return { text: p.text + '-' + d, number: p.number + d }; + } + / p:PhonePrefix d1:Digits "-" d2:Digits { + return { text: p.text + d1 + '-' + d2, number: p.number + d1 + d2 }; + } + / p:PhonePrefix d:Digits { + return { text: p.text + d, number: p.number + d }; + } + / d:Digits { return { text: d, number: d }; } + +PhonePrefix + = d:Digits { return { text: d, number: d }; } + / "(" d:Digits ")" { return { text: '(' + d + ')', number: d }; } + +AutolinkedPhone = p:Phone { return phoneChecker(p.text, p.number); } + +/** + * + * Email + * e.g: contact@rocket.chat + * + */ +Email = "mailto:"? @$(LocalPart "@" DomainName) + +LocalPart = $(LocalPartChar+ ("." LocalPartChar+)*) + +LocalPartChar = AlphaNumericOrMarkChar+ LocalPartSpecialChars* + +LocalPartSpecialChars = [!#$%&'*+/=?^_\`{|}~-] + +AutolinkedEmail = e:Email { return autoEmail(e); } + +/** + * + * Auto Link URL + * e.g: rocket.chat, https://rocket.chat, + * with customDomains options as intranet: protocol://internaltool.intranet + * + */ +AutolinkedURL = u:AutoLinkURL { return autoLink(u, options.customDomains); } + +AutoLinkURL + = $(URLScheme URLAuthority AutoLinkURLBody*) + / $(URLAuthorityHost AutoLinkURLBody*) + +AutoLinkURLBody = !(Extra* (Whitespace / EndOfLine)) . + +/** + * + * Emphasis + * +*/ +Emphasis = Bold / Italic / Strikethrough + +/** + * + * Emphasis for References + * +*/ +EmphasisForReferences = BoldForReferences / ItalicForReferences / StrikethroughForReferences + +/** + * + * Italic, Bold and Strike + * e.g: __italic__, _italic_, **bold**, __*bold italic*__, ~~strikethrough~~ + * + */ + +/* Italic */ +Italic + = value:$([a-zA-Z0-9]+ [\x5F] [\x5F]?) { return plain(value); } + / [\x5F] [\x5F] i:ItalicContentItems [\x5F] [\x5F] t:$[a-zA-Z0-9]+ { + return reducePlainTexts([plain('__'), ...i, plain('__'), plain(t)])[0]; + } + / [\x5F] i:ItalicContentItems [\x5F] t:$[a-zA-Z]+ { + return reducePlainTexts([plain('_'), ...i, plain('_'), plain(t)])[0]; + } + / [\x5F] [\x5F] @ItalicContent [\x5F] [\x5F] + / [\x5F] @ItalicContent [\x5F] + +ItalicContent = text:ItalicContentItems { return italic(text); } + +ItalicContentItems = text:ItalicContentItem+ { return reducePlainTexts(text); } + +ItalicContentItem + = Whitespace + / InlineCode + / References + / UserMention + / ChannelMention + / Bold + / Strikethrough + / Emoji + / Emoticon + / AnyItalic + / Line + +/* Bold */ +Bold = [\x2A] [\x2A] @BoldContent [\x2A] [\x2A] / [\x2A] @BoldContent [\x2A] + +BoldContent = text:BoldContentItem+ { return bold(reducePlainTexts(text)); } + +BoldContentItem = Whitespace / InlineCode / References / UserMention / ChannelMention / Italic / Strikethrough / Emoji / Emoticon / AnyBold / Line + +/* Strike */ +Strikethrough = [\x7E] [\x7E] @StrikethroughContent [\x7E] [\x7E] / [\x7E] @StrikethroughContent [\x7E] + +StrikethroughContent = text:(InlineCode / Whitespace / References / UserMention / ChannelMention / Italic / Bold / Emoji / Emoticon / AnyStrike / Line)+ { + return strike(reducePlainTexts(text)); + } + +/* Italic for References */ +ItalicForReferences + = value:$([a-zA-Z0-9]+ [\x5F] [\x5F]?) { return plain(value); } + / [\x5F] [\x5F] i:ItalicContentItemsForReferences [\x5F] [\x5F] t:$[a-zA-Z0-9]+ { + return reducePlainTexts([plain('__'), ...i, plain('__'), plain(t)])[0]; + } + / [\x5F] i:ItalicContentItemsForReferences [\x5F] t:$[a-zA-Z]+ { + return reducePlainTexts([plain('_'), ...i, plain('_'), plain(t)])[0]; + } + / [\x5F] [\x5F] @ItalicContentForReferences [\x5F] [\x5F] + / [\x5F] @ItalicContentForReferences [\x5F] + +ItalicContentForReferences = text:ItalicContentItemsForReferences { return italic(text); } + +ItalicContentItemsForReferences = text:ItalicContentItemForReferences+ { return reducePlainTexts(text); } + +ItalicContentItemForReferences + = Whitespace + / UserMention + / ChannelMention + / BoldForReferences + / StrikethroughForReferences + / Emoji + / Emoticon + / AnyItalic + / Line + / InlineCode + +/* Bold for References */ +BoldForReferences = [\x2A] [\x2A] @BoldContentForReferences [\x2A] [\x2A] / [\x2A] @BoldContentForReferences [\x2A] + +BoldContentForReferences = text:(Whitespace / UserMention / ChannelMention / ItalicForReferences / StrikethroughForReferences / Emoji / Emoticon / AnyBold / Line / InlineCode)+ { return bold(reducePlainTexts(text)); } + +/* Strike for References */ +StrikethroughForReferences = [\x7E] [\x7E] @StrikethroughContentForReferences [\x7E] [\x7E] / [\x7E] @StrikethroughContentForReferences [\x7E] + +StrikethroughContentForReferences = text:(Whitespace / UserMention / ChannelMention / ItalicForReferences / BoldForReferences / Emoji / Emoticon / AnyStrike / Line / InlineCode)+ { + return strike(reducePlainTexts(text)); + } + +AnyBold = t:[^\x0a\* ] { return plain(t); } + +AnyStrike = t:[^\x0a\~ ] { return plain(t); } + +AnyItalic = t:[^\x0a\_ ] { return plain(t); } + +/** + * Emphasis with only whitespaces return plain text + * e.g: __ __, _ _, ** **, * *, ** *, ~~ ~~ +*/ +EmphasisWithWhitespace = AsteriskWithWhitespace / UnderscoreWithWhitespace / TildeWithWhitespace + +AsteriskWithWhitespace = first:Asterisk second:Whitespace third:Asterisk +{ + return reducePlainTexts([first,second,third])[0]; +} + +UnderscoreWithWhitespace = first:Underscore second:Whitespace third:Underscore +{ + return reducePlainTexts([first,second,third])[0]; +} + +TildeWithWhitespace = first:Tilde second:Whitespace third:Tilde +{ + return reducePlainTexts([first,second,third])[0]; +} + +Asterisk = t:"*"+ {return plain(t.join(""))} +Underscore = t:"_"+ {return plain(t.join(""))} +Tilde = t:"~"+ {return plain(t.join(""))} + +/** + * + * Mentions + * e.g: @user, #channel + * +*/ +UserMention + = t:Text "@"+ user:AlphaNumericChar { + return reducePlainTexts([t, plain('@' + user)])[0]; + } + / "@"+ user:$(UTF8NamesValidation ([:@] UTF8NamesValidation)?) { + return mentionUser(user); + } + +ChannelMention + = t:Text "#" channel:AlphaNumericChar { + return reducePlainTexts([t, plain('#' + channel)])[0]; + } + / "#" channel:UTF8NamesValidation { return mentionChannel(channel); } + +/** + * + * EMOJIS + * e.g: + * BigEmoji (min: 1 | max: 3 without text) and ShortCode :smile: :+1: :heart: + * Unicode: 😀 🚀 🌈 + * Emoticon: :D :P :( :/ + * + */ +BigEmoji = (EndOfLine / Space)* es:(@(Emoji / Emoticon) (EndOfLine / Space)*) |1..3| { return [bigEmoji(es)]; } + +Emoji = EmojiShortCode / ch:UnicodeEmoji { return emojiUnicode(ch); } + +EmojiShortCode = ":" shortCode:EmojiShortCodeName ":" { return emoji(shortCode); } + +EmojiShortCodeName = $[0-9a-zA-Z-_+.]+ + +/* Emoticons */ +Emoticon = & { return options.emoticons; } @EmoticonPattern + +EmoticonPattern + = e:$"<3" { return emoticon(e, 'heart'); } + / e:$":)" / ">;)" / ">:-)" / ">=)") { return emoticon(e, 'laughing'); } + / e:$("':)" / "':-)" / "'=)" / "':D" / "':-D" / "'=D") { + return emoticon(e, 'sweat_smile'); + } + / e:$(":')" / ":'-)") { return emoticon(e, 'joy'); } + / e:$( + "O:-)" + / "0:-3" + / "0:3" + / "0:-)" + / "0:)" + / "0;^)" + / "O:)" + / "O;-)" + / "O=)" + / "0;-)" + / "O:-3" + / "O:3" + ) { return emoticon(e, 'innocent'); } + / e:$(":)" / ":-)" / "=]" / "=)" / ":]") { + return emoticon(e, 'slight_smile'); + } + / e:$(";)" / ";-)" / "*-)" / "*)" / ";-]" / ";]" / ";D" / ";^)") { + return emoticon(e, 'wink'); + } + / e:$(":*" / ":-*" / "=*" / ":^*") { return emoticon(e, 'kissing_heart'); } + / e:$(":P" / ":-P" / "=P" / ":-\u00de" / ":\u00de" / ":-b" / ":b") { + return emoticon(e, 'stuck_out_tongue'); + } + / e:$(">:P" / "X-P") { return emoticon(e, 'stuck_out_tongue_winking_eye'); } + / e:$("B-)" / "B)" / "8)" / "8-)" / "B-D" / "8-D") { + return emoticon(e, 'sunglasses'); + } + / e:$(">:[" / ":-(" / ":(" / ":-[" / ":[" / "=(") { + return emoticon(e, 'disappointed'); + } + / e:$( + ">:\\" + / ">:\/" + / ":-\/" + / ":-." + / ":\/" + / ":\\" + / "=\/" + / "=\\" + / ":L" + / "=L" + ) { return emoticon(e, 'confused'); } + / e:$">.<" { return emoticon(e, 'persevere'); } + / e:$(":'(" / ":'-(" / ";(" / ";-(") { return emoticon(e, 'cry'); } + / e:$(">:(" / ">:-(" / ":@") { return emoticon(e, 'angry'); } + / e:$(":$" / "=$") { return emoticon(e, 'flushed'); } + / e:$"D:" { return emoticon(e, 'fearful'); } + / e:$("':(" / "':-(" / "'=(") { return emoticon(e, 'sweat'); } + / e:$(":-X" / ":X" / ":-#" / ":#" / "=X" / "=#") { + return emoticon(e, 'no_mouth'); + } + / e:$("-_-" / "-__-" / "-___-") { return emoticon(e, 'expressionless'); } + / e:$(":-O" / ":O" / "O_O" / ">:O") { return emoticon(e, 'open_mouth'); } + / e:$("#-)" / "#)" / "%-)" / "%)" / "X)" / "X-)") { + return emoticon(e, 'dizzy_face'); + } + / e:$"(y)" { return emoticon(e, 'thumbsup'); } + / e:$("*\\0\/*" / "\\0\/" / "*\\O\/*" / "\\O\/") { + return emoticon(e, 'person_gesturing_ok'); + } + +/* Unicode emojis */ +UnicodeEmoji + = UnicodeEmojiEmoticon + / $( + UnicodeEmojiSupplementalSymbolsAndPictographs + ( + UnicodeEmojiMiscellaneousSymbolsAndPictographs + ([\u200D] UnicodeEmojiMiscellaneousSymbolsAndPictographs)* + )? + ) + / $( + ( + UnicodeEmojiMiscellaneousSymbolsAndPictographs + UnicodeEmojiMiscellaneousSymbolsAndPictographsFitzpatrickModifiers? + [\u200D] + )* + UnicodeEmojiMiscellaneousSymbolsAndPictographs + UnicodeEmojiMiscellaneousSymbolsAndPictographsFitzpatrickModifiers? + ) + / UnicodeEmojiTransportAndMapSymbols + / UnicodeEmojiMiscellaneousTechnical + / UnicodeEmojiMiscellaneousSymbols + / UnicodeEmojiDingbats + / UnicodeEmojiFlags + +UnicodeEmojiEmoticon = $([\uD83D] [\uDE00-\uDE4F]) + +UnicodeEmojiSupplementalSymbolsAndPictographs = $([\uD83E] [\uDD00-\uDDFF]) + +UnicodeEmojiMiscellaneousSymbolsAndPictographs = $([\uD83C] [\uDF00-\uDFFF] [\uFE00-\uFE0F]?) / $([\uD83D] [\uDC00-\uDDFF] [\uFE00-\uFE0F]?) + +UnicodeEmojiMiscellaneousSymbolsAndPictographsFitzpatrickModifiers = $([\uD83C] [\uDFFB-\uDFFF]) + +UnicodeEmojiTransportAndMapSymbols = $([\uD83D] [\uDE80-\uDEFA]) + +UnicodeEmojiMiscellaneousTechnical = $([\u2300-\u23FF] [\uFE00-\uFE0F]?) + +UnicodeEmojiMiscellaneousSymbols = $([\u2600-\u26FF] [\uFE00-\uFE0F]?) + +UnicodeEmojiDingbats = $([\u2700-\u27BF] [\uFE00-\uFE0F]?) + +UnicodeEmojiFlags = $([\uD83C] [\uDD00-\uDDFF] [\uD83C] [\uDD00-\uDDFF]) + +/** + * + * Inline Code + * e.g: `console.log('hello world')` + * + */ +InlineCode = "`" text:$InlineCode__+ "`" { return inlineCode(plain(text)); } + +InlineCode__ = $(!"`" !"\n" .) + +/** + * + * Colors + * e.g: color:#ff0000 , color:#ff0 + * + */ +Color = & { return options.colors; } "color:#" rgba:ColorRGBATuple !AnyText { + return color(...rgba); + } + +ColorRGBATuple = HexByte|3..4| / HexNible|3..4| + +/** + * + * Macros + * + */ +Whitespace = w:$Space+ { return plain(w); } + +EndOfLine = "\r\n" / "\n" / "\r" + +Space = " " / "\t" + +Escaped = "\\" t:[*_~`#.] { return plain(t); } + +Any = !EndOfLine t:. p:$AutolinkedPhone? u:$URL? { return plain(t + p + u); } + +AnyText = [\x20-\x27\x2B-\x40\x41-\x5A\x61-\x7A] / NonASCII + +Text = text:AnyText { return plain(text); } + +Line = t:LineStructure { return plain(t); } + +LineStructure = head:$Space* text:$AnyText+ tail:$Space* { return head + text + tail; } + +UTF8NamesValidation = $([-_.] / AlphaNumericChar)+ + +NonASCII = [\x80-\uFFFF] + +Unicode = "\\" Digits:$(HexDigit |1..6|) ("\r\n" / [ \t\r\n\f])? { return String.fromCharCode(parseInt(Digits, 16)); } + +Digit = [0-9] + +Digits = $Digit+ + +Safe = [$@&+\__#?-] + +Extra = [.,!%~*\"':;()=~] + +HexDigit = [0-9A-Fa-f] + +HexNible = a:HexDigit { return parseInt(a + a, 16); } + +HexByte = a:HexDigit b:HexDigit { return parseInt(a + b, 16); } + +AlphaDigit = [a-zA-Z0-9] + +AlphaNumericOrMarkChar = AlphaOrMarkChar / DecimalNumberChar + +AlphaOrMarkChar = AlphaChar / EmojiChar / MarkChar + +AlphaNumericChar = AlphaChar / DecimalNumberChar + +AlphaChar = [A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC] + +DecimalNumberChar = [0-9\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0BE6-\u0BEF\u0C66-\u0C6F\u0CE6-\u0CEF\u0D66-\u0D6F\u0DE6-\u0DEF\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F29\u1040-\u1049\u1090-\u1099\u17E0-\u17E9\u1810-\u1819\u1946-\u194F\u19D0-\u19D9\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\uA620-\uA629\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uA9F0-\uA9F9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19] + +EmojiChar = [\u2700-\u27bf\udde6-\uddff\ud800-\udbff\udc00-\udfff\ufe0e\ufe0f\u0300-\u036f\ufe20-\ufe23\u20d0-\u20f0\ud83c\udffb-\udfff\u200d\u3299\u3297\u303d\u3030\u24c2\ud83c\udd70-\udd71\udd7e-\udd7f\udd8e\udd91-\udd9a\udde6-\uddff\ude01-\ude02\ude1a\ude2f\ude32-\ude3a\ude50-\ude51\u203c\u2049\u25aa-\u25ab\u25b6\u25c0\u25fb-\u25fe\u00a9\u00ae\u2122\u2139\udc04\u2600-\u26FF\u2b05\u2b06\u2b07\u2b1b\u2b1c\u2b50\u2b55\u231a\u231b\u2328\u23cf\u23e9-\u23f3\u23f8-\u23fa\udccf\u2935\u2934\u2190-\u21ff] + +MarkChar = [\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F] diff --git a/packages/message-parser/src/guards.ts b/packages/message-parser/src/guards.ts new file mode 100644 index 000000000000..f9d812f49458 --- /dev/null +++ b/packages/message-parser/src/guards.ts @@ -0,0 +1,10 @@ +import type { ASTNode } from './definitions'; + +export const isNodeOfType = ( + value: unknown, + type: N['type'] +): value is N => + typeof value === 'object' && + value !== null && + 'type' in value && + (value as { type: unknown }).type === type; diff --git a/packages/message-parser/src/index.ts b/packages/message-parser/src/index.ts new file mode 100644 index 000000000000..cbdae1382cb5 --- /dev/null +++ b/packages/message-parser/src/index.ts @@ -0,0 +1,26 @@ +import type { Root } from './definitions'; +import * as grammar from './grammar.pegjs'; + +export * from './definitions'; + +export { isNodeOfType } from './guards'; + +export type Options = { + colors?: boolean; + emoticons?: boolean; + katex?: { + dollarSyntax?: boolean; + parenthesisSyntax?: boolean; + }; + customDomains?: string[]; +}; + +export const parse = (input: string, options?: Options): Root => + grammar.parse(input, options); + +export { + /** @deprecated */ + parse as parser, + /** @deprecated */ + Root as MarkdownAST, +}; diff --git a/packages/message-parser/src/typings/peg.d.ts b/packages/message-parser/src/typings/peg.d.ts new file mode 100644 index 000000000000..9485bc0f0f8a --- /dev/null +++ b/packages/message-parser/src/typings/peg.d.ts @@ -0,0 +1,7 @@ +declare module '*.pegjs' { + import type { ParserOptions } from 'peggy'; + + import type { ASTMessage } from '../definitions'; + + export const parse: (input: string, options?: ParserOptions) => ASTMessage; +} diff --git a/packages/message-parser/src/utils.ts b/packages/message-parser/src/utils.ts new file mode 100644 index 000000000000..5902d8e33e7d --- /dev/null +++ b/packages/message-parser/src/utils.ts @@ -0,0 +1,235 @@ +import { parse as tldParse } from 'tldts'; + +import type { + BigEmoji, + Code, + Color, + Heading, + Markup, + Paragraph, + Types, + Task, + ListItem, + Inlines, + LineBreak, + Emoji, + KaTeX, + InlineKaTeX, + Link, +} from './definitions'; + +const generate = + (type: Type) => + (value: Types[Type]['value']): Types[Type] => + ({ type, value } as any); + +export const paragraph = generate('PARAGRAPH'); + +export const bold = generate('BOLD'); + +export const color = (r: number, g: number, b: number, a = 255): Color => ({ + type: 'COLOR', + value: { r, g, b, a }, +}); + +export const heading = ( + value: Heading['value'], + level: Heading['level'] = 1 +): Heading => ({ + type: 'HEADING', + level, + value, +}); + +export const code = ( + value: Code['value'], + language?: Code['language'] +): Code => ({ + type: 'CODE', + language: language || 'none', + value, +}); + +export const bigEmoji = (value: BigEmoji['value']): BigEmoji => ({ + type: 'BIG_EMOJI', + value, +}); + +export const task = (value: Task['value'], status: boolean): Task => ({ + type: 'TASK', + status, + value, +}); + +export const inlineCode = generate('INLINE_CODE'); +export const tasks = generate('TASKS'); + +export const italic = generate('ITALIC'); + +export const plain = generate('PLAIN_TEXT'); +export const strike = generate('STRIKE'); + +export const codeLine = generate('CODE_LINE'); + +const isValidLink = (link: string) => { + try { + return Boolean(new URL(link)); + } catch (error) { + return false; + } +}; + +export const link = (src: string, label?: Markup[]): Link => ({ + type: 'LINK', + value: { src: plain(src), label: label ?? [plain(src)] }, +}); + +export const autoLink = (src: string, customDomains?: string[]) => { + const validHosts = ['localhost', ...(customDomains ?? [])]; + const { isIcann, isIp, isPrivate, domain } = tldParse(src, { + detectIp: false, + allowPrivateDomains: true, + validHosts, + }); + + if ( + !(isIcann || isIp || isPrivate || (domain && validHosts.includes(domain))) + ) { + return plain(src); + } + + const href = isValidLink(src) || src.startsWith('//') ? src : `//${src}`; + + return link(href, [plain(src)]); +}; + +export const autoEmail = (src: string) => { + const href = `mailto:${src}`; + + const { isIcann, isIp, isPrivate } = tldParse(href, { + detectIp: false, + allowPrivateDomains: true, + }); + + if (!(isIcann || isIp || isPrivate)) { + return plain(src); + } + + return link(href, [plain(src)]); +}; + +export const image = (() => { + const fn = generate('IMAGE'); + return (src: string, label?: Markup) => + fn({ src: plain(src), label: label || plain(src) }); +})(); + +export const quote = generate('QUOTE'); + +export const mentionChannel = (() => { + const fn = generate('MENTION_CHANNEL'); + return (value: string) => fn(plain(value)); +})(); + +export const orderedList = generate('ORDERED_LIST'); + +export const unorderedList = generate('UNORDERED_LIST'); + +export const listItem = (text: Inlines[], number?: number): ListItem => ({ + type: 'LIST_ITEM', + value: text, + ...(number && { number }), +}); + +export const mentionUser = (() => { + const fn = generate('MENTION_USER'); + return (value: string) => fn(plain(value)); +})(); + +export const emoji = (shortCode: string): Emoji => ({ + type: 'EMOJI', + value: plain(shortCode), + shortCode, +}); + +export const emojiUnicode = (unicode: string): Emoji => ({ + type: 'EMOJI', + value: undefined, + unicode, +}); + +export const emoticon = (emoticon: string, shortCode: string): Emoji => ({ + type: 'EMOJI', + value: plain(emoticon), + shortCode, +}); + +const joinEmoji = ( + current: Inlines, + previous: Inlines | undefined, + next: Inlines | undefined +): Inlines => { + if (current.type !== 'EMOJI' || !current.value || (!previous && !next)) { + return current; + } + + const hasEmojiAsNeighbor = + previous?.type === current.type || current.type === next?.type; + const hasPlainAsNeighbor = + (previous?.type === 'PLAIN_TEXT' && previous.value.trim() !== '') || + (next?.type === 'PLAIN_TEXT' && next.value.trim() !== ''); + const isEmoticon = current.shortCode !== current.value.value; + + if (current.value && (hasEmojiAsNeighbor || hasPlainAsNeighbor)) { + if (isEmoticon) { + return current.value; + } + + return { + ...current.value, + value: `:${current.value.value}:`, + }; + } + + return current; +}; + +export const reducePlainTexts = ( + values: Paragraph['value'] +): Paragraph['value'] => + values.reduce((result, item, index) => { + const next = values[index + 1]; + const current = joinEmoji(item, values[index - 1], next); + const previous: Inlines = result[result.length - 1]; + + if (previous) { + if (current.type === 'PLAIN_TEXT' && current.type === previous.type) { + previous.value += current.value; + return result; + } + } + + return [...result, current]; + }, [] as Paragraph['value']); +export const lineBreak = (): LineBreak => ({ + type: 'LINE_BREAK', + value: undefined, +}); + +export const katex = (content: string): KaTeX => ({ + type: 'KATEX', + value: content, +}); + +export const inlineKatex = (content: string): InlineKaTeX => ({ + type: 'INLINE_KATEX', + value: content, +}); + +export const phoneChecker = (text: string, number: string) => { + if (number.length < 5) { + return plain(text); + } + + return link(`tel:${number}`, [plain(text)]); +}; diff --git a/packages/message-parser/tests/any.test.ts b/packages/message-parser/tests/any.test.ts new file mode 100644 index 000000000000..63e9a72c91b0 --- /dev/null +++ b/packages/message-parser/tests/any.test.ts @@ -0,0 +1,13 @@ +import { parse } from '../src'; +import { paragraph, plain } from '../src/utils'; + +test.each([ + ['free text', [paragraph([plain('free text')])]], + ['free text, with comma', [paragraph([plain('free text, with comma')])]], + [ + 'free text with unxpected/unfinished blocks *bold_', + [paragraph([plain('free text with unxpected/unfinished blocks *bold_')])], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/blockquotes.test.ts b/packages/message-parser/tests/blockquotes.test.ts new file mode 100644 index 000000000000..835074ec7fa6 --- /dev/null +++ b/packages/message-parser/tests/blockquotes.test.ts @@ -0,0 +1,40 @@ +import { parse } from '../src'; +import { paragraph, plain, quote, bold } from '../src/utils'; + +test.each([ + [ + ` +As Rocket Cat said: +> meowww +> grr. +`.trim(), + [ + paragraph([plain('As Rocket Cat said:')]), + quote([paragraph([plain('meowww')]), paragraph([plain('grr.')])]), + ], + ], + [ + ` +As Rocket Cat said: +> *meowww* +> grr. +`.trim(), + [ + paragraph([plain('As Rocket Cat said:')]), + quote([paragraph([bold([plain('meowww')])]), paragraph([plain('grr.')])]), + ], + ], + [ + ` +As Rocket Cat said: +>meowww +>grr. +`.trim(), + [ + paragraph([plain('As Rocket Cat said:')]), + quote([paragraph([plain('meowww')]), paragraph([plain('grr.')])]), + ], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/codeFence.test.ts b/packages/message-parser/tests/codeFence.test.ts new file mode 100644 index 000000000000..3c13ca3b8e62 --- /dev/null +++ b/packages/message-parser/tests/codeFence.test.ts @@ -0,0 +1,83 @@ +import { parse } from '../src'; +import { paragraph, plain, codeLine, code } from '../src/utils'; + +const multiply = (a: number, element: T): Array => + Array.from({ length: a }, () => element); + +test.each([ + [ + `\`\`\` +code + + + + +code +\`\`\``, + [ + code([ + codeLine(plain('code')), + ...multiply(4, codeLine(plain(''))), + codeLine(plain('code')), + ]), + ], + ], + [ + `\`\`\` +code + + +\`\`\``, + [code([codeLine(plain('code')), ...multiply(2, codeLine(plain('')))])], + ], + [ + `\`\`\` +code +\`\`\``, + [code([codeLine(plain('code'))])], + ], + [ + `\`\`\` +var a = "teste"; +\`\`\``, + [code([codeLine(plain('var a = "teste";'))])], + ], + [ + `\`\`\`javascript +code +\`\`\``, + [code([codeLine(plain('code'))], 'javascript')], + ], + [ + `\`\`\`bash c +code +\`\`\``, + [code([codeLine(plain('code'))], 'bash c')], + ], + [ + ` \`\`\` +code +\`\`\``, + [ + paragraph([plain(` \`\`\``)]), + paragraph([plain(`code`)]), + paragraph([plain(`\`\`\``)]), + ], + ], + [ + `\`\`\` +code +code +\`\`\``, + [code([codeLine(plain(`code`)), codeLine(plain(`code`))])], + ], + [ + `\`\`\` +# code +**code** +\`\`\``, + [code([codeLine(plain(`# code`)), codeLine(plain(`**code**`))])], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/color.test.ts b/packages/message-parser/tests/color.test.ts new file mode 100644 index 000000000000..9e492dbcfb22 --- /dev/null +++ b/packages/message-parser/tests/color.test.ts @@ -0,0 +1,34 @@ +import { parse } from '../src'; +import { color, paragraph, plain } from '../src/utils'; + +test.each([ + [ + 'color:#ccc', + [paragraph([color(0xcc, 0xcc, 0xcc)])], + [paragraph([plain('color:#ccc')])], + ], + [ + 'color:#cccc', + [paragraph([color(0xcc, 0xcc, 0xcc, 0xcc)])], + [paragraph([plain('color:#cccc')])], + ], + [ + 'color:#c7c7c7', + [paragraph([color(0xc7, 0xc7, 0xc7)])], + [paragraph([plain('color:#c7c7c7')])], + ], + [ + 'color:#c7c7c7c7', + [paragraph([color(0xc7, 0xc7, 0xc7, 0xc7)])], + [paragraph([plain('color:#c7c7c7c7')])], + ], + ['color:#c7c7c7c7c7', [paragraph([plain('color:#c7c7c7c7c7')])], undefined], + ['color:#c7', [paragraph([plain('color:#c7')])], undefined], + ['color:#zzz', [paragraph([plain('color:#zzz')])], undefined], +])('parses %p', (input, output, disabledOutput) => { + expect(parse(input, { colors: true })).toMatchObject(output); + + if (disabledOutput) { + expect(parse(input, { colors: false })).toMatchObject(disabledOutput); + } +}); diff --git a/packages/message-parser/tests/email.test.ts b/packages/message-parser/tests/email.test.ts new file mode 100644 index 000000000000..4e841385a991 --- /dev/null +++ b/packages/message-parser/tests/email.test.ts @@ -0,0 +1,144 @@ +import { parse } from '../src'; +import { link, paragraph, plain } from '../src/utils'; + +test.each([ + [ + 'joe@joe.com', + [paragraph([link('mailto:joe@joe.com', [plain('joe@joe.com')])])], + ], + [ + "joe@joe.com is Joe's email", + [ + paragraph([ + link('mailto:joe@joe.com', [plain('joe@joe.com')]), + plain(" is Joe's email"), + ]), + ], + ], + [ + "Joe's email is joe@joe.com because it is", + [ + paragraph([ + plain("Joe's email is "), + link('mailto:joe@joe.com', [plain('joe@joe.com')]), + plain(' because it is'), + ]), + ], + ], + [ + "Joe's email is joe@joe.com", + [ + paragraph([ + plain("Joe's email is "), + link('mailto:joe@joe.com', [plain('joe@joe.com')]), + ]), + ], + ], + [ + "Joe's email is joe@joe.com. Try emailing him", + [ + paragraph([ + plain("Joe's email is "), + link('mailto:joe@joe.com', [plain('joe@joe.com')]), + plain('. Try emailing him'), + ]), + ], + ], + [ + "Joe's email is joe.smith@joe.com", + [ + paragraph([ + plain("Joe's email is "), + link('mailto:joe.smith@joe.com', [plain('joe.smith@joe.com')]), + ]), + ], + ], + [ + "Joe's email is JOE@JOE.COM", + [ + paragraph([ + plain("Joe's email is "), + link('mailto:JOE@JOE.COM', [plain('JOE@JOE.COM')]), + ]), + ], + ], + [ + "Joe's email is (joe@joe.com)", + [ + paragraph([ + plain("Joe's email is ("), + link('mailto:joe@joe.com', [plain('joe@joe.com')]), + plain(')'), + ]), + ], + ], + [ + "Joe's email is (joe_roe@joe.com)", + [ + paragraph([ + plain("Joe's email is ("), + link('mailto:joe_roe@joe.com', [plain('joe_roe@joe.com')]), + plain(')'), + ]), + ], + ], + [ + "Joe's email is (joe'roe@joe.com)", + [ + paragraph([ + plain("Joe's email is ("), + link("mailto:joe'roe@joe.com", [plain("joe'roe@joe.com")]), + plain(')'), + ]), + ], + ], + [ + "Joe's email is mañana@mañana.com", + [ + paragraph([ + plain("Joe's email is "), + link('mailto:mañana@mañana.com', [plain('mañana@mañana.com')]), + ]), + ], + ], + [ + "Joe's email is Кириллица@Кириллица.com", + [ + paragraph([ + plain("Joe's email is "), + link('mailto:Кириллица@Кириллица.com', [ + plain('Кириллица@Кириллица.com'), + ]), + ]), + ], + ], + ['Hi there@stuff', [paragraph([plain('Hi there@stuff')])]], + [ + 'My email is busueng.kim@aaa.com', + [ + paragraph([ + plain('My email is '), + link('mailto:busueng.kim@aaa.com', [plain('busueng.kim@aaa.com')]), + ]), + ], + ], + [ + 'My email is mailto:asdf@asdf.com', + [ + paragraph([ + plain('My email is '), + link('mailto:asdf@asdf.com', [plain('asdf@asdf.com')]), + ]), + ], + ], + [ + 'My email is fake@gmail.c', + [paragraph([plain('My email is fake@gmail.c')])], + ], + [ + 'My email is fake@gmail.comf', + [paragraph([plain('My email is fake@gmail.comf')])], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/emoji.test.ts b/packages/message-parser/tests/emoji.test.ts new file mode 100644 index 000000000000..3d64e59a2c3c --- /dev/null +++ b/packages/message-parser/tests/emoji.test.ts @@ -0,0 +1,90 @@ +import { parse } from '../src'; +import { emoji, bigEmoji, paragraph, plain, emojiUnicode } from '../src/utils'; + +test.each([ + [':smile: asd', [paragraph([emoji('smile'), plain(' asd')])]], + ['text:inner:outer', [paragraph([plain('text:inner:outer')])]], + ['10:20:30', [paragraph([plain('10:20:30')])]], + ['10:20:30:', [paragraph([plain('10:20:30:')])]], + ['":smile:"', [paragraph([plain('":smile:"')])]], + ['":smile: "', [paragraph([plain('":smile: "')])]], + ['" :smile: "', [paragraph([plain('" '), emoji('smile'), plain(' "')])]], + [ + `:smile: + :smile: + `, + [bigEmoji([emoji('smile'), emoji('smile')])], + ], + [ + 'asdas :smile: asd', + [paragraph([plain('asdas '), emoji('smile'), plain(' asd')])], + ], + [ + 'normal emojis :smile: :smile: :smile:', + [ + paragraph([ + plain('normal emojis '), + emoji('smile'), + plain(' '), + emoji('smile'), + plain(' '), + emoji('smile'), + ]), + ], + ], + [ + ':smile::smile::smile:', + [bigEmoji([emoji('smile'), emoji('smile'), emoji('smile')])], + ], + [ + ' :smile::smile::smile: ', + [bigEmoji([emoji('smile'), emoji('smile'), emoji('smile')])], + ], + [ + '\n :smile::smile::smile: \n', + [bigEmoji([emoji('smile'), emoji('smile'), emoji('smile')])], + ], + [ + ':smile: :smile: :smile:', + [bigEmoji([emoji('smile'), emoji('smile'), emoji('smile')])], + ], + [':smile::smile:', [bigEmoji([emoji('smile'), emoji('smile')])]], + [':smile:', [bigEmoji([emoji('smile')])]], + ['Hi :+1:', [paragraph([plain('Hi '), emoji('+1')])]], + ['Hi :+1_tone4:', [paragraph([plain('Hi '), emoji('+1_tone4')])]], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); + +// Tests for unicode emojis +test.each([ + ['😀', [bigEmoji([emojiUnicode('😀')])]], + ['😃', [bigEmoji([emojiUnicode('😃')])]], + ['🥵', [bigEmoji([emojiUnicode('🥵')])]], + ['🧿', [bigEmoji([emojiUnicode('🧿')])]], + ['🐶', [bigEmoji([emojiUnicode('🐶')])]], + ['🍏', [bigEmoji([emojiUnicode('🍏')])]], + ['⚽', [bigEmoji([emojiUnicode('⚽')])]], + ['⚽️', [bigEmoji([emojiUnicode('⚽️')])]], + ['👨‍👩‍👧‍👦', [bigEmoji([emojiUnicode('👨‍👩‍👧‍👦')])]], + ['🚗', [bigEmoji([emojiUnicode('🚗')])]], + ['⌚️', [bigEmoji([emojiUnicode('⌚️')])]], + ['❤️', [bigEmoji([emojiUnicode('❤️')])]], + ['🏳️', [bigEmoji([emojiUnicode('🏳️')])]], + ['🧑🏾‍💻', [bigEmoji([emojiUnicode('🧑🏾‍💻')])]], + ['🧑🏾‍💻🧑🏾‍💻', [bigEmoji([emojiUnicode('🧑🏾‍💻'), emojiUnicode('🧑🏾‍💻')])]], + [ + '🧑🏾‍💻🧑🏾‍💻🧑🏾‍💻', + [bigEmoji([emojiUnicode('🧑🏾‍💻'), emojiUnicode('🧑🏾‍💻'), emojiUnicode('🧑🏾‍💻')])], + ], + ['👆🏽', [bigEmoji([emojiUnicode('👆🏽')])]], + ['👆🏽👆🏽', [bigEmoji([emojiUnicode('👆🏽'), emojiUnicode('👆🏽')])]], + [ + '👆🏽👆🏽👆🏽', + [bigEmoji([emojiUnicode('👆🏽'), emojiUnicode('👆🏽'), emojiUnicode('👆🏽')])], + ], + ['👆🏺', [bigEmoji([emojiUnicode('👆'), emojiUnicode('🏺')])]], + ['Hi 👍', [paragraph([plain('Hi '), emojiUnicode('👍')])]], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/emoticons.test.ts b/packages/message-parser/tests/emoticons.test.ts new file mode 100644 index 000000000000..9cf58187e035 --- /dev/null +++ b/packages/message-parser/tests/emoticons.test.ts @@ -0,0 +1,148 @@ +import { parse } from '../src'; +import { bigEmoji, paragraph, plain, emoticon } from '../src/utils'; + +test.each([ + // Should render normal Emojis + [ + `test + :) test`, + [ + paragraph([plain('test')]), + paragraph([ + plain(' '), + emoticon(':)', 'slight_smile'), + plain(' test'), + ]), + ], + ], + [':) asd', [paragraph([emoticon(':)', 'slight_smile'), plain(' asd')])]], + [':) asd', [paragraph([emoticon(':)', 'slight_smile'), plain(' asd')])]], + [ + ' :) asd', + [paragraph([plain(' '), emoticon(':)', 'slight_smile'), plain(' asd')])], + ], + ['Hi :)', [paragraph([plain('Hi '), emoticon(':)', 'slight_smile')])]], + [ + 'asdas :) asd', + [ + paragraph([ + plain('asdas '), + emoticon(':)', 'slight_smile'), + plain(' asd'), + ]), + ], + ], + [ + ':) :) :) :)', + [ + paragraph([ + emoticon(':)', 'slight_smile'), + plain(' '), + emoticon(':)', 'slight_smile'), + plain(' '), + emoticon(':)', 'slight_smile'), + plain(' '), + emoticon(':)', 'slight_smile'), + ]), + ], + ], + + // Should render BigEmojis + [ + `:) + :) + `, + [ + bigEmoji([ + emoticon(':)', 'slight_smile'), + emoticon(':)', 'slight_smile'), + ]), + ], + ], + [ + ':):):)', + [ + bigEmoji([ + emoticon(':)', 'slight_smile'), + emoticon(':)', 'slight_smile'), + emoticon(':)', 'slight_smile'), + ]), + ], + ], + + [ + ' :):):) ', + [ + bigEmoji([ + emoticon(':)', 'slight_smile'), + emoticon(':)', 'slight_smile'), + emoticon(':)', 'slight_smile'), + ]), + ], + ], + + [ + '\n :):):) \n', + [ + bigEmoji([ + emoticon(':)', 'slight_smile'), + emoticon(':)', 'slight_smile'), + emoticon(':)', 'slight_smile'), + ]), + ], + ], + [ + ':) :) :)', + [ + bigEmoji([ + emoticon(':)', 'slight_smile'), + emoticon(':)', 'slight_smile'), + emoticon(':)', 'slight_smile'), + ]), + ], + ], + + [ + ':) :)', + [ + bigEmoji([ + emoticon(':)', 'slight_smile'), + emoticon(':)', 'slight_smile'), + ]), + ], + ], + [':)', [bigEmoji([emoticon(':)', 'slight_smile')])]], + [' :)', [bigEmoji([emoticon(':)', 'slight_smile')])]], + [':) ', [bigEmoji([emoticon(':)', 'slight_smile')])]], + [' :) ', [bigEmoji([emoticon(':)', 'slight_smile')])]], + ['D:', [bigEmoji([emoticon('D:', 'fearful')])]], + ['D: D:', [bigEmoji([emoticon('D:', 'fearful'), emoticon('D:', 'fearful')])]], + [ + ' D: D: D: ', + [ + bigEmoji([ + emoticon('D:', 'fearful'), + emoticon('D:', 'fearful'), + emoticon('D:', 'fearful'), + ]), + ], + ], + ['Hi D:', [paragraph([plain('Hi '), emoticon('D:', 'fearful')])]], + + // Should not render Emojis or BigEmojis if they are not surrounded by spaces + ['normal emojis :):):)', [paragraph([plain('normal emojis :):):)')])]], + [':)10:30', [paragraph([plain(':)10:30')])]], + [':smile::)text', [paragraph([plain(':smile::)text')])]], + ['text:):smile:', [paragraph([plain('text:):smile:')])]], + ['text:):)', [paragraph([plain('text:):)')])]], + [':):):) normal emojis', [paragraph([plain(':):):) normal emojis')])]], + [':):):):)', [paragraph([plain(':):):):)')])]], + ['10:30', [paragraph([plain('10:30')])]], + ['he:)llo', [paragraph([plain('he:)llo')])]], + [':)Hi', [paragraph([plain(':)Hi')])]], + ['Hi:) Hi', [paragraph([plain('Hi:) Hi')])]], + ['Hi:)', [paragraph([plain('Hi:)')])]], + ['@#@#! :)@!@', [paragraph([plain('@#@#! :)@!@')])]], +])('parses %p', (input, output) => { + expect(parse(input, { emoticons: true })).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/emphasis.test.ts b/packages/message-parser/tests/emphasis.test.ts new file mode 100644 index 000000000000..bae05be7d158 --- /dev/null +++ b/packages/message-parser/tests/emphasis.test.ts @@ -0,0 +1,174 @@ +import { parse } from '../src'; +import { + italic, + paragraph, + plain, + strike, + bold, + emoji, + link, + bigEmoji, + emojiUnicode, + mentionChannel, + mentionUser, +} from '../src/utils'; + +test.each([ + ['_:smile:_', [paragraph([italic([emoji('smile')])])]], + ['_:slight_smile:_', [paragraph([italic([emoji('slight_smile')])])]], + [ + '_test :smile: test_', + [paragraph([italic([plain('test '), emoji('smile'), plain(' test')])])], + ], + [ + '_test :slight_smile: test_', + [ + paragraph([ + italic([plain('test '), emoji('slight_smile'), plain(' test')]), + ]), + ], + ], + ['_😀_', [paragraph([italic([emojiUnicode('😀')])])]], + ['_test 😀_', [paragraph([italic([plain('test '), emojiUnicode('😀')])])]], + [ + '_test @guilherme.gazzo test_', + [ + paragraph([ + italic([ + plain('test '), + mentionUser('guilherme.gazzo'), + plain(' test'), + ]), + ]), + ], + ], + [ + '_test #GENERAL test_', + [ + paragraph([ + italic([plain('test '), mentionChannel('GENERAL'), plain(' test')]), + ]), + ], + ], + [ + '_[A brand new Gist](https://gist.github.com/24dddfa97bef58f46ac2ce0f80c58ba4)_', + [ + paragraph([ + italic([ + link('https://gist.github.com/24dddfa97bef58f46ac2ce0f80c58ba4', [ + plain('A brand new Gist'), + ]), + ]), + ]), + ], + ], + ['__italic__', [paragraph([italic([plain('italic')])])]], + ['__italic__non', [paragraph([plain('__italic__non')])]], + ['__test__test__', [paragraph([plain('__test__test__')])]], + ['pre__italic__post', [paragraph([plain('pre__italic__post')])]], + [' pre__italic__post', [paragraph([plain(' pre__italic__post')])]], + [ + ' pre__**~~boldstrikeitalic~~**__post ', + [ + paragraph([ + plain(' pre__'), + bold([strike([plain('boldstrikeitalic')])]), + plain('__post '), + ]), + ], + ], + ['__', [paragraph([plain('__')])]], + ['_ _', [paragraph([plain('_ _')])]], + ['__ _', [paragraph([plain('__ _')])]], + ['__ __', [paragraph([plain('__ __')])]], + ['_ Hello_', [paragraph([italic([plain(' Hello')])])]], + ['_Hello _', [paragraph([italic([plain('Hello ')])])]], + [':custom_emoji_case:', [bigEmoji([emoji('custom_emoji_case')])]], + ['_Hel lo_', [paragraph([italic([plain('Hel lo')])])]], + ['_Hello_', [paragraph([italic([plain('Hello')])])]], + ['__Hello__', [paragraph([italic([plain('Hello')])])]], + ['__Hello_', [paragraph([plain('_'), italic([plain('Hello')])])]], + ['_Hello__', [paragraph([italic([plain('Hello')]), plain('_')])]], + ['_Hello', [paragraph([plain('_Hello')])]], + ['Hello_', [paragraph([plain('Hello_')])]], + ['He_llo', [paragraph([plain('He_llo')])]], + [ + '___Hello___', + [paragraph([plain('_'), italic([plain('Hello')]), plain('_')])], + ], + ['___Hello__', [paragraph([plain('_'), italic([plain('Hello')])])]], + [ + '_Hello_ this is dog', + [paragraph([italic([plain('Hello')]), plain(` this is dog`)])], + ], + [ + 'Rocket cat says _Hello_', + [paragraph([plain(`Rocket cat says `), italic([plain('Hello')])])], + ], + [ + 'He said _Hello_ to her', + [ + paragraph([ + plain(`He said `), + italic([plain('Hello')]), + plain(` to her`), + ]), + ], + ], + [ + '__Hello__ this is dog', + [paragraph([italic([plain('Hello')]), plain(` this is dog`)])], + ], + [ + 'Rocket cat says __Hello__', + [paragraph([plain(`Rocket cat says `), italic([plain('Hello')])])], + ], + [ + 'He said __Hello__ to her', + [ + paragraph([ + plain(`He said `), + italic([plain('Hello')]), + plain(` to her`), + ]), + ], + ], + ['text_hello_text', [paragraph([plain('text_hello_text')])]], + ['_hello_text', [paragraph([plain('_hello_text')])]], + ['text_hello_', [paragraph([plain('text_hello_')])]], + ['_italic@test_', [paragraph([italic([plain('italic@test')])])]], + ['_italic#test_', [paragraph([italic([plain('italic#test')])])]], + ['paragraph@test__', [paragraph([plain('paragraph@test__')])]], + [ + '_ @guilherme_gazzo_ _', + [ + paragraph([ + italic([plain(' '), mentionUser('guilherme_gazzo_'), plain(' ')]), + ]), + ], + ], + [ + '_ @guilherme.gazzo _', + [ + paragraph([ + italic([plain(' '), mentionUser('guilherme.gazzo'), plain(' ')]), + ]), + ], + ], + [ + '**reference link inside [emphasis with more [references](https://rocket.chat)](https://rocket.chat)**', + [ + paragraph([ + bold([ + plain('reference link inside '), + link('https://rocket.chat', [ + plain('emphasis with more [references'), + ]), + plain('](https://rocket.chat)'), + ]), + ]), + ], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/escaped.test.ts b/packages/message-parser/tests/escaped.test.ts new file mode 100644 index 000000000000..ffe297aee299 --- /dev/null +++ b/packages/message-parser/tests/escaped.test.ts @@ -0,0 +1,38 @@ +import { parse } from '../src'; +import { paragraph, plain, bold } from '../src/utils'; + +test.each([ + ['¯\\\\_(ツ)_/¯', [paragraph([plain('¯\\_(ツ)_/¯')])]], + [ + '\\*escaped as*bold*escaped*', + [ + paragraph([ + plain('*escaped as'), + bold([plain('bold')]), + plain('escaped*'), + ]), + ], + ], + ['\\*not bold*', [paragraph([plain('*not bold*')])]], + ['*_~`#.'.split('').join('\\'), [paragraph([plain('*_~`#.')])]], + ['\\*not emphasized*', [paragraph([plain('*not emphasized*')])]], + ['\\
          tag plain text', [paragraph([plain('\\
          tag plain text')])]], + [ + '\\[it is not a link](/foo)', + [paragraph([plain('\\[it is not a link](/foo)')])], + ], + ['\\`not code`', [paragraph([plain('`not code`')])]], + ['1\\. not a list', [paragraph([plain('1. not a list')])]], + ['\\* not a list', [paragraph([plain('* not a list')])]], + ['\\# not a heading', [paragraph([plain('# not a heading')])]], + [ + '\\[foo]: /url "not a reference"', + [paragraph([plain('\\[foo]: /url "not a reference"')])], + ], + [ + '\\ö not a character entity', + [paragraph([plain('\\ö not a character entity')])], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/heading.test.ts b/packages/message-parser/tests/heading.test.ts new file mode 100644 index 000000000000..84b9463bb639 --- /dev/null +++ b/packages/message-parser/tests/heading.test.ts @@ -0,0 +1,58 @@ +import { parse } from '../src'; +import { + heading, + lineBreak, + mentionChannel, + paragraph, + plain, +} from '../src/utils'; + +test.each([ + ['# h1', [heading([plain('h1')], 1)]], + ['# Hello', [heading([plain('Hello')], 1)]], + ['# Rocket.Cat', [heading([plain('Rocket.Cat')], 1)]], + ['# Hi', [heading([plain('Hi')], 1)]], + ['# Hello this is dog', [heading([plain('Hello this is dog')], 1)]], + ['# Rocket cat says Hello', [heading([plain('Rocket cat says Hello')], 1)]], + ['# He said Hello to her', [heading([plain('He said Hello to her')], 1)]], + ['#Hello', [paragraph([mentionChannel('Hello')])]], + ['#Hello#', [paragraph([mentionChannel('Hello'), plain('#')])]], + ['He#llo', [paragraph([plain('He#llo')])]], + + ['## Hello', [heading([plain('Hello')], 2)]], + ['## Rocket.Cat', [heading([plain('Rocket.Cat')], 2)]], + ['## Hi', [heading([plain('Hi')], 2)]], + ['## Hello this is dog', [heading([plain('Hello this is dog')], 2)]], + ['## Rocket cat says Hello', [heading([plain('Rocket cat says Hello')], 2)]], + ['## He said Hello to her', [heading([plain('He said Hello to her')], 2)]], + ['##Hello', [paragraph([plain('##Hello')])]], + ['##Hello##', [paragraph([plain('##Hello##')])]], + ['He##llo', [paragraph([plain('He##llo')])]], + + ['### Hello', [heading([plain('Hello')], 3)]], + ['### Rocket.Cat', [heading([plain('Rocket.Cat')], 3)]], + ['### Hi', [heading([plain('Hi')], 3)]], + ['### Hello this is dog', [heading([plain('Hello this is dog')], 3)]], + ['### Rocket cat says Hello', [heading([plain('Rocket cat says Hello')], 3)]], + ['### He said Hello to her', [heading([plain('He said Hello to her')], 3)]], + ['###Hello', [paragraph([plain('###Hello')])]], + ['###Hello###', [paragraph([plain('###Hello###')])]], + ['He###llo', [paragraph([plain('He###llo')])]], + + ['#### Hello', [heading([plain('Hello')], 4)]], + ['#### Rocket.Cat', [heading([plain('Rocket.Cat')], 4)]], + ['#### Hi', [heading([plain('Hi')], 4)]], + ['#### Hello this is dog', [heading([plain('Hello this is dog')], 4)]], + [ + '#### Rocket cat says Hello', + [heading([plain('Rocket cat says Hello')], 4)], + ], + ['#### He said Hello to her', [heading([plain('He said Hello to her')], 4)]], + ['####Hello', [paragraph([plain('####Hello')])]], + ['####Hello####', [paragraph([plain('####Hello####')])]], + ['He####llo', [paragraph([plain('He####llo')])]], + ['# Hello\n', [heading([plain('Hello')], 1), lineBreak()]], + ['# # Hello\n', [heading([plain('# Hello')], 1), lineBreak()]], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/image.test.ts b/packages/message-parser/tests/image.test.ts new file mode 100644 index 000000000000..a9e0306bbf23 --- /dev/null +++ b/packages/message-parser/tests/image.test.ts @@ -0,0 +1,19 @@ +import { parse } from '../src'; +import { image, paragraph, plain } from '../src/utils'; + +test.each([ + [ + '![image](https://rocket.chat/assets/img/header/logo.svg)', + [ + paragraph([ + image('https://rocket.chat/assets/img/header/logo.svg', plain('image')), + ]), + ], + ], + [ + '![](https://rocket.chat/assets/img/header/logo.svg)', + [paragraph([image('https://rocket.chat/assets/img/header/logo.svg')])], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/inlineCode.test.ts b/packages/message-parser/tests/inlineCode.test.ts new file mode 100644 index 000000000000..72d86adcff34 --- /dev/null +++ b/packages/message-parser/tests/inlineCode.test.ts @@ -0,0 +1,27 @@ +import { parse } from '../src'; +import { inlineCode, paragraph, plain } from '../src/utils'; + +test.each([ + [ + '`[asd](https://localhost)`', + [paragraph([inlineCode(plain('[asd](https://localhost)'))])], + ], + [`\`code\``, [paragraph([inlineCode(plain('code'))])]], + [ + `File extension (\`.mov\`)`, + [ + paragraph([ + plain('File extension ('), + inlineCode(plain('.mov')), + plain(')'), + ]), + ], + ], + ['`@rocket.chat`', [paragraph([inlineCode(plain('@rocket.chat'))])]], + [ + '`@rocket.chat/message-parser`', + [paragraph([inlineCode(plain('@rocket.chat/message-parser'))])], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/inlineCodeStrike.test.ts b/packages/message-parser/tests/inlineCodeStrike.test.ts new file mode 100644 index 000000000000..cdd3e039cd26 --- /dev/null +++ b/packages/message-parser/tests/inlineCodeStrike.test.ts @@ -0,0 +1,46 @@ +import { parse } from '../src'; +import { + bold, + inlineCode, + italic, + paragraph, + plain, + strike, +} from '../src/utils'; + +test.each([ + [ + '~~`Striking Inline Code`~~', + [paragraph([strike([inlineCode(plain('Striking Inline Code'))])])], + ], + [ + '~~_`Striking Inline Code with Italics`_~~', + [ + paragraph([ + strike([ + italic([inlineCode(plain('Striking Inline Code with Italics'))]), + ]), + ]), + ], + ], + [ + '~~**`Striking Inline Code with Bold`**~~', + [ + paragraph([ + strike([bold([inlineCode(plain('Striking Inline Code with Bold'))])]), + ]), + ], + ], + [ + '~~__*`Striking Inline Code with Bold`*__~~', + [ + paragraph([ + strike([ + italic([bold([inlineCode(plain('Striking Inline Code with Bold'))])]), + ]), + ]), + ], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/katex.test.ts b/packages/message-parser/tests/katex.test.ts new file mode 100644 index 000000000000..2ffb3ec2f06b --- /dev/null +++ b/packages/message-parser/tests/katex.test.ts @@ -0,0 +1,33 @@ +import { parse } from '../src'; +import { inlineKatex, katex, paragraph, plain } from '../src/utils'; + +test.each([ + [ + `\\[ + \\f\\relax{x} = \\int_{-\\infty}^\\infty + \\f\\hat\\xi\\,e^{2 \\pi i \\xi x} + \\,d\\xi + \\]`, + [ + katex(` + \\f\\relax{x} = \\int_{-\\infty}^\\infty + \\f\\hat\\xi\\,e^{2 \\pi i \\xi x} + \\,d\\xi + `), + ], + ], + [ + 'Easy as \\(E = mc^2\\), right?', + [ + paragraph([ + plain('Easy as '), + inlineKatex('E = mc^2'), + plain(', right?'), + ]), + ], + ], +])('parses %p', (input, output) => { + expect(parse(input, { katex: { parenthesisSyntax: true } })).toMatchObject( + output + ); +}); diff --git a/packages/message-parser/tests/lineBreak.test.ts b/packages/message-parser/tests/lineBreak.test.ts new file mode 100644 index 000000000000..e4faf67b17f3 --- /dev/null +++ b/packages/message-parser/tests/lineBreak.test.ts @@ -0,0 +1,35 @@ +import { parse } from '../src'; +import { lineBreak, paragraph, plain } from '../src/utils'; + +test.each([ + [ + `test + +test2`, + [paragraph([plain('test')]), lineBreak(), paragraph([plain('test2')])], + ], + [ + `test + +test2 +`, + [paragraph([plain('test')]), lineBreak(), paragraph([plain('test2')])], + ], + [ + `test + + + +test2 +`, + [ + paragraph([plain('test')]), + lineBreak(), + lineBreak(), + lineBreak(), + paragraph([plain('test2')]), + ], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/link.test.ts b/packages/message-parser/tests/link.test.ts new file mode 100644 index 000000000000..1e083fde5d70 --- /dev/null +++ b/packages/message-parser/tests/link.test.ts @@ -0,0 +1,589 @@ +import { parse } from '../src'; +import { + link, + paragraph, + plain, + bold, + strike, + italic, + quote, + lineBreak, + unorderedList, + listItem, + orderedList, +} from '../src/utils'; + +test.each([ + [ + '', + [paragraph([link('https://domain.com', [plain('Test')])])], + ], + + [ + ``, + [paragraph([plain('')])], + ], + [ + ` quote here`, + [ + paragraph([plain('', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351', + [plain('Test')] + ), + ]), + ], + ], + [ + '[title](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351', + [plain('title')] + ), + ]), + ], + ], + [ + '[**title**](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351', + [bold([plain('title')])] + ), + ]), + ], + ], + [ + '[~~title~~](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351', + [strike([plain('title')])] + ), + ]), + ], + ], + [ + '[__title__](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351', + [italic([plain('title')])] + ), + ]), + ], + ], + [ + '[__**~~title~~**__](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351', + [italic([bold([strike([plain('title')])])])] + ), + ]), + ], + ], + [ + '[title](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351?query=test12-34)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351?query=test12-34', + [plain('title')] + ), + ]), + ], + ], + [ + '[title](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do?query=test12-34#Cases/dv/413244000073043351)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do?query=test12-34#Cases/dv/413244000073043351', + [plain('title')] + ), + ]), + ], + ], + [ + '[title](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351?query=test12-34&query2=abc123)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351?query=test12-34&query2=abc123', + [plain('title')] + ), + ]), + ], + ], + [ + '[title](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases?query=test12-34&query2=abcd!e/dv/413244000073043351)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases?query=test12-34&query2=abcd!e/dv/413244000073043351', + [plain('title')] + ), + ]), + ], + ], + [ + '[title](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351?query=test12-34&query2=abcd!~-._%2B+)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351?query=test12-34&query2=abcd!~-._%2B+', + [plain('title')] + ), + ]), + ], + ], + ['google.com', [paragraph([link('//google.com', [plain('google.com')])])]], + [ + 'www.google.com', + [paragraph([link('//www.google.com', [plain('www.google.com')])])], + ], + ['rocket.chat:8080', [paragraph([link('rocket.chat:8080')])]], + ['ShouldNotBeALink', [paragraph([plain('ShouldNotBeALink')])]], + [ + 'http:/ google.com', + [ + paragraph([ + plain('http:/ '), + link('//google.com', [plain('google.com')]), + ]), + ], + ], + [ + '[custom](custom://google.com)', + [paragraph([link('custom://google.com', [plain('custom')])])], + ], + [ + '[thing](https://www.thingiverse.com/thing:5451684)', + [ + paragraph([ + link('https://www.thingiverse.com/thing:5451684', [plain('thing')]), + ]), + ], + ], + [ + 'https://t.me/joinchat/chatexample', + [paragraph([link('https://t.me/joinchat/chatexample')])], + ], + [ + '[telegram invite](https://t.me/joinchat/chatexample)', + [ + paragraph([ + link('https://t.me/joinchat/chatexample', [plain('telegram invite')]), + ]), + ], + ], + [ + '[Github link with hash](https://github.com/RocketChat/Rocket.Chat/pull/26751/files#diff-c87b108ecf1ede549f8ede68eca840fbb330180b927df0b8a0b4df5d06cbd89b)', + [ + paragraph([ + link( + 'https://github.com/RocketChat/Rocket.Chat/pull/26751/files#diff-c87b108ecf1ede549f8ede68eca840fbb330180b927df0b8a0b4df5d06cbd89b', + [plain('Github link with hash')] + ), + ]), + ], + ], + [ + '[Github link with hash](https://github.com/RocketChat/Rocket.Chat/pull/26751/files#diff)', + [ + paragraph([ + link( + 'https://github.com/RocketChat/Rocket.Chat/pull/26751/files#diff', + [plain('Github link with hash')] + ), + ]), + ], + ], + [ + '[Github link without hash](https://github.com/RocketChat/Rocket.Chat/pull/26751/files)', + [ + paragraph([ + link('https://github.com/RocketChat/Rocket.Chat/pull/26751/files', [ + plain('Github link without hash'), + ]), + ]), + ], + ], + [ + '[Link with special chars](https://github.com/RocketChat/Rocket.Chat*[/]^_`{}~)', + [ + paragraph([ + link('https://github.com/RocketChat/Rocket.Chat*[/]^_`{}~', [ + plain('Link with special chars'), + ]), + ]), + ], + ], + [ + '[Google complex Link](https://www.google.com/url?rct=j&sa=t&url=https://ga.de/freizeit/region-erleben/bonn-und-region-tipps-fuers-wochenende-flohmarkt-rheinaue-weltkindertag-stadtfest_aid-53876987&ct=ga&cd=CAIyHDQ0NzEyYWE3MDA1MGNhNTQ6Y29tOmRlOkRFOlI&usg=AOvVaw3ySYrO9lM0iNSnk43gPVwZ)', + [ + paragraph([ + link( + 'https://www.google.com/url?rct=j&sa=t&url=https://ga.de/freizeit/region-erleben/bonn-und-region-tipps-fuers-wochenende-flohmarkt-rheinaue-weltkindertag-stadtfest_aid-53876987&ct=ga&cd=CAIyHDQ0NzEyYWE3MDA1MGNhNTQ6Y29tOmRlOkRFOlI&usg=AOvVaw3ySYrO9lM0iNSnk43gPVwZ', + [plain('Google complex Link')] + ), + ]), + ], + ], + [ + '[Rocket.Chat](https://rocket.chat) Inline Text', + [ + paragraph([ + link('https://rocket.chat', [plain('Rocket.Chat')]), + plain(' Inline Text'), + ]), + ], + ], + [ + 'https://analytics.zoho.com/open-view/123456789 Same Line', + [ + paragraph([ + link('https://analytics.zoho.com/open-view/123456789', [ + plain('https://analytics.zoho.com/open-view/123456789'), + ]), + plain(' Same Line'), + ]), + ], + ], + [ + `[Rocket.Chat](https://rocket.chat) +Text after in a new line after link`, + [ + paragraph([link('https://rocket.chat', [plain('Rocket.Chat')])]), + paragraph([plain('Text after in a new line after link')]), + ], + ], + [ + `https://analytics.zoho.com/open-view/123456789 +Second line`, + [ + paragraph([ + link('https://analytics.zoho.com/open-view/123456789', [ + plain('https://analytics.zoho.com/open-view/123456789'), + ]), + ]), + paragraph([plain('Second line')]), + ], + ], + [ + `[Rocket.Chat](https://rocket.chat) + +Text after line break`, + [ + paragraph([link('https://rocket.chat', [plain('Rocket.Chat')])]), + lineBreak(), + paragraph([plain('Text after line break')]), + ], + ], + [ + ` +[List Header Link](https://rocket.chat) +- First item +- Second item +- Third item +- *Fourth item* +`.trim(), + [ + paragraph([link('https://rocket.chat', [plain('List Header Link')])]), + unorderedList([ + listItem([plain('First item')]), + listItem([plain('Second item')]), + listItem([plain('Third item')]), + listItem([bold([plain('Fourth item')])]), + ]), + ], + ], + [ + `[List Header Link](https://rocket.chat) +7. First item +2. Second item +8. Third item +4. *Fourth item* +15. *Fifteenth item* +`.trim(), + [ + paragraph([link('https://rocket.chat', [plain('List Header Link')])]), + orderedList([ + listItem([plain('First item')], 7), + listItem([plain('Second item')], 2), + listItem([plain('Third item')], 8), + listItem([bold([plain('Fourth item')])], 4), + listItem([bold([plain('Fifteenth item')])], 15), + ]), + ], + ], + [ + '[9gag](https://9gag.com/)', + [paragraph([link('https://9gag.com/', [plain('9gag')])])], + ], + ['[9gag](9gag.com)', [paragraph([link('9gag.com', [plain('9gag')])])]], + ['<9gag.com|9gag>', [paragraph([link('9gag.com', [plain('9gag')])])]], + ['9gag.com', [paragraph([link('//9gag.com', [plain('9gag.com')])])]], + [ + '[notes link](notes://Server/C3257116002CAD60/0/CCAF6BE2824A1F49432588D2001FA73E)', + [ + paragraph([ + link( + 'notes://Server/C3257116002CAD60/0/CCAF6BE2824A1F49432588D2001FA73E', + [plain('notes link')] + ), + ]), + ], + ], + [ + '[File Path](C:/Users/user1/Documents/projects/file.js)', + [ + paragraph([ + link('C:/Users/user1/Documents/projects/file.js', [plain('File Path')]), + ]), + ], + ], + [ + '[Test with **bold** element](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351', + [plain('Test with '), bold([plain('bold')]), plain(' element')] + ), + ]), + ], + ], + [ + '[Test with *bold* element](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351', + [plain('Test with '), bold([plain('bold')]), plain(' element')] + ), + ]), + ], + ], + [ + '[Test with _italic_ element](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351', + [plain('Test with '), italic([plain('italic')]), plain(' element')] + ), + ]), + ], + ], + [ + '[Test with ~strike~ element](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351', + [plain('Test with '), strike([plain('strike')]), plain(' element')] + ), + ]), + ], + ], + [ + '[Test with __**~~title~~**__ element](https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351)', + [ + paragraph([ + link( + 'https://desk.rocket.chat/support/rocketchat/ShowHomePage.do#Cases/dv/413244000073043351', + [ + plain('Test with '), + italic([bold([strike([plain('title')])])]), + plain(' element'), + ] + ), + ]), + ], + ], + [ + '([Github Issue: #24929](https://github.com/RocketChat/Rocket.Chat/issues/24929))', + [ + paragraph([ + plain('('), + link('https://github.com/RocketChat/Rocket.Chat/issues/24929', [ + plain('Github Issue: #24929'), + ]), + plain(')'), + ]), + ], + ], + [ + 'the [audio_url and video_url for post message attachments](https://developer.rocket.chat/reference/api/rest-api/endpoints/core-endpoints/chat-endpoints/postmessage)', + [ + paragraph([ + plain('the '), + link( + 'https://developer.rocket.chat/reference/api/rest-api/endpoints/core-endpoints/chat-endpoints/postmessage', + [plain('audio_url and video_url for post message attachments')] + ), + ]), + ], + ], + [ + 'the [Jira [Task] parentheses not working](rocket.chat)', + [ + paragraph([ + plain('the '), + link('rocket.chat', [plain('Jira [Task] parentheses not working')]), + ]), + ], + ], + [ + 'the [Jira (Task) parentheses not working](rocket.chat)', + [ + paragraph([ + plain('the '), + link('rocket.chat', [plain('Jira (Task) parentheses not working')]), + ]), + ], + ], + [ + '[Jira [Task] parentheses not working](rocket.chat)', + [ + paragraph([ + link('rocket.chat', [plain('Jira [Task] parentheses not working')]), + ]), + ], + ], + [ + '[Jira (Task) parentheses not working](rocket.chat)', + [ + paragraph([ + link('rocket.chat', [plain('Jira (Task) parentheses not working')]), + ]), + ], + ], + // Should not parse as link + ['77.77%', [paragraph([plain('77.77%')])]], + ['77.77', [paragraph([plain('77.77')])]], + ['https://77.77', [paragraph([plain('https://77.77')])]], + ['test.9gag', [paragraph([plain('test.9gag')])]], + [ + '[here](https://github.com/RocketChat/Rocket.Chat/releases/tag/6.0.0-rc.3)', + [ + paragraph([ + link( + 'https://github.com/RocketChat/Rocket.Chat/releases/tag/6.0.0-rc.3', + [plain('here')] + ), + ]), + ], + ], + [ + '[ ~ [ ~ [ ~ [ ~ [ ~ [ ~ [ ~ [ ~ [ ~ [ ~ [ ~ [ ~ [ ~ [ ~ [test](https://rocket.chat)', + [ + paragraph([ + link('https://rocket.chat', [ + plain(' '), + strike([plain(' [ ')]), + plain(' [ '), + strike([plain(' [ ')]), + plain(' [ '), + strike([plain(' [ ')]), + plain(' [ '), + strike([plain(' [ ')]), + plain(' [ '), + strike([plain(' [ ')]), + plain(' [ '), + strike([plain(' [ ')]), + plain(' [ '), + strike([plain(' [ ')]), + plain(' [test'), + ]), + ]), + ], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/mention.test.ts b/packages/message-parser/tests/mention.test.ts new file mode 100644 index 000000000000..5a86d5587acc --- /dev/null +++ b/packages/message-parser/tests/mention.test.ts @@ -0,0 +1,34 @@ +import { parse } from '../src'; +import { paragraph, plain, mentionUser, mentionChannel } from '../src/utils'; + +test.each([ + ['@guilherme.gazzo', [paragraph([mentionUser('guilherme.gazzo')])]], + [ + '@guilherme.gazzo. ', + [paragraph([mentionUser('guilherme.gazzo.'), plain(' ')])], + ], + ['#GENERAL', [paragraph([mentionChannel('GENERAL')])]], + ['@user:server.com', [paragraph([mentionUser('user:server.com')])]], + [ + '@marcos.defendi:matrix.org', + [paragraph([mentionUser('marcos.defendi:matrix.org')])], + ], + ['@username@example.com', [paragraph([mentionUser('username@example.com')])]], + [ + '@099fnd2ee@example.com', + [paragraph([mentionUser('099fnd2ee@example.com')])], + ], + ['@téstãçâò', [paragraph([mentionUser('téstãçâò')])]], + ['@สมชาย', [paragraph([mentionUser('สมชาย')])]], + ['@李祖阳', [paragraph([mentionUser('李祖阳')])]], + ['@あおい', [paragraph([mentionUser('あおい')])]], + ['@アオイ', [paragraph([mentionUser('アオイ')])]], + ['@Владимир', [paragraph([mentionUser('Владимир')])]], + ['@Кириллица', [paragraph([mentionUser('Кириллица')])]], + [ + 'test @Кириллица test', + [paragraph([plain('test '), mentionUser('Кириллица'), plain(' test')])], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/orderedList.test.ts b/packages/message-parser/tests/orderedList.test.ts new file mode 100644 index 000000000000..2939c4842e4b --- /dev/null +++ b/packages/message-parser/tests/orderedList.test.ts @@ -0,0 +1,25 @@ +import { parse } from '../src'; +import { bold, plain, orderedList, listItem } from '../src/utils'; + +test.each([ + [ + ` +7. First item +2. Second item +8. Third item +4. *Fourth item* +15. *Fifteenth item* +`.trim(), + [ + orderedList([ + listItem([plain('First item')], 7), + listItem([plain('Second item')], 2), + listItem([plain('Third item')], 8), + listItem([bold([plain('Fourth item')])], 4), + listItem([bold([plain('Fifteenth item')])], 15), + ]), + ], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/phoneNumber.test.ts b/packages/message-parser/tests/phoneNumber.test.ts new file mode 100644 index 000000000000..781bae3fcfe2 --- /dev/null +++ b/packages/message-parser/tests/phoneNumber.test.ts @@ -0,0 +1,63 @@ +import { parse } from '../src'; +import { link, paragraph, plain, bold } from '../src/utils'; + +test.each([ + [ + '+07563546725', + [paragraph([link('tel:07563546725', [plain('+07563546725')])])], + ], + [ + '+075-63546725', + [paragraph([link('tel:07563546725', [plain('+075-63546725')])])], + ], + [ + '+(075)-63546725', + [paragraph([link('tel:07563546725', [plain('+(075)-63546725')])])], + ], + [ + '+(075)63546725', + [paragraph([link('tel:07563546725', [plain('+(075)63546725')])])], + ], + [ + '[here](+(075)63546725)', + [paragraph([link('tel:07563546725', [plain('here')])])], + ], + [ + '[**here**](+(075)63546725)', + [paragraph([link('tel:07563546725', [bold([plain('here')])])])], + ], + [ + '[**here**](+(075)63546725)', + [paragraph([link('tel:07563546725', [bold([plain('here')])])])], + ], + [ + '+(11)99999-9999', + [paragraph([link('tel:11999999999', [plain('+(11)99999-9999')])])], + ], + [ + '5 +51231', + [paragraph([plain('5 '), link('tel:51231', [plain('+51231')])])], + ], + [ + '5 +51231 5', + [ + paragraph([ + plain('5 '), + link('tel:51231', [plain('+51231')]), + plain(' 5'), + ]), + ], + ], + ['+(12)3-45', [paragraph([link('tel:12345', [plain('+(12)3-45')])])]], + ['+1.599123', [paragraph([plain('+1.599123')])]], + ['1+1=2', [paragraph([plain('1+1=2')])]], + ['1+1=2 text', [paragraph([plain('1+1=2 text')])]], + ['+1000,00', [paragraph([plain('+1000,00')])]], + ['+ 1199999999', [paragraph([plain('+ 1199999999')])]], + ['+1234', [paragraph([plain('+1234')])]], + ['+(12)3-4', [paragraph([plain('+(12)3-4')])]], + ['+123-4', [paragraph([plain('+123-4')])]], + ['5+51231', [paragraph([plain('5+51231')])]], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/strikethrough.test.ts b/packages/message-parser/tests/strikethrough.test.ts new file mode 100644 index 000000000000..36b3439168f5 --- /dev/null +++ b/packages/message-parser/tests/strikethrough.test.ts @@ -0,0 +1,122 @@ +import { parse } from '../src'; +import { + emoji, + emojiUnicode, + link, + mentionChannel, + mentionUser, + paragraph, + plain, + strike, +} from '../src/utils'; + +test.each([ + ['~:smile:~', [paragraph([strike([emoji('smile')])])]], + [ + '~test :smile: test~', + [paragraph([strike([plain('test '), emoji('smile'), plain(' test')])])], + ], + ['~😀~', [paragraph([strike([emojiUnicode('😀')])])]], + ['~test 😀~', [paragraph([strike([plain('test '), emojiUnicode('😀')])])]], + [ + '~@guilherme.gazzo~', + [paragraph([strike([mentionUser('guilherme.gazzo')])])], + ], + ['~#GENERAL~', [paragraph([strike([mentionChannel('GENERAL')])])]], + [ + '~test @guilherme.gazzo~', + [paragraph([strike([plain('test '), mentionUser('guilherme.gazzo')])])], + ], + [ + '~test #GENERAL~', + [paragraph([strike([plain('test '), mentionChannel('GENERAL')])])], + ], + [ + '~~[A brand new Gist](https://gist.github.com/24dddfa97bef58f46ac2ce0f80c58ba4)~~', + [ + paragraph([ + strike([ + link('https://gist.github.com/24dddfa97bef58f46ac2ce0f80c58ba4', [ + plain('A brand new Gist'), + ]), + ]), + ]), + ], + ], + ['~~strike~~', [paragraph([strike([plain('strike')])])]], + [ + 'pre~~strike~~post', + [paragraph([plain('pre'), strike([plain('strike')]), plain('post')])], + ], + [ + ' pre~~strike~~post ', + [paragraph([plain(' pre'), strike([plain('strike')]), plain('post ')])], + ], + ['~~', [paragraph([plain('~~')])]], + ['~ ~', [paragraph([plain('~ ~')])]], + ['~~ ~', [paragraph([plain('~~ ~')])]], + ['~~ ~~', [paragraph([plain('~~ ~~')])]], + ['~ Hello~', [paragraph([strike([plain(' Hello')])])]], + ['~Hello ~', [paragraph([strike([plain('Hello ')])])]], + [ + ':custom~emoji~case:', + [paragraph([plain(`:custom`), strike([plain('emoji')]), plain(`case:`)])], + ], + [ + 'text~hello~text', + [paragraph([plain(`text`), strike([plain('hello')]), plain(`text`)])], + ], + ['~hello~text', [paragraph([strike([plain('hello')]), plain(`text`)])]], + ['text~hello~', [paragraph([plain(`text`), strike([plain('hello')])])]], + ['~Hel lo~', [paragraph([strike([plain('Hel lo')])])]], + ['~Hello~', [paragraph([strike([plain('Hello')])])]], + ['~~Hello~~', [paragraph([strike([plain('Hello')])])]], + ['~~Hello~', [paragraph([plain(`~`), strike([plain('Hello')])])]], + ['~Hello~~', [paragraph([strike([plain('Hello')]), plain(`~`)])]], + ['~Hello', [paragraph([plain('~Hello')])]], + ['Hello~', [paragraph([plain('Hello~')])]], + ['He~llo', [paragraph([plain('He~llo')])]], + [ + '~~~Hello~~~', + [paragraph([plain(`~`), strike([plain('Hello')]), plain(`~`)])], + ], + ['~~~Hello~~', [paragraph([plain(`~`), strike([plain('Hello')])])]], + [ + '~Hello~ this is dog', + [paragraph([strike([plain('Hello')]), plain(` this is dog`)])], + ], + [ + 'Rocket cat says ~Hello~', + [paragraph([plain(`Rocket cat says `), strike([plain('Hello')])])], + ], + [ + 'He said ~Hello~ to her', + [ + paragraph([ + plain(`He said `), + strike([plain('Hello')]), + plain(` to her`), + ]), + ], + ], + [ + '~~Hello~~ this is dog', + [paragraph([strike([plain('Hello')]), plain(` this is dog`)])], + ], + [ + 'Rocket cat says ~~Hello~~', + [paragraph([plain(`Rocket cat says `), strike([plain('Hello')])])], + ], + [ + 'He said ~~Hello~~ to her', + [ + paragraph([ + plain(`He said `), + strike([plain('Hello')]), + plain(` to her`), + ]), + ], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/strongEmphasis.test.ts b/packages/message-parser/tests/strongEmphasis.test.ts new file mode 100644 index 000000000000..8384030aa11b --- /dev/null +++ b/packages/message-parser/tests/strongEmphasis.test.ts @@ -0,0 +1,156 @@ +import { parse } from '../src'; +import { + bold, + link, + paragraph, + plain, + italic, + strike, + emoji, + emojiUnicode, + mentionChannel, + mentionUser, +} from '../src/utils'; + +test.each([ + ['*:smile:*', [paragraph([bold([emoji('smile')])])]], + [ + '*test :smile: test*', + [paragraph([bold([plain('test '), emoji('smile'), plain(' test')])])], + ], + ['*😀*', [paragraph([bold([emojiUnicode('😀')])])]], + ['*test 😀*', [paragraph([bold([plain('test '), emojiUnicode('😀')])])]], + ['*@guilherme.gazzo*', [paragraph([bold([mentionUser('guilherme.gazzo')])])]], + ['*#GENERAL*', [paragraph([bold([mentionChannel('GENERAL')])])]], + [ + '*test @guilherme.gazzo*', + [paragraph([bold([plain('test '), mentionUser('guilherme.gazzo')])])], + ], + [ + '*test #GENERAL*', + [paragraph([bold([plain('test '), mentionChannel('GENERAL')])])], + ], + [ + '*[A brand new Gist](https://gist.github.com/24dddfa97bef58f46ac2ce0f80c58ba4)*', + [ + paragraph([ + bold([ + link('https://gist.github.com/24dddfa97bef58f46ac2ce0f80c58ba4', [ + plain('A brand new Gist'), + ]), + ]), + ]), + ], + ], + ['**bold**', [paragraph([bold([plain('bold')])])]], + ['** bold**', [paragraph([bold([plain(' bold')])])]], + ['** bold **', [paragraph([bold([plain(' bold ')])])]], + ['** bo ld **', [paragraph([bold([plain(' bo ld ')])])]], + ['pre**bold**', [paragraph([plain('pre'), bold([plain('bold')])])]], + ['**bold**pos', [paragraph([bold([plain('bold')]), plain('pos')])]], + [ + '**bold****bold**', + [paragraph([bold([plain('bold')]), bold([plain('bold')])])], + ], + [ + '**bold** **bold**', + [paragraph([bold([plain('bold')]), plain(' '), bold([plain('bold')])])], + ], + [ + '**bold** __italic__', + [paragraph([bold([plain('bold')]), plain(' '), italic([plain('italic')])])], + ], + ['**__italicbold__**', [paragraph([bold([italic([plain('italicbold')])])])]], + [ + 'plain **__italicbold__**', + [paragraph([plain('plain '), bold([italic([plain('italicbold')])])])], + ], + [ + 'plain **__~~strikeitalicbold~~__**', + [ + paragraph([ + plain('plain '), + bold([italic([strike([plain('strikeitalicbold')])])]), + ]), + ], + ], + ['**', [paragraph([plain('**')])]], + ['* *', [paragraph([plain('* *')])]], + ['** *', [paragraph([plain('** *')])]], + ['** **', [paragraph([plain('** **')])]], + ['** **', [paragraph([plain('** **')])]], + ['* Hello*', [paragraph([bold([plain(' Hello')])])]], + ['*Hello *', [paragraph([bold([plain('Hello ')])])]], + [ + ':custom*emoji*case:', + [paragraph([plain(':custom'), bold([plain('emoji')]), plain('case:')])], + ], + [ + 'text*hello*text', + [paragraph([plain('text'), bold([plain('hello')]), plain('text')])], + ], + ['*hello*text', [paragraph([bold([plain('hello')]), plain('text')])]], + ['text*hello*', [paragraph([plain('text'), bold([plain('hello')])])]], + ['*Hel lo*', [paragraph([bold([plain('Hel lo')])])]], + ['*Hello*', [paragraph([bold([plain('Hello')])])]], + ['**Hello*', [paragraph([plain('*'), bold([plain('Hello')])])]], + ['*Hello**', [paragraph([bold([plain('Hello')]), plain('*')])]], + ['*Hello', [paragraph([plain('*Hello')])]], + ['Hello*', [paragraph([plain('Hello*')])]], + ['He*llo', [paragraph([plain('He*llo')])]], + [ + '***Hello***', + [paragraph([plain('*'), bold([plain('Hello')]), plain('*')])], + ], + ['***Hello**', [paragraph([plain('*'), bold([plain('Hello')])])]], + [ + '*Hello* this is dog', + [paragraph([bold([plain('Hello')]), plain(' this is dog')])], + ], + [ + 'Rocket cat says *Hello*', + [paragraph([plain('Rocket cat says '), bold([plain('Hello')])])], + ], + [ + 'He said *Hello* to her', + [paragraph([plain('He said '), bold([plain('Hello')]), plain(' to her')])], + ], + [ + '**Hello** this is dog', + [paragraph([bold([plain('Hello')]), plain(' this is dog')])], + ], + [ + 'Rocket cat says **Hello**', + [paragraph([plain('Rocket cat says '), bold([plain('Hello')])])], + ], + [ + 'He said **Hello** to her', + [paragraph([plain('He said '), bold([plain('Hello')]), plain(' to her')])], + ], + [ + 'He was a**nn**oyed', + [paragraph([plain('He was a'), bold([plain('nn')]), plain('oyed')])], + ], + [ + 'There are two o in f*oo*tball', + [ + paragraph([ + plain('There are two o in f'), + bold([plain('oo')]), + plain('tball'), + ]), + ], + ], + ['*(teste*', [paragraph([bold([plain('(teste')])])]], + ['*(teste)*', [paragraph([bold([plain('(teste)')])])]], + [ + '*__~bolditalicstrike~_*', + [ + paragraph([ + bold([plain('_'), italic([strike([plain('bolditalicstrike')])])]), + ]), + ], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/tasks.test.ts b/packages/message-parser/tests/tasks.test.ts new file mode 100644 index 000000000000..ef6752bbfc0a --- /dev/null +++ b/packages/message-parser/tests/tasks.test.ts @@ -0,0 +1,49 @@ +import { parse } from '../src'; +import { + plain, + tasks, + task, + mentionUser, + mentionChannel, + link, + bold, +} from '../src/utils'; + +test.each([ + [ + ` +- [ ] this is an incomplete item +- [x] this is a complete item +- [x] @mentions, #refs, [links](http://localhost), **formatting** +- [x] list syntax required (any unordered or ordered list supported) +`.trim(), + [ + tasks([ + task([plain('this is an incomplete item')], false), + task([plain('this is a complete item')], true), + task( + [ + mentionUser('mentions'), + plain(', '), + mentionChannel('refs'), + plain(', '), + link('http://localhost', [plain('links')]), + plain(', '), + bold([plain('formatting')]), + ], + true + ), + task( + [ + plain( + 'list syntax required (any unordered or ordered list supported)' + ), + ], + true + ), + ]), + ], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/unorderedList.test.ts b/packages/message-parser/tests/unorderedList.test.ts new file mode 100644 index 000000000000..771e0689271b --- /dev/null +++ b/packages/message-parser/tests/unorderedList.test.ts @@ -0,0 +1,78 @@ +import { parse } from '../src'; +import { unorderedList, plain, listItem, bold } from '../src/utils'; + +test.each([ + [ + ` +- First item +- Second item +- Third item +- *Fourth item* +`.trim(), + [ + unorderedList([ + listItem([plain('First item')]), + listItem([plain('Second item')]), + listItem([plain('Third item')]), + listItem([bold([plain('Fourth item')])]), + ]), + ], + ], + [ + ` +* First item +* Second item +* Third item +* *Fourth item* +`.trim(), + [ + unorderedList([ + listItem([plain('First item')]), + listItem([plain('Second item')]), + listItem([plain('Third item')]), + listItem([bold([plain('Fourth item')])]), + ]), + ], + ], + + [ + ` +- First item +* Second item +* Third item +* *Fourth item* +`.trim(), + [ + unorderedList([listItem([plain('First item')])]), + unorderedList([ + listItem([plain('Second item')]), + listItem([plain('Third item')]), + listItem([bold([plain('Fourth item')])]), + ]), + ], + ], + // [ + // ` + // * First item + // * Second item + // * Third item + // * Indented item + // * Indented item + // * Fourth item + // `.trim(), + // [paragraph([])], + // ], + // [ + // ` + // - First item + // - Second item + // - Third item + // - Indented item + // - Indented item + // - Fourth item + // `.trim(), + // [paragraph([])], + // ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); diff --git a/packages/message-parser/tests/url.test.ts b/packages/message-parser/tests/url.test.ts new file mode 100644 index 000000000000..0c7c03d110d2 --- /dev/null +++ b/packages/message-parser/tests/url.test.ts @@ -0,0 +1,362 @@ +import { parse } from '../src'; +import { lineBreak, autoLink, paragraph, plain, link } from '../src/utils'; + +test.each([ + [ + 'https://pt.wikipedia.org/wiki/Condi%C3%A7%C3%A3o_de_corrida#:~:text=Uma%20condi%C3%A7%C3%A3o%20de%20corrida%20%C3%A9,sequ%C3%AAncia%20ou%20sincronia%20doutros%20eventos', + [ + paragraph([ + autoLink( + 'https://pt.wikipedia.org/wiki/Condi%C3%A7%C3%A3o_de_corrida#:~:text=Uma%20condi%C3%A7%C3%A3o%20de%20corrida%20%C3%A9,sequ%C3%AAncia%20ou%20sincronia%20doutros%20eventos' + ), + ]), + ], + ], + [ + 'https://pt.wikipedia.org/', + [paragraph([autoLink('https://pt.wikipedia.org/')])], + ], + [ + 'https://pt.wikipedia.org/with-hyphen', + [paragraph([autoLink('https://pt.wikipedia.org/with-hyphen')])], + ], + [ + 'https://pt.wikipedia.org/with_underscore', + [paragraph([autoLink('https://pt.wikipedia.org/with_underscore')])], + ], + [ + 'https://www.npmjs.com/package/@rocket.chat/message-parser', + [ + paragraph([ + autoLink('https://www.npmjs.com/package/@rocket.chat/message-parser'), + ]), + ], + ], + ['http:/rocket.chat/teste', [paragraph([plain('http:/rocket.chat/teste')])]], + ['https:/rocket.chat/', [paragraph([plain('https:/rocket.chat/')])]], + ['https://test', [paragraph([plain('https://test')])]], + [ + 'httpsss://rocket.chat/test', + [paragraph([autoLink('httpsss://rocket.chat/test')])], + ], + [ + 'https://rocket.chat:3000/test', + [paragraph([autoLink('https://rocket.chat:3000/test')])], + ], + [ + 'https://rocket.chat/test?search', + [paragraph([autoLink('https://rocket.chat/test?search')])], + ], + [ + 'https://rocket.chat/test?search=test', + [paragraph([autoLink('https://rocket.chat/test?search=test')])], + ], + ['https://rocket.chat', [paragraph([autoLink('https://rocket.chat')])]], + ['https://localhost', [paragraph([autoLink('https://localhost')])]], + ['https://localhost:3000', [paragraph([autoLink('https://localhost:3000')])]], + [ + 'https://localhost:3000#fragment', + [paragraph([autoLink('https://localhost:3000#fragment')])], + ], + [ + 'https://localhost:3000#', + [paragraph([autoLink('https://localhost:3000#')])], + ], + [ + 'https://localhost:3000?', + [paragraph([autoLink('https://localhost:3000?')])], + ], + [ + 'https://localhost:3000/', + [paragraph([autoLink('https://localhost:3000/')])], + ], + [ + 'ftp://user:pass@localhost:21/etc/hosts', + [paragraph([autoLink('ftp://user:pass@localhost:21/etc/hosts')])], + ], + ['ssh://test@example.com', [paragraph([autoLink('ssh://test@example.com')])]], + [ + 'custom://test@example.com', + [paragraph([autoLink('custom://test@example.com')])], + ], + ['ftp://example.com', [paragraph([autoLink('ftp://example.com')])]], + [ + 'https://www.thingiverse.com/thing:5451684', + [paragraph([autoLink('https://www.thingiverse.com/thing:5451684')])], + ], + ['http://📙.la/❤️', [paragraph([autoLink('http://📙.la/❤️')])]], + [ + 'https://developer.rocket.chat/reference/api/rest-api#production-security-concerns look at this', + [ + paragraph([ + autoLink( + 'https://developer.rocket.chat/reference/api/rest-api#production-security-concerns' + ), + plain(' look at this'), + ]), + ], + ], + [ + 'https://developer.rocket.chat/reference/api/rest-api look at this', + [ + paragraph([ + autoLink('https://developer.rocket.chat/reference/api/rest-api'), + plain(' look at this'), + ]), + ], + ], + + [ + 'https://developer.rocket.chat/reference/api/rest-api#fragment?query=query look at this', + [ + paragraph([ + autoLink( + 'https://developer.rocket.chat/reference/api/rest-api#fragment?query=query' + ), + plain(' look at this'), + ]), + ], + ], + [ + 'https://developer.rocket.chat look at this', + [ + paragraph([ + autoLink('https://developer.rocket.chat'), + plain(' look at this'), + ]), + ], + ], + [ + 'https://developer.rocket.chat?query=query look at this', + [ + paragraph([ + autoLink('https://developer.rocket.chat?query=query'), + plain(' look at this'), + ]), + ], + ], + [ + 'https://developer.rocket.chat?query=query\nline break', + [ + paragraph([autoLink('https://developer.rocket.chat?query=query')]), + paragraph([plain('line break')]), + ], + ], + [ + 'https://developer.rocket.chat?query=query\n\nline break', + [ + paragraph([autoLink('https://developer.rocket.chat?query=query')]), + lineBreak(), + paragraph([plain('line break')]), + ], + ], + [ + 'https://developer.rocket.chat?query=query_with_underscore look at this', + [ + paragraph([ + autoLink('https://developer.rocket.chat?query=query_with_underscore'), + plain(' look at this'), + ]), + ], + ], + [ + 'https://developer.rocket.chat/path_with_underscore look at this', + [ + paragraph([ + autoLink('https://developer.rocket.chat/path_with_underscore'), + plain(' look at this'), + ]), + ], + ], + [ + 'https://developer.rocket.chat#fragment_with_underscore look at this', + [ + paragraph([ + autoLink('https://developer.rocket.chat#fragment_with_underscore'), + plain(' look at this'), + ]), + ], + ], + [ + 'https://developer.rocket.chat followed by text', + [ + paragraph([ + autoLink('https://developer.rocket.chat'), + plain(' followed by text'), + ]), + ], + ], + [ + 'two urls https://developer.rocket.chat , https://rocket.chat', + [ + paragraph([ + plain('two urls '), + autoLink('https://developer.rocket.chat'), + plain(' , '), + autoLink('https://rocket.chat'), + ]), + ], + ], + [ + 'https://1developer.rocket.chat', + [paragraph([autoLink('https://1developer.rocket.chat')])], + ], + [ + 'https://en.m.wikipedia.org/wiki/Main_Page', + [paragraph([autoLink('https://en.m.wikipedia.org/wiki/Main_Page')])], + ], + ['test.1test.com', [paragraph([autoLink('test.1test.com')])]], + [ + 'http://test.e-xample.com', + [paragraph([autoLink('http://test.e-xample.com')])], + ], + ['www.n-tv.de', [paragraph([autoLink('www.n-tv.de')])]], + [ + 'www.n-tv.de/test, test', + [paragraph([autoLink('www.n-tv.de/test'), plain(', test')])], + ], + [ + 'www.n-tv.de/, test', + [paragraph([autoLink('www.n-tv.de/'), plain(', test')])], + ], + [ + 'www.n-tv.de, test', + [paragraph([autoLink('www.n-tv.de'), plain(', test')])], + ], + [ + 'https://www.n-tv.de, test', + [paragraph([autoLink('https://www.n-tv.de'), plain(', test')])], + ], + ['http://te_st.com', [paragraph([autoLink('http://te_st.com')])]], + ['www.te_st.com', [paragraph([autoLink('www.te_st.com')])]], + [ + '[google_search](http://google.com)', + [paragraph([link('http://google.com', [plain('google_search')])])], + ], + [ + 'app...https://rocket.chat https://rocket.chat', + [ + paragraph([ + plain('app...https://rocket.chat '), + autoLink('https://rocket.chat'), + ]), + ], + ], + [ + 'Hey check it out the best communication platform https://rocket.chat! There is not discussion about it.', + [ + paragraph([ + plain('Hey check it out the best communication platform '), + autoLink('https://rocket.chat'), + plain('! There is not discussion about it.'), + ]), + ], + ], + [ + 'This is a normal phrase.This in another phrase.', + [paragraph([plain('This is a normal phrase.This in another phrase.')])], + ], + [ + 'https://github.com/RocketChat/Rocket.Chat/releases/tag/6.0.0-rc.3', + [ + paragraph([ + autoLink( + 'https://github.com/RocketChat/Rocket.Chat/releases/tag/6.0.0-rc.3' + ), + ]), + ], + ], + [ + 'https://www.rocket.chat/(W(601))/Main?ScreenId=GI000027', + [ + paragraph([ + autoLink('https://www.rocket.chat/(W(601))/Main?ScreenId=GI000027'), + ]), + ], + ], + [ + 'https://rocketchat.atlassian.net/browse/OC-718?filter=10078&jql=%22Defect%20from%5BVersion%20Picker%20(multiple%20versions)%5D%22%20%3D%206.0.0%20AND%20%22Defect%20from%5BVersion%20Picker%20(multiple%20versions)%5D%22%20%3D%206.0.0%20AND%20created%20%3E%3D%20-48h%20ORDER%20BY%20cf%5B10070%5D%20ASC%2C%20status%20ASC%2C%20created%20DESC', + [ + paragraph([ + link( + 'https://rocketchat.atlassian.net/browse/OC-718?filter=10078&jql=%22Defect%20from%5BVersion%20Picker%20(multiple%20versions)%5D%22%20%3D%206.0.0%20AND%20%22Defect%20from%5BVersion%20Picker%20(multiple%20versions)%5D%22%20%3D%206.0.0%20AND%20created%20%3E%3D%20-48h%20ORDER%20BY%20cf%5B10070%5D%20ASC%2C%20status%20ASC%2C%20created%20DESC' + ), + ]), + ], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); + +describe('autoLink with custom hosts settings comming from Rocket.Chat', () => { + test.each([ + [ + 'http://gitlab.local', + [paragraph([autoLink('http://gitlab.local', ['local'])])], + ], + ['gitlab.local', [paragraph([autoLink('gitlab.local', ['local'])])]], + [ + 'internaltool.intranet', + [paragraph([autoLink('internaltool.intranet', ['local', 'intranet'])])], + ], + ])('parses %p', (input, output) => { + expect( + parse(input, { customDomains: ['local', 'intranet'] }) + ).toMatchObject(output); + }); +}); + +describe('autoLink WITHOUT custom hosts settings comming from Rocket.Chat', () => { + test.each([ + [ + 'https://internaltool.testt', + [paragraph([plain('https://internaltool.testt')])], + ], + ])('parses %p', (input, output) => { + expect(parse(input, { customDomains: ['local'] })).toMatchObject(output); + }); +}); + +describe('autoLink helper function', () => { + it('should preserve the original protocol if the protocol is http or https', () => { + expect(autoLink('https://rocket.chat/test')).toMatchObject( + link('https://rocket.chat/test') + ); + + expect(autoLink('http://rocket.chat/test')).toMatchObject( + link('http://rocket.chat/test') + ); + }); + + it('should preserve the original protocol even if for custom protocols', () => { + expect(autoLink('custom://rocket.chat/test')).toMatchObject( + link('custom://rocket.chat/test') + ); + }); + + it('should return // as the protocol if // is the protocol specified', () => { + expect(autoLink('//rocket.chat/test')).toMatchObject( + link('//rocket.chat/test') + ); + }); + + it("should return an url concatenated '//' if the url has no protocol", () => { + expect(autoLink('rocket.chat/test')).toMatchObject( + link('//rocket.chat/test', [plain('rocket.chat/test')]) + ); + }); + + it("should return an url concatenated '//' if the url has no protocol and has sub-domain", () => { + expect(autoLink('spark-public.s3.amazonaws.com')).toMatchObject( + link('//spark-public.s3.amazonaws.com', [ + plain('spark-public.s3.amazonaws.com'), + ]) + ); + }); + + it("should return an plain text url due to invalid TLD that's validate with the external library TLDTS", () => { + expect(autoLink('rocket.chattt/url_path')).toMatchObject( + plain('rocket.chattt/url_path') + ); + }); +}); diff --git a/packages/message-parser/tsconfig-bundle.json b/packages/message-parser/tsconfig-bundle.json new file mode 100644 index 000000000000..77caa2b8f6cd --- /dev/null +++ b/packages/message-parser/tsconfig-bundle.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "./src" + }, + "include": ["./src"] +} diff --git a/packages/message-parser/tsconfig.json b/packages/message-parser/tsconfig.json new file mode 100644 index 000000000000..3e59aeae898f --- /dev/null +++ b/packages/message-parser/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "rootDir": ".", + "target": "es5", + "module": "ESNext", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "noImplicitThis": true, + "alwaysStrict": true + } +} diff --git a/packages/message-parser/typedoc.json b/packages/message-parser/typedoc.json new file mode 100644 index 000000000000..af0fd90acfc7 --- /dev/null +++ b/packages/message-parser/typedoc.json @@ -0,0 +1,4 @@ +{ + "entryPoints": ["src/index.ts"], + "out": "../../static/message-parser" +} diff --git a/packages/message-parser/webpack.config.js b/packages/message-parser/webpack.config.js new file mode 100644 index 000000000000..1bae39ea98ac --- /dev/null +++ b/packages/message-parser/webpack.config.js @@ -0,0 +1,86 @@ +const path = require('path'); + +const config = (outputDeclarations = false) => ({ + entry: './src/index.ts', + module: { + rules: [ + { + test: /\.ts$/, + use: { + loader: 'ts-loader', + options: { + configFile: path.resolve(__dirname, './tsconfig-bundle.json'), + ...(!outputDeclarations && { + compilerOptions: { + declaration: false, + declarationMap: undefined, + }, + }), + }, + }, + include: [path.resolve(__dirname, './src')], + exclude: [path.resolve(__dirname, './tests')], + }, + { + test: /\.pegjs$/, + use: ['babel-loader', '@rocket.chat/peggy-loader'], + }, + ], + }, + resolve: { + extensions: ['.ts', '.js', '.pegjs'], + }, +}); + +module.exports = [ + { + ...config(), + mode: 'development', + output: { + path: path.resolve(__dirname, './dist'), + filename: 'messageParser.development.js', + library: { + type: 'commonjs2', + }, + }, + }, + { + ...config(), + mode: 'production', + output: { + path: path.resolve(__dirname, './dist'), + filename: 'messageParser.production.js', + library: { + type: 'commonjs2', + }, + }, + }, + { + ...config(), + mode: 'production', + experiments: { + outputModule: true, + }, + output: { + path: path.resolve(__dirname, './dist'), + filename: 'messageParser.mjs', + library: { + type: 'module', + }, + }, + }, + { + ...config(true), + mode: 'production', + output: { + path: path.resolve(__dirname, './dist'), + filename: 'messageParser.umd.js', + library: { + type: 'umd', + name: 'RocketChatMessageParser', + umdNamedDefine: true, + }, + globalObject: 'this', + }, + }, +]; diff --git a/packages/peggy-loader/.eslintignore b/packages/peggy-loader/.eslintignore new file mode 100644 index 000000000000..8225baa4a77d --- /dev/null +++ b/packages/peggy-loader/.eslintignore @@ -0,0 +1,2 @@ +/node_modules +/dist diff --git a/packages/peggy-loader/.eslintrc.json b/packages/peggy-loader/.eslintrc.json new file mode 100644 index 000000000000..6a4609388dd8 --- /dev/null +++ b/packages/peggy-loader/.eslintrc.json @@ -0,0 +1,8 @@ +{ + "extends": ["@rocket.chat/eslint-config"], + "plugins": ["jest"], + "env": { + "jest/globals": true + }, + "ignorePatterns": ["**/dist"] +} diff --git a/packages/peggy-loader/.gitignore b/packages/peggy-loader/.gitignore new file mode 100644 index 000000000000..9b1c8b133c96 --- /dev/null +++ b/packages/peggy-loader/.gitignore @@ -0,0 +1 @@ +/dist diff --git a/packages/peggy-loader/.prettierignore b/packages/peggy-loader/.prettierignore new file mode 100644 index 000000000000..8225baa4a77d --- /dev/null +++ b/packages/peggy-loader/.prettierignore @@ -0,0 +1,2 @@ +/node_modules +/dist diff --git a/packages/peggy-loader/.prettierrc.js b/packages/peggy-loader/.prettierrc.js new file mode 100644 index 000000000000..b57f474edb93 --- /dev/null +++ b/packages/peggy-loader/.prettierrc.js @@ -0,0 +1 @@ +module.exports = require('@rocket.chat/prettier-config/fuselage'); diff --git a/packages/peggy-loader/CHANGELOG.md b/packages/peggy-loader/CHANGELOG.md new file mode 100644 index 000000000000..0c1ace99d7d4 --- /dev/null +++ b/packages/peggy-loader/CHANGELOG.md @@ -0,0 +1,43 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.31.0](https://github.com/RocketChat/fuselage/compare/v0.30.1...v0.31.0) (2021-12-28) + +### Features + +- New hooks for element size tracking ([#413](https://github.com/RocketChat/fuselage/issues/413)) ([8ca682c](https://github.com/RocketChat/fuselage/commit/8ca682c636d2e4813f7d346cb881513382be63cf)) + +# [0.30.0](https://github.com/RocketChat/fuselage/compare/v0.29.0...v0.30.0) (2021-10-06) + +### Bug Fixes + +- **jest:** Adjust jest and ts-jest dependencies ([#547](https://github.com/RocketChat/fuselage/issues/547)) ([91a4fa1](https://github.com/RocketChat/fuselage/commit/91a4fa1365394001afe1bd46480bda3bafed5505)) + +# [0.29.0](https://github.com/RocketChat/fuselage/compare/v0.28.0...v0.29.0) (2021-08-31) + +**Note:** Version bump only for package @rocket.chat/peggy-loader + +# [0.28.0](https://github.com/RocketChat/fuselage/compare/v0.27.0...v0.28.0) (2021-07-30) + +### Features + +- **onboarding-ui:** Administrator information form and Organization information form ([#489](https://github.com/RocketChat/fuselage/issues/489)) ([b289f68](https://github.com/RocketChat/fuselage/commit/b289f68676954b91c792d8d97680314178bf2c60)) +- styled API; monorepo grooming ([#482](https://github.com/RocketChat/fuselage/issues/482)) ([1b6b70c](https://github.com/RocketChat/fuselage/commit/1b6b70cf67ec16927b1566adc2350295a8927223)) + +# [0.27.0](https://github.com/RocketChat/fuselage/compare/v0.26.0...v0.27.0) (2021-06-28) + +**Note:** Version bump only for package @rocket.chat/peggy-loader + +# [0.26.0](https://github.com/RocketChat/fuselage/compare/v0.25.0...v0.26.0) (2021-05-28) + +### Bug Fixes + +- Peggy loader options ([#459](https://github.com/RocketChat/fuselage/issues/459)) ([fc91054](https://github.com/RocketChat/fuselage/commit/fc91054abeb340718596b0c8f4ce8e14c87574af)) + +# [0.25.0](https://github.com/RocketChat/fuselage/compare/v0.24.0...v0.25.0) (2021-05-19) + +### Features + +- Peggy loader ([#450](https://github.com/RocketChat/fuselage/issues/450)) ([0496cad](https://github.com/RocketChat/fuselage/commit/0496cad457d76f8a4d6a217209e4a55e315e8365)) diff --git a/packages/peggy-loader/README.md b/packages/peggy-loader/README.md new file mode 100644 index 000000000000..4d2cf606c2df --- /dev/null +++ b/packages/peggy-loader/README.md @@ -0,0 +1,89 @@ + + +

          + + Rocket.Chat + +

          + +# `@rocket.chat/peggy-loader` + +> Peggy loader for webpack + +--- + +[![npm@latest](https://img.shields.io/npm/v/@rocket.chat/peggy-loader/latest?style=flat-square)](https://www.npmjs.com/package/@rocket.chat/peggy-loader/v/latest) [![npm@next](https://img.shields.io/npm/v/@rocket.chat/peggy-loader/next?style=flat-square)](https://www.npmjs.com/package/@rocket.chat/peggy-loader/v/next) ![npm downloads](https://img.shields.io/npm/dw/@rocket.chat/peggy-loader?style=flat-square) ![License: MIT](https://img.shields.io/npm/l/@rocket.chat/peggy-loader?style=flat-square) + +![deps](https://img.shields.io/librariesio/release/npm/@rocket.chat/peggy-loader?style=flat-square) ![npm bundle size](https://img.shields.io/bundlephobia/min/@rocket.chat/peggy-loader?style=flat-square) + + + +## Install + + + +Firstly, install the peer dependencies (prerequisites): + +```sh +npm i peggy webpack + +# or, if you are using yarn: + +yarn add peggy webpack +``` + +Add `@rocket.chat/peggy-loader` as a dependency: + +```sh +npm i @rocket.chat/peggy-loader + +# or, if you are using yarn: + +yarn add @rocket.chat/peggy-loader +``` + + + +## Contributing + + + +Contributions, issues, and feature requests are welcome!
          +Feel free to check the [issues](https://github.com/RocketChat/fuselage/issues). + + + +### Building + +As this package dependends on others in this monorepo, before anything run the following at the root directory: + + + +```sh +yarn build +``` + + + +### Linting + +To ensure the source is matching our coding style, we perform [linting](). +Before commiting, check if your code fits our style by running: + + + +```sh +yarn lint +``` + + + +Some linter warnings and errors can be automatically fixed: + + + +```sh +yarn lint-and-fix +``` + + diff --git a/packages/peggy-loader/jest.config.js b/packages/peggy-loader/jest.config.js new file mode 100644 index 000000000000..21526c604f6b --- /dev/null +++ b/packages/peggy-loader/jest.config.js @@ -0,0 +1,5 @@ +module.exports = { + preset: 'ts-jest', + errorOnDeprecated: true, + testMatch: ['/src/**/*.spec.[jt]s?(x)'], +}; diff --git a/packages/peggy-loader/package.json b/packages/peggy-loader/package.json new file mode 100644 index 000000000000..eaa775d7c995 --- /dev/null +++ b/packages/peggy-loader/package.json @@ -0,0 +1,57 @@ +{ + "name": "@rocket.chat/peggy-loader", + "version": "0.31.25", + "description": "Peggy loader for webpack", + "keywords": [ + "peggy", + "loader", + "webpack" + ], + "author": { + "name": "Rocket.Chat", + "url": "https://rocket.chat/" + }, + "homepage": "https://github.com/RocketChat/fuselage#readme", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/RocketChat/fuselage.git", + "directory": "packages/peggy-loader" + }, + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", + "types": "dist/esm/index.d.ts", + "files": [ + "/dist" + ], + "scripts": { + "build": "run-s .:build:clean .:build:esm .:build:cjs", + ".:build:clean": "rimraf dist", + ".:build:esm": "tsc -p tsconfig.json", + ".:build:cjs": "tsc -p tsconfig-cjs.json", + "lint": "eslint src" + }, + "bugs": { + "url": "https://github.com/RocketChat/fuselage/issues" + }, + "peerDependencies": { + "peggy": "*", + "webpack": "*" + }, + "devDependencies": { + "@rocket.chat/eslint-config": "workspace:~", + "@rocket.chat/prettier-config": "~0.31.25", + "@types/node": "~14.18.42", + "eslint": "~8.45.0", + "npm-run-all": "^4.1.5", + "peggy": "3.0.2", + "prettier": "~2.8.7", + "rimraf": "^3.0.2", + "ts-jest": "~29.1.0", + "typescript": "~5.0.4", + "webpack": "~5.78.0" + } +} diff --git a/packages/peggy-loader/src/index.ts b/packages/peggy-loader/src/index.ts new file mode 100644 index 000000000000..4d805d2be14d --- /dev/null +++ b/packages/peggy-loader/src/index.ts @@ -0,0 +1,39 @@ +import type { + BuildOptionsBase, + OutputFormatAmdCommonjsEs, + OutputFormatBare, + OutputFormatGlobals, + OutputFormatUmd, + SourceOptionsBase, +} from 'peggy'; +import peggy from 'peggy'; +import type { LoaderContext } from 'webpack'; + +type Options = + | BuildOptionsBase & + ( + | Omit< + OutputFormatAmdCommonjsEs<'source'>, + keyof SourceOptionsBase<'source'> + > + | Omit, keyof SourceOptionsBase<'source'>> + | Omit, keyof SourceOptionsBase<'source'>> + | Omit, keyof SourceOptionsBase<'source'>> + ); + +function peggyLoader( + this: LoaderContext, + grammarContent: string +): string { + const options: Options = { + format: 'commonjs', + ...this.getOptions(), + }; + + return peggy.generate(grammarContent, { + output: 'source', + ...options, + }); +} + +export default peggyLoader; diff --git a/packages/peggy-loader/tsconfig-cjs.json b/packages/peggy-loader/tsconfig-cjs.json new file mode 100644 index 000000000000..44b902831e41 --- /dev/null +++ b/packages/peggy-loader/tsconfig-cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "./dist/cjs" + } +} diff --git a/packages/peggy-loader/tsconfig.json b/packages/peggy-loader/tsconfig.json new file mode 100644 index 000000000000..9d2804b0261f --- /dev/null +++ b/packages/peggy-loader/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "ESNext", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist/esm", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true + } +} diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index 86c41231d618..34a2a79086a0 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -27,7 +27,7 @@ "dependencies": { "@rocket.chat/apps-engine": "1.41.0", "@rocket.chat/core-typings": "workspace:^", - "@rocket.chat/message-parser": "~0.31.28", + "@rocket.chat/message-parser": "workspace:^", "@rocket.chat/ui-kit": "workspace:~", "ajv": "^8.11.0", "ajv-formats": "^2.1.1" diff --git a/packages/uikit-playground/vite.config.ts b/packages/uikit-playground/vite.config.ts index 97c59e3c0229..a18e01b590e8 100644 --- a/packages/uikit-playground/vite.config.ts +++ b/packages/uikit-playground/vite.config.ts @@ -7,11 +7,11 @@ export default defineConfig(() => ({ esbuild: {}, plugins: [react()], optimizeDeps: { - include: ['@rocket.chat/ui-contexts'], + include: ['@rocket.chat/ui-contexts', '@rocket.chat/message-parser'], }, build: { commonjsOptions: { - include: [/ui-contexts/, /node_modules/], + include: [/ui-contexts/, /message-parser/, /node_modules/], }, }, })); diff --git a/yarn.lock b/yarn.lock index a82a88ee4157..066187e54b31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -975,17 +975,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.22.13, @babel/code-frame@npm:^7.5.5, @babel/code-frame@npm:^7.8.3": - version: 7.22.13 - resolution: "@babel/code-frame@npm:7.22.13" - dependencies: - "@babel/highlight": ^7.22.13 - chalk: ^2.4.2 - checksum: 22e342c8077c8b77eeb11f554ecca2ba14153f707b85294fcf6070b6f6150aae88a7b7436dd88d8c9289970585f3fe5b9b941c5aa3aa26a6d5a8ef3f292da058 - languageName: node - linkType: hard - -"@babel/code-frame@npm:^7.21.4, @babel/code-frame@npm:^7.23.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.21.4, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.5.5, @babel/code-frame@npm:^7.8.3": version: 7.23.5 resolution: "@babel/code-frame@npm:7.23.5" dependencies: @@ -995,20 +985,13 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.21.5": +"@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.21.5, @babel/compat-data@npm:^7.22.20, @babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9": version: 7.23.5 resolution: "@babel/compat-data@npm:7.23.5" checksum: 06ce244cda5763295a0ea924728c09bae57d35713b675175227278896946f922a63edf803c322f855a3878323d48d0255a2a3023409d2a123483c8a69ebb4744 languageName: node linkType: hard -"@babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.22.20, @babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9": - version: 7.22.20 - resolution: "@babel/compat-data@npm:7.22.20" - checksum: efedd1d18878c10fde95e4d82b1236a9aba41395ef798cbb651f58dbf5632dbff475736c507b8d13d4c8f44809d41c0eb2ef0d694283af9ba5dd8339b6dab451 - languageName: node - linkType: hard - "@babel/core@npm:7.12.9": version: 7.12.9 resolution: "@babel/core@npm:7.12.9" @@ -1079,6 +1062,20 @@ __metadata: languageName: node linkType: hard +"@babel/eslint-parser@npm:~7.21.3": + version: 7.21.8 + resolution: "@babel/eslint-parser@npm:7.21.8" + dependencies: + "@nicolo-ribaudo/eslint-scope-5-internals": 5.1.1-v1 + eslint-visitor-keys: ^2.1.0 + semver: ^6.3.0 + peerDependencies: + "@babel/core": ">=7.11.0" + eslint: ^7.5.0 || ^8.0.0 + checksum: 6d870f53808682b9d7e3c2a69a832b2095963103bb2d686daee3fcf1df49a0b3dfe58e95c773cab8cf59f2657ec432dfd5e47b9f1835c264eb84d2ec5ab2ad35 + languageName: node + linkType: hard + "@babel/eslint-parser@npm:~7.23.3": version: 7.23.3 resolution: "@babel/eslint-parser@npm:7.23.3" @@ -1093,19 +1090,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.12.11, @babel/generator@npm:^7.12.5, @babel/generator@npm:^7.22.15, @babel/generator@npm:^7.23.0, @babel/generator@npm:^7.7.2": - version: 7.23.0 - resolution: "@babel/generator@npm:7.23.0" - dependencies: - "@babel/types": ^7.23.0 - "@jridgewell/gen-mapping": ^0.3.2 - "@jridgewell/trace-mapping": ^0.3.17 - jsesc: ^2.5.1 - checksum: 8efe24adad34300f1f8ea2add420b28171a646edc70f2a1b3e1683842f23b8b7ffa7e35ef0119294e1901f45bfea5b3dc70abe1f10a1917ccdfb41bed69be5f1 - languageName: node - linkType: hard - -"@babel/generator@npm:^7.21.5, @babel/generator@npm:^7.23.5": +"@babel/generator@npm:^7.12.11, @babel/generator@npm:^7.12.5, @babel/generator@npm:^7.21.5, @babel/generator@npm:^7.22.15, @babel/generator@npm:^7.23.5, @babel/generator@npm:^7.7.2": version: 7.23.5 resolution: "@babel/generator@npm:7.23.5" dependencies: @@ -1135,16 +1120,7 @@ __metadata: 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" - dependencies: - "@babel/types": ^7.22.5 - checksum: d753acac62399fc6dd354cf1b9441bde0c331c2fe792a4c14904c5e5eafc3cac79478f6aa038e8a51c1148b0af6710a2e619855e4b5d54497ac972eaffed5884 - 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.21.5, @babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.5, @babel/helper-compilation-targets@npm:^7.22.6": +"@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.21.5, @babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.6": version: 7.22.15 resolution: "@babel/helper-compilation-targets@npm:7.22.15" dependencies: @@ -1176,20 +1152,7 @@ __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": - version: 7.22.5 - resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.5" - dependencies: - "@babel/helper-annotate-as-pure": ^7.22.5 - regexpu-core: ^5.3.1 - semver: ^6.3.0 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 94932145beeb1f91856be25fea8de30b4b81b63fbc7c5a207ed97a5ddc34cd1e9b04041ed28bd24ec09cdcfbb62e8d66f820e4fe864672afe0aa2f357c784e11 - languageName: node - linkType: hard - -"@babel/helper-create-regexp-features-plugin@npm:^7.22.15": +"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.22.15, @babel/helper-create-regexp-features-plugin@npm:^7.22.5": version: 7.22.15 resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.15" dependencies: @@ -1286,7 +1249,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.21.4, @babel/helper-module-imports@npm:^7.22.15, @babel/helper-module-imports@npm:^7.22.5": +"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.21.4, @babel/helper-module-imports@npm:^7.22.15": version: 7.22.15 resolution: "@babel/helper-module-imports@npm:7.22.15" dependencies: @@ -1295,22 +1258,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.22.20, @babel/helper-module-transforms@npm:^7.22.5, @babel/helper-module-transforms@npm:^7.23.0": - version: 7.23.0 - resolution: "@babel/helper-module-transforms@npm:7.23.0" - dependencies: - "@babel/helper-environment-visitor": ^7.22.20 - "@babel/helper-module-imports": ^7.22.15 - "@babel/helper-simple-access": ^7.22.5 - "@babel/helper-split-export-declaration": ^7.22.6 - "@babel/helper-validator-identifier": ^7.22.20 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 6e2afffb058cf3f8ce92f5116f710dda4341c81cfcd872f9a0197ea594f7ce0ab3cb940b0590af2fe99e60d2e5448bfba6bca8156ed70a2ed4be2adc8586c891 - languageName: node - linkType: hard - -"@babel/helper-module-transforms@npm:^7.21.5, @babel/helper-module-transforms@npm:^7.23.3": +"@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.21.5, @babel/helper-module-transforms@npm:^7.22.20, @babel/helper-module-transforms@npm:^7.23.3": version: 7.23.3 resolution: "@babel/helper-module-transforms@npm:7.23.3" dependencies: @@ -1348,7 +1296,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.18.9, @babel/helper-remap-async-to-generator@npm:^7.22.20": +"@babel/helper-remap-async-to-generator@npm:^7.18.9, @babel/helper-remap-async-to-generator@npm:^7.22.20, @babel/helper-remap-async-to-generator@npm:^7.22.9": version: 7.22.20 resolution: "@babel/helper-remap-async-to-generator@npm:7.22.20" dependencies: @@ -1361,20 +1309,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.22.5, @babel/helper-remap-async-to-generator@npm:^7.22.9": - version: 7.22.9 - resolution: "@babel/helper-remap-async-to-generator@npm:7.22.9" - dependencies: - "@babel/helper-annotate-as-pure": ^7.22.5 - "@babel/helper-environment-visitor": ^7.22.5 - "@babel/helper-wrap-function": ^7.22.9 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 05538079447829b13512157491cc77f9cf1ea7e1680e15cff0682c3ed9ee162de0c4862ece20a6d6b2df28177a1520bcfe45993fbeccf2747a81795a7c3f6290 - languageName: node - linkType: hard - -"@babel/helper-replace-supers@npm:^7.16.7, @babel/helper-replace-supers@npm:^7.22.20, @babel/helper-replace-supers@npm:^7.22.5, @babel/helper-replace-supers@npm:^7.22.9": +"@babel/helper-replace-supers@npm:^7.16.7, @babel/helper-replace-supers@npm:^7.22.20, @babel/helper-replace-supers@npm:^7.22.9": version: 7.22.20 resolution: "@babel/helper-replace-supers@npm:7.22.20" dependencies: @@ -1414,13 +1349,6 @@ __metadata: languageName: node linkType: hard -"@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.23.4": version: 7.23.4 resolution: "@babel/helper-string-parser@npm:7.23.4" @@ -1435,14 +1363,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.16.7, @babel/helper-validator-option@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/helper-validator-option@npm:7.22.15" - checksum: 68da52b1e10002a543161494c4bc0f4d0398c8fdf361d5f7f4272e95c45d5b32d974896d44f6a0ea7378c9204988879d73613ca683e13bd1304e46d25ff67a8d - languageName: node - linkType: hard - -"@babel/helper-validator-option@npm:^7.21.0": +"@babel/helper-validator-option@npm:^7.16.7, @babel/helper-validator-option@npm:^7.21.0, @babel/helper-validator-option@npm:^7.22.15": version: 7.23.5 resolution: "@babel/helper-validator-option@npm:7.23.5" checksum: 537cde2330a8aede223552510e8a13e9c1c8798afee3757995a7d4acae564124fe2bf7e7c3d90d62d3657434a74340a274b3b3b1c6f17e9a2be1f48af29cb09e @@ -1460,29 +1381,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-wrap-function@npm:^7.22.9": - version: 7.22.10 - resolution: "@babel/helper-wrap-function@npm:7.22.10" - dependencies: - "@babel/helper-function-name": ^7.22.5 - "@babel/template": ^7.22.5 - "@babel/types": ^7.22.10 - checksum: 854bd85fc1de1d4c633f04aa1f5b6b022fbc013b47d012b6a11a7a9125a1f4a2a4f13a3e0d7a7056fe7eda8a9ecd1ea3daf8af685685a2d1b16578768cfdd28f - languageName: node - linkType: hard - -"@babel/helpers@npm:^7.12.5, @babel/helpers@npm:^7.22.15": - version: 7.23.2 - resolution: "@babel/helpers@npm:7.23.2" - dependencies: - "@babel/template": ^7.22.15 - "@babel/traverse": ^7.23.2 - "@babel/types": ^7.23.0 - checksum: aaf4828df75ec460eaa70e5c9f66e6dadc28dae3728ddb7f6c13187dbf38030e142194b83d81aa8a31bbc35a5529a5d7d3f3cf59d5d0b595f5dd7f9d8f1ced8e - languageName: node - linkType: hard - -"@babel/helpers@npm:^7.21.5": +"@babel/helpers@npm:^7.12.5, @babel/helpers@npm:^7.21.5, @babel/helpers@npm:^7.22.15": version: 7.23.5 resolution: "@babel/helpers@npm:7.23.5" dependencies: @@ -1493,18 +1392,7 @@ __metadata: languageName: node linkType: hard -"@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.22.13": - version: 7.22.20 - resolution: "@babel/highlight@npm:7.22.20" - dependencies: - "@babel/helper-validator-identifier": ^7.22.20 - chalk: ^2.4.2 - js-tokens: ^4.0.0 - checksum: 84bd034dca309a5e680083cd827a766780ca63cef37308404f17653d32366ea76262bd2364b2d38776232f2d01b649f26721417d507e8b4b6da3e4e739f6d134 - languageName: node - linkType: hard - -"@babel/highlight@npm:^7.23.4": +"@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.23.4": version: 7.23.4 resolution: "@babel/highlight@npm:7.23.4" dependencies: @@ -1515,16 +1403,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.22.15, @babel/parser@npm:^7.22.16, @babel/parser@npm:^7.23.0": - version: 7.23.0 - resolution: "@babel/parser@npm:7.23.0" - bin: - parser: ./bin/babel-parser.js - checksum: 453fdf8b9e2c2b7d7b02139e0ce003d1af21947bbc03eb350fb248ee335c9b85e4ab41697ddbdd97079698de825a265e45a0846bb2ed47a2c7c1df833f42a354 - languageName: node - linkType: hard - -"@babel/parser@npm:^7.21.8, @babel/parser@npm:^7.23.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.8, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.22.16, @babel/parser@npm:^7.23.5": version: 7.23.5 resolution: "@babel/parser@npm:7.23.5" bin: @@ -1533,7 +1412,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.18.6": +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.18.6, @babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.22.15": version: 7.23.3 resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.23.3" dependencies: @@ -1544,18 +1423,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.22.15" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 8910ca21a7ec7c06f7b247d4b86c97c5aa15ef321518f44f6f490c5912fdf82c605aaa02b90892e375d82ccbedeadfdeadd922c1b836c9dd4c596871bf654753 - languageName: node - linkType: hard - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.20.7": +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.20.7, @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.22.15": version: 7.23.3 resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.23.3" dependencies: @@ -1568,19 +1436,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.22.15" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 - "@babel/plugin-transform-optional-chaining": ^7.22.15 - peerDependencies: - "@babel/core": ^7.13.0 - checksum: fbefedc0da014c37f1a50a8094ce7dbbf2181ae93243f23d6ecba2499b5b20196c2124d6a4dfe3e9e0125798e80593103e456352a4beb4e5c6f7c75efb80fdac - 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" @@ -1793,21 +1648,7 @@ __metadata: languageName: node linkType: hard -"@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: - "@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-private-property-in-object": ^7.14.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: add881a6a836635c41d2710551fdf777e2c07c0b691bf2baacc5d658dd64107479df1038680d6e67c468bfc6f36fb8920025d6bac2a1df0a81b867537d40ae78 - languageName: node - linkType: hard - -"@babel/plugin-proposal-private-property-in-object@npm:^7.21.0": +"@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.11 resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.11" dependencies: @@ -1932,7 +1773,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-assertions@npm:^7.20.0": +"@babel/plugin-syntax-import-assertions@npm:^7.20.0, @babel/plugin-syntax-import-assertions@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-syntax-import-assertions@npm:7.23.3" dependencies: @@ -1943,17 +1784,6 @@ __metadata: 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" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 2b8b5572db04a7bef1e6cd20debf447e4eef7cb012616f5eceb8fa3e23ce469b8f76ee74fd6d1e158ba17a8f58b0aec579d092fb67c5a30e83ccfbc5754916c1 - languageName: node - linkType: hard - "@babel/plugin-syntax-import-attributes@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-syntax-import-attributes@npm:7.22.5" @@ -2120,18 +1950,7 @@ __metadata: languageName: node linkType: hard -"@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: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 35abb6c57062802c7ce8bd96b2ef2883e3124370c688bbd67609f7d2453802fb73944df8808f893b6c67de978eb2bcf87bbfe325e46d6f39b5fcb09ece11d01a - languageName: node - linkType: hard - -"@babel/plugin-transform-arrow-functions@npm:^7.21.5": +"@babel/plugin-transform-arrow-functions@npm:^7.12.1, @babel/plugin-transform-arrow-functions@npm:^7.21.5, @babel/plugin-transform-arrow-functions@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-arrow-functions@npm:7.23.3" dependencies: @@ -2156,7 +1975,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:^7.20.7": +"@babel/plugin-transform-async-to-generator@npm:^7.20.7, @babel/plugin-transform-async-to-generator@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-async-to-generator@npm:7.23.3" dependencies: @@ -2169,20 +1988,7 @@ __metadata: 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" - dependencies: - "@babel/helper-module-imports": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-remap-async-to-generator": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: b95f23f99dcb379a9f0a1c2a3bbea3f8dc0e1b16dc1ac8b484fe378370169290a7a63d520959a9ba1232837cf74a80e23f6facbe14fd42a3cda6d3c2d7168e62 - languageName: node - linkType: hard - -"@babel/plugin-transform-block-scoped-functions@npm:^7.18.6": +"@babel/plugin-transform-block-scoped-functions@npm:^7.18.6, @babel/plugin-transform-block-scoped-functions@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.23.3" dependencies: @@ -2193,29 +1999,7 @@ __metadata: 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" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 416b1341858e8ca4e524dee66044735956ced5f478b2c3b9bc11ec2285b0c25d7dbb96d79887169eb938084c95d0a89338c8b2fe70d473bd9dc92e5d9db1732c - languageName: node - linkType: hard - -"@babel/plugin-transform-block-scoping@npm:^7.12.12, @babel/plugin-transform-block-scoping@npm:^7.22.15": - version: 7.23.0 - resolution: "@babel/plugin-transform-block-scoping@npm:7.23.0" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 0cfe925cc3b5a3ad407e2253fab3ceeaa117a4b291c9cb245578880872999bca91bd83ffa0128ae9ca356330702e1ef1dcb26804f28d2cef678239caf629f73e - languageName: node - linkType: hard - -"@babel/plugin-transform-block-scoping@npm:^7.21.0": +"@babel/plugin-transform-block-scoping@npm:^7.12.12, @babel/plugin-transform-block-scoping@npm:^7.21.0, @babel/plugin-transform-block-scoping@npm:^7.22.15": version: 7.23.4 resolution: "@babel/plugin-transform-block-scoping@npm:7.23.4" dependencies: @@ -2251,26 +2035,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.12.1, @babel/plugin-transform-classes@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-transform-classes@npm:7.22.15" - dependencies: - "@babel/helper-annotate-as-pure": ^7.22.5 - "@babel/helper-compilation-targets": ^7.22.15 - "@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.9 - "@babel/helper-split-export-declaration": ^7.22.6 - globals: ^11.1.0 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: d3f4d0c107dd8a3557ea3575cc777fab27efa92958b41e4a9822f7499725c1f554beae58855de16ddec0a7b694e45f59a26cea8fbde4275563f72f09c6e039a0 - languageName: node - linkType: hard - -"@babel/plugin-transform-classes@npm:^7.21.0": +"@babel/plugin-transform-classes@npm:^7.12.1, @babel/plugin-transform-classes@npm:^7.21.0, @babel/plugin-transform-classes@npm:^7.22.15": version: 7.23.5 resolution: "@babel/plugin-transform-classes@npm:7.23.5" dependencies: @@ -2289,7 +2054,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-computed-properties@npm:^7.21.5": +"@babel/plugin-transform-computed-properties@npm:^7.21.5, @babel/plugin-transform-computed-properties@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-computed-properties@npm:7.23.3" dependencies: @@ -2301,30 +2066,7 @@ __metadata: 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" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/template": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: c2a77a0f94ec71efbc569109ec14ea2aa925b333289272ced8b33c6108bdbb02caf01830ffc7e49486b62dec51911924d13f3a76f1149f40daace1898009e131 - languageName: node - linkType: hard - -"@babel/plugin-transform-destructuring@npm:^7.12.1, @babel/plugin-transform-destructuring@npm:^7.22.15": - version: 7.23.0 - resolution: "@babel/plugin-transform-destructuring@npm:7.23.0" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: cd6dd454ccc2766be551e4f8a04b1acc2aa539fa19e5c7501c56cc2f8cc921dd41a7ffb78455b4c4b2f954fcab8ca4561ba7c9c7bd5af9f19465243603d18cc3 - languageName: node - linkType: hard - -"@babel/plugin-transform-destructuring@npm:^7.21.3": +"@babel/plugin-transform-destructuring@npm:^7.12.1, @babel/plugin-transform-destructuring@npm:^7.21.3, @babel/plugin-transform-destructuring@npm:^7.22.15": version: 7.23.3 resolution: "@babel/plugin-transform-destructuring@npm:7.23.3" dependencies: @@ -2335,7 +2077,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-dotall-regex@npm:^7.18.6, @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.22.5, @babel/plugin-transform-dotall-regex@npm:^7.4.4": version: 7.23.3 resolution: "@babel/plugin-transform-dotall-regex@npm:7.23.3" dependencies: @@ -2347,19 +2089,7 @@ __metadata: 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: - "@babel/helper-create-regexp-features-plugin": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 409b658d11e3082c8f69e9cdef2d96e4d6d11256f005772425fb230cc48fd05945edbfbcb709dab293a1a2f01f9c8a5bb7b4131e632b23264039d9f95864b453 - languageName: node - linkType: hard - -"@babel/plugin-transform-duplicate-keys@npm:^7.18.9": +"@babel/plugin-transform-duplicate-keys@npm:^7.18.9, @babel/plugin-transform-duplicate-keys@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-duplicate-keys@npm:7.23.3" dependencies: @@ -2370,17 +2100,6 @@ __metadata: 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" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: bb1280fbabaab6fab2ede585df34900712698210a3bd413f4df5bae6d8c24be36b496c92722ae676a7a67d060a4624f4d6c23b923485f906bfba8773c69f55b4 - languageName: node - linkType: hard - "@babel/plugin-transform-dynamic-import@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-dynamic-import@npm:7.22.11" @@ -2393,7 +2112,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-exponentiation-operator@npm:^7.18.6": +"@babel/plugin-transform-exponentiation-operator@npm:^7.18.6, @babel/plugin-transform-exponentiation-operator@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.23.3" dependencies: @@ -2405,18 +2124,6 @@ __metadata: 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" - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: f2d660c1b1d51ad5fec1cd5ad426a52187204068c4158f8c4aa977b31535c61b66898d532603eef21c15756827be8277f724c869b888d560f26d7fe848bb5eae - languageName: node - linkType: hard - "@babel/plugin-transform-export-namespace-from@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-export-namespace-from@npm:7.22.11" @@ -2441,18 +2148,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.12.1, @babel/plugin-transform-for-of@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-transform-for-of@npm:7.22.15" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: f395ae7bce31e14961460f56cf751b5d6e37dd27d7df5b1f4e49fec1c11b6f9cf71991c7ffbe6549878591e87df0d66af798cf26edfa4bfa6b4c3dba1fb2f73a - languageName: node - linkType: hard - -"@babel/plugin-transform-for-of@npm:^7.21.5": +"@babel/plugin-transform-for-of@npm:^7.12.1, @babel/plugin-transform-for-of@npm:^7.21.5, @babel/plugin-transform-for-of@npm:^7.22.15": version: 7.23.3 resolution: "@babel/plugin-transform-for-of@npm:7.23.3" dependencies: @@ -2463,7 +2159,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-function-name@npm:^7.18.9": +"@babel/plugin-transform-function-name@npm:^7.18.9, @babel/plugin-transform-function-name@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-function-name@npm:7.23.3" dependencies: @@ -2476,19 +2172,6 @@ __metadata: 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" - dependencies: - "@babel/helper-compilation-targets": ^7.22.5 - "@babel/helper-function-name": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: cff3b876357999cb8ae30e439c3ec6b0491a53b0aa6f722920a4675a6dd5b53af97a833051df4b34791fe5b3dd326ccf769d5c8e45b322aa50ee11a660b17845 - languageName: node - linkType: hard - "@babel/plugin-transform-json-strings@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-json-strings@npm:7.22.11" @@ -2501,7 +2184,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-literals@npm:^7.18.9": +"@babel/plugin-transform-literals@npm:^7.18.9, @babel/plugin-transform-literals@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-literals@npm:7.23.3" dependencies: @@ -2512,17 +2195,6 @@ __metadata: 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" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: ec37cc2ffb32667af935ab32fe28f00920ec8a1eb999aa6dc6602f2bebd8ba205a558aeedcdccdebf334381d5c57106c61f52332045730393e73410892a9735b - languageName: node - linkType: hard - "@babel/plugin-transform-logical-assignment-operators@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.22.11" @@ -2535,7 +2207,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-member-expression-literals@npm:^7.18.6": +"@babel/plugin-transform-member-expression-literals@npm:^7.18.6, @babel/plugin-transform-member-expression-literals@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-member-expression-literals@npm:7.23.3" dependencies: @@ -2546,18 +2218,7 @@ __metadata: 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" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: ec4b0e07915ddd4fda0142fd104ee61015c208608a84cfa13643a95d18760b1dc1ceb6c6e0548898b8c49e5959a994e46367260176dbabc4467f729b21868504 - languageName: node - linkType: hard - -"@babel/plugin-transform-modules-amd@npm:^7.20.11": +"@babel/plugin-transform-modules-amd@npm:^7.20.11, @babel/plugin-transform-modules-amd@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-modules-amd@npm:7.23.3" dependencies: @@ -2569,19 +2230,7 @@ __metadata: 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" - dependencies: - "@babel/helper-module-transforms": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 7da4c4ebbbcf7d182abb59b2046b22d86eee340caf8a22a39ef6a727da2d8acfec1f714fcdcd5054110b280e4934f735e80a6848d192b6834c5d4459a014f04d - languageName: node - linkType: hard - -"@babel/plugin-transform-modules-commonjs@npm:^7.21.5": +"@babel/plugin-transform-modules-commonjs@npm:^7.21.5, @babel/plugin-transform-modules-commonjs@npm:^7.22.15, @babel/plugin-transform-modules-commonjs@npm:^7.23.0": version: 7.23.3 resolution: "@babel/plugin-transform-modules-commonjs@npm:7.23.3" dependencies: @@ -2594,20 +2243,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-commonjs@npm:^7.22.15, @babel/plugin-transform-modules-commonjs@npm:^7.23.0": - version: 7.23.0 - resolution: "@babel/plugin-transform-modules-commonjs@npm:7.23.0" - dependencies: - "@babel/helper-module-transforms": ^7.23.0 - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-simple-access": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 7fb25997194053e167c4207c319ff05362392da841bd9f42ddb3caf9c8798a5d203bd926d23ddf5830fdf05eddc82c2810f40d1287e3a4f80b07eff13d1024b5 - languageName: node - linkType: hard - -"@babel/plugin-transform-modules-systemjs@npm:^7.20.11": +"@babel/plugin-transform-modules-systemjs@npm:^7.20.11, @babel/plugin-transform-modules-systemjs@npm:^7.22.11": version: 7.23.3 resolution: "@babel/plugin-transform-modules-systemjs@npm:7.23.3" dependencies: @@ -2621,21 +2257,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-systemjs@npm:^7.22.11": - version: 7.23.0 - resolution: "@babel/plugin-transform-modules-systemjs@npm:7.23.0" - dependencies: - "@babel/helper-hoist-variables": ^7.22.5 - "@babel/helper-module-transforms": ^7.23.0 - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-validator-identifier": ^7.22.20 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 2d481458b22605046badea2317d5cc5c94ac3031c2293e34c96f02063f5b02af0979c4da6a8fbc67cc249541575dc9c6d710db6b919ede70b7337a22d9fd57a7 - languageName: node - linkType: hard - -"@babel/plugin-transform-modules-umd@npm:^7.18.6": +"@babel/plugin-transform-modules-umd@npm:^7.18.6, @babel/plugin-transform-modules-umd@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-modules-umd@npm:7.23.3" dependencies: @@ -2647,18 +2269,6 @@ __metadata: 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" - dependencies: - "@babel/helper-module-transforms": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 46622834c54c551b231963b867adbc80854881b3e516ff29984a8da989bd81665bd70e8cba6710345248e97166689310f544aee1a5773e262845a8f1b3e5b8b4 - languageName: node - linkType: hard - "@babel/plugin-transform-named-capturing-groups-regex@npm:^7.20.5, @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" @@ -2671,7 +2281,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-new-target@npm:^7.18.6": +"@babel/plugin-transform-new-target@npm:^7.18.6, @babel/plugin-transform-new-target@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-new-target@npm:7.23.3" dependencies: @@ -2682,17 +2292,6 @@ __metadata: 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" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 6b72112773487a881a1d6ffa680afde08bad699252020e86122180ee7a88854d5da3f15d9bca3331cf2e025df045604494a8208a2e63b486266b07c14e2ffbf3 - languageName: node - linkType: hard - "@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.22.11" @@ -2732,7 +2331,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-object-super@npm:^7.18.6": +"@babel/plugin-transform-object-super@npm:^7.18.6, @babel/plugin-transform-object-super@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-object-super@npm:7.23.3" dependencies: @@ -2744,18 +2343,6 @@ __metadata: 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" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-replace-supers": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: b71887877d74cb64dbccb5c0324fa67e31171e6a5311991f626650e44a4083e5436a1eaa89da78c0474fb095d4ec322d63ee778b202d33aa2e4194e1ed8e62d7 - languageName: node - linkType: hard - "@babel/plugin-transform-optional-catch-binding@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.22.11" @@ -2768,20 +2355,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-optional-chaining@npm:^7.22.15": - version: 7.23.0 - resolution: "@babel/plugin-transform-optional-chaining@npm:7.23.0" - 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: f702634f2b97e5260dbec0d4bde05ccb6f4d96d7bfa946481aeacfa205ca846cb6e096a38312f9d51fdbdac1f258f211138c5f7075952e46a5bf8574de6a1329 - languageName: node - linkType: hard - -"@babel/plugin-transform-optional-chaining@npm:^7.23.3": +"@babel/plugin-transform-optional-chaining@npm:^7.22.15, @babel/plugin-transform-optional-chaining@npm:^7.23.3": version: 7.23.4 resolution: "@babel/plugin-transform-optional-chaining@npm:7.23.4" dependencies: @@ -2794,18 +2368,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.22.15": - version: 7.22.15 - resolution: "@babel/plugin-transform-parameters@npm:7.22.15" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 541188bb7d1876cad87687b5c7daf90f63d8208ae83df24acb1e2b05020ad1c78786b2723ca4054a83fcb74fb6509f30c4cacc5b538ee684224261ad5fb047c1 - languageName: node - linkType: hard - -"@babel/plugin-transform-parameters@npm:^7.21.3": +"@babel/plugin-transform-parameters@npm:^7.12.1, @babel/plugin-transform-parameters@npm:^7.20.7, @babel/plugin-transform-parameters@npm:^7.21.3, @babel/plugin-transform-parameters@npm:^7.22.15": version: 7.23.3 resolution: "@babel/plugin-transform-parameters@npm:7.23.3" dependencies: @@ -2842,7 +2405,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-property-literals@npm:^7.18.6": +"@babel/plugin-transform-property-literals@npm:^7.18.6, @babel/plugin-transform-property-literals@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-property-literals@npm:7.23.3" dependencies: @@ -2853,17 +2416,6 @@ __metadata: 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" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 796176a3176106f77fcb8cd04eb34a8475ce82d6d03a88db089531b8f0453a2fb8b0c6ec9a52c27948bc0ea478becec449893741fc546dfc3930ab927e3f9f2e - 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" @@ -2935,7 +2487,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-regenerator@npm:^7.21.5": +"@babel/plugin-transform-regenerator@npm:^7.21.5, @babel/plugin-transform-regenerator@npm:^7.22.10": version: 7.23.3 resolution: "@babel/plugin-transform-regenerator@npm:7.23.3" dependencies: @@ -2947,19 +2499,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-regenerator@npm:^7.22.10": - version: 7.22.10 - resolution: "@babel/plugin-transform-regenerator@npm:7.22.10" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - regenerator-transform: ^0.15.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: e13678d62d6fa96f11cb8b863f00e8693491e7adc88bfca3f2820f80cbac8336e7dec3a596eee6a1c4663b7ececc3564f2cd7fb44ed6d4ce84ac2bb7f39ecc6e - languageName: node - linkType: hard - -"@babel/plugin-transform-reserved-words@npm:^7.18.6": +"@babel/plugin-transform-reserved-words@npm:^7.18.6, @babel/plugin-transform-reserved-words@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-reserved-words@npm:7.23.3" dependencies: @@ -2970,17 +2510,6 @@ __metadata: 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" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 3ffd7dbc425fe8132bfec118b9817572799cab1473113a635d25ab606c1f5a2341a636c04cf6b22df3813320365ed5a965b5eeb3192320a10e4cc2c137bd8bfc - languageName: node - linkType: hard - "@babel/plugin-transform-runtime@npm:~7.21.4": version: 7.21.4 resolution: "@babel/plugin-transform-runtime@npm:7.21.4" @@ -2997,18 +2526,7 @@ __metadata: languageName: node linkType: hard -"@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: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: a5ac902c56ea8effa99f681340ee61bac21094588f7aef0bc01dff98246651702e677552fa6d10e548c4ac22a3ffad047dd2f8c8f0540b68316c2c203e56818b - languageName: node - linkType: hard - -"@babel/plugin-transform-shorthand-properties@npm:^7.18.6": +"@babel/plugin-transform-shorthand-properties@npm:^7.12.1, @babel/plugin-transform-shorthand-properties@npm:^7.18.6, @babel/plugin-transform-shorthand-properties@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-shorthand-properties@npm:7.23.3" dependencies: @@ -3019,19 +2537,7 @@ __metadata: languageName: node linkType: hard -"@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: - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 5587f0deb60b3dfc9b274e269031cc45ec75facccf1933ea2ea71ced9fd3ce98ed91bb36d6cd26817c14474b90ed998c5078415f0eab531caf301496ce24c95c - languageName: node - linkType: hard - -"@babel/plugin-transform-spread@npm:^7.20.7": +"@babel/plugin-transform-spread@npm:^7.12.1, @babel/plugin-transform-spread@npm:^7.20.7, @babel/plugin-transform-spread@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-spread@npm:7.23.3" dependencies: @@ -3043,7 +2549,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-sticky-regex@npm:^7.18.6": +"@babel/plugin-transform-sticky-regex@npm:^7.18.6, @babel/plugin-transform-sticky-regex@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-sticky-regex@npm:7.23.3" dependencies: @@ -3054,29 +2560,7 @@ __metadata: 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" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 63b2c575e3e7f96c32d52ed45ee098fb7d354b35c2223b8c8e76840b32cc529ee0c0ceb5742fd082e56e91e3d82842a367ce177e82b05039af3d602c9627a729 - languageName: node - linkType: hard - -"@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: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 27e9bb030654cb425381c69754be4abe6a7c75b45cd7f962cd8d604b841b2f0fb7b024f2efc1c25cc53f5b16d79d5e8cfc47cacbdaa983895b3aeefa3e7e24ff - languageName: node - linkType: hard - -"@babel/plugin-transform-template-literals@npm:^7.18.9": +"@babel/plugin-transform-template-literals@npm:^7.12.1, @babel/plugin-transform-template-literals@npm:^7.18.9, @babel/plugin-transform-template-literals@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-template-literals@npm:7.23.3" dependencies: @@ -3087,7 +2571,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-typeof-symbol@npm:^7.18.9": +"@babel/plugin-transform-typeof-symbol@npm:^7.18.9, @babel/plugin-transform-typeof-symbol@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-typeof-symbol@npm:7.23.3" dependencies: @@ -3098,17 +2582,6 @@ __metadata: 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" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 82a53a63ffc3010b689ca9a54e5f53b2718b9f4b4a9818f36f9b7dba234f38a01876680553d2716a645a61920b5e6e4aaf8d4a0064add379b27ca0b403049512 - languageName: node - linkType: hard - "@babel/plugin-transform-typescript@npm:^7.22.15": version: 7.22.15 resolution: "@babel/plugin-transform-typescript@npm:7.22.15" @@ -3123,7 +2596,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-escapes@npm:^7.21.5": +"@babel/plugin-transform-unicode-escapes@npm:^7.21.5, @babel/plugin-transform-unicode-escapes@npm:^7.22.10": version: 7.23.3 resolution: "@babel/plugin-transform-unicode-escapes@npm:7.23.3" dependencies: @@ -3134,17 +2607,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-escapes@npm:^7.22.10": - version: 7.22.10 - resolution: "@babel/plugin-transform-unicode-escapes@npm:7.22.10" - dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 807f40ed1324c8cb107c45358f1903384ca3f0ef1d01c5a3c5c9b271c8d8eec66936a3dcc8d75ddfceea9421420368c2e77ae3adef0a50557e778dfe296bf382 - languageName: node - linkType: hard - "@babel/plugin-transform-unicode-property-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.22.5" @@ -3157,7 +2619,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-regex@npm:^7.18.6": +"@babel/plugin-transform-unicode-regex@npm:^7.18.6, @babel/plugin-transform-unicode-regex@npm:^7.22.5": version: 7.23.3 resolution: "@babel/plugin-transform-unicode-regex@npm:7.23.3" dependencies: @@ -3169,18 +2631,6 @@ __metadata: 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" - dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 6b5d1404c8c623b0ec9bd436c00d885a17d6a34f3f2597996343ddb9d94f6379705b21582dfd4cec2c47fd34068872e74ab6b9580116c0566b3f9447e2a7fa06 - languageName: node - linkType: hard - "@babel/plugin-transform-unicode-sets-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.22.5" @@ -3514,7 +2964,7 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.12.7, @babel/template@npm:^7.20.7, @babel/template@npm:^7.22.15, @babel/template@npm:^7.22.5, @babel/template@npm:^7.3.3": +"@babel/template@npm:^7.12.7, @babel/template@npm:^7.20.7, @babel/template@npm:^7.22.15, @babel/template@npm:^7.3.3": version: 7.22.15 resolution: "@babel/template@npm:7.22.15" dependencies: @@ -3525,25 +2975,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.22.20, @babel/traverse@npm:^7.23.2": - version: 7.23.2 - resolution: "@babel/traverse@npm:7.23.2" - dependencies: - "@babel/code-frame": ^7.22.13 - "@babel/generator": ^7.23.0 - "@babel/helper-environment-visitor": ^7.22.20 - "@babel/helper-function-name": ^7.23.0 - "@babel/helper-hoist-variables": ^7.22.5 - "@babel/helper-split-export-declaration": ^7.22.6 - "@babel/parser": ^7.23.0 - "@babel/types": ^7.23.0 - debug: ^4.1.0 - globals: ^11.1.0 - checksum: 26a1eea0dde41ab99dde8b9773a013a0dc50324e5110a049f5d634e721ff08afffd54940b3974a20308d7952085ac769689369e9127dea655f868c0f6e1ab35d - languageName: node - linkType: hard - -"@babel/traverse@npm:^7.21.5, @babel/traverse@npm:^7.23.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.21.5, @babel/traverse@npm:^7.22.20, @babel/traverse@npm:^7.23.5": version: 7.23.5 resolution: "@babel/traverse@npm:7.23.5" dependencies: @@ -3561,18 +2993,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.2.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.10, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @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.23.0 - resolution: "@babel/types@npm:7.23.0" - dependencies: - "@babel/helper-string-parser": ^7.22.5 - "@babel/helper-validator-identifier": ^7.22.20 - to-fast-properties: ^2.0.0 - checksum: 215fe04bd7feef79eeb4d33374b39909ce9cad1611c4135a4f7fdf41fe3280594105af6d7094354751514625ea92d0875aba355f53e86a92600f290e77b0e604 - languageName: node - linkType: hard - -"@babel/types@npm:^7.21.5, @babel/types@npm:^7.23.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.21.5, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.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.23.5 resolution: "@babel/types@npm:7.23.5" dependencies: @@ -5061,10 +4482,10 @@ __metadata: languageName: node linkType: hard -"@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 +"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 83b85f72c59d1c080b4cbec0fef84528963a1b5db34e4370fa4bd1e3ff64a0d80e0cee7369d11d73c704e0286fb2865b530acac7a871088fbe92b5edf1000870 languageName: node linkType: hard @@ -5085,10 +4506,10 @@ __metadata: languageName: node linkType: hard -"@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 +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14": + version: 1.4.15 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" + checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 languageName: node linkType: hard @@ -5102,13 +4523,13 @@ __metadata: languageName: node linkType: hard -"@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" +"@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.20, @jridgewell/trace-mapping@npm:^0.3.9": + version: 0.3.22 + resolution: "@jridgewell/trace-mapping@npm:0.3.22" dependencies: - "@jridgewell/resolve-uri": 3.1.0 - "@jridgewell/sourcemap-codec": 1.4.14 - checksum: 0572669f855260808c16fe8f78f5f1b4356463b11d3f2c7c0b5580c8ba1cbf4ae53efe9f627595830856e57dbac2325ac17eb0c3dd0ec42102e6f227cc289c02 + "@jridgewell/resolve-uri": ^3.1.0 + "@jridgewell/sourcemap-codec": ^1.4.14 + checksum: ac7dd2cfe0b479aa1b81776d40d789243131cc792dc8b6b6a028c70fcd6171958ae1a71bf67b618ffe3c0c3feead9870c095ee46a5e30319410d92976b28f498 languageName: node linkType: hard @@ -5150,16 +4571,7 @@ __metadata: languageName: node linkType: hard -"@lezer/highlight@npm:^1.0.0, @lezer/highlight@npm:^1.1.3": - version: 1.1.6 - resolution: "@lezer/highlight@npm:1.1.6" - dependencies: - "@lezer/common": ^1.0.0 - checksum: 411a702394c4c996b7d7f145a38f3a85a8cc698b3918acc7121c629255bb76d4ab383753f69009e011dc415210c6acbbb5b27bde613259ab67e600b29397b03b - 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.2.0 resolution: "@lezer/highlight@npm:1.2.0" dependencies: @@ -9009,7 +8421,7 @@ __metadata: "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/icons": ^0.33.0 - "@rocket.chat/message-parser": ~0.31.28 + "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/models": "workspace:^" "@rocket.chat/rest-typings": "workspace:^" "@rocket.chat/ui-kit": "workspace:~" @@ -9034,7 +8446,7 @@ __metadata: "@rocket.chat/apps-engine": 1.41.0 "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/icons": ^0.33.0 - "@rocket.chat/message-parser": ~0.31.28 + "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/ui-kit": "workspace:~" eslint: ~8.45.0 mongodb: ^4.17.2 @@ -9097,7 +8509,7 @@ __metadata: jest-environment-jsdom: ~29.6.4 jest-websocket-mock: ^2.4.0 ts-jest: ^29.1.2 - typescript: ^5.3.3 + typescript: ^5.3.2 ws: ^8.13.0 peerDependencies: "@rocket.chat/emitter": "*" @@ -9393,7 +8805,7 @@ __metadata: "@rocket.chat/css-in-js": ~0.31.25 "@rocket.chat/fuselage": ^0.47.0 "@rocket.chat/fuselage-tokens": ~0.32.0 - "@rocket.chat/message-parser": ~0.31.28 + "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/styled": ~0.31.25 "@rocket.chat/ui-client": "workspace:^" "@rocket.chat/ui-contexts": "workspace:^" @@ -9543,7 +8955,7 @@ __metadata: "@rocket.chat/fuselage-tokens": ~0.32.0 "@rocket.chat/gazzodown": "workspace:^" "@rocket.chat/logo": ^0.31.29 - "@rocket.chat/message-parser": ~0.31.28 + "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/random": "workspace:~" "@rocket.chat/sdk": ^1.0.0-alpha.42 "@rocket.chat/ui-kit": "workspace:~" @@ -9673,14 +9085,36 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/message-parser@npm:~0.31.28": - version: 0.31.28 - resolution: "@rocket.chat/message-parser@npm:0.31.28" +"@rocket.chat/message-parser@workspace:^, @rocket.chat/message-parser@workspace:packages/message-parser": + version: 0.0.0-use.local + resolution: "@rocket.chat/message-parser@workspace:packages/message-parser" dependencies: + "@babel/core": ~7.21.4 + "@babel/eslint-parser": ~7.21.3 + "@babel/preset-env": ~7.21.4 + "@rocket.chat/eslint-config": "workspace:^" + "@rocket.chat/peggy-loader": "workspace:~" + "@rocket.chat/prettier-config": ~0.31.25 + "@types/jest": ~29.5.7 + "@types/node": ~14.18.42 + "@typescript-eslint/parser": ~5.58.0 + babel-loader: ~9.1.2 + eslint: ~8.45.0 + jest: ~29.6.4 + npm-run-all: ^4.1.5 + peggy: 3.0.2 + prettier: ~2.8.7 + prettier-plugin-pegjs: ~0.5.4 + rimraf: ^3.0.2 tldts: ~5.7.112 - checksum: 26a1a49318052201c0f4d2807ea740f69ffe741f7d51ea192bf56d2a9e1a19c8317232dff8f0b0f332a63035a3589683d41b1fb64503d76f8cd76122a5f6b0fe - languageName: node - linkType: hard + ts-jest: ~29.1.0 + ts-loader: ~9.4.2 + typedoc: ~0.24.1 + typescript: ~5.0.4 + webpack: ~5.78.0 + webpack-cli: ~5.0.1 + languageName: unknown + linkType: soft "@rocket.chat/meteor@workspace:apps/meteor": version: 0.0.0-use.local @@ -9742,7 +9176,7 @@ __metadata: "@rocket.chat/logger": "workspace:^" "@rocket.chat/logo": ^0.31.29 "@rocket.chat/memo": ~0.31.25 - "@rocket.chat/message-parser": ~0.31.28 + "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/mock-providers": "workspace:^" "@rocket.chat/model-typings": "workspace:^" "@rocket.chat/models": "workspace:^" @@ -10261,6 +9695,27 @@ __metadata: languageName: unknown linkType: soft +"@rocket.chat/peggy-loader@workspace:packages/peggy-loader, @rocket.chat/peggy-loader@workspace:~": + version: 0.0.0-use.local + resolution: "@rocket.chat/peggy-loader@workspace:packages/peggy-loader" + dependencies: + "@rocket.chat/eslint-config": "workspace:~" + "@rocket.chat/prettier-config": ~0.31.25 + "@types/node": ~14.18.42 + eslint: ~8.45.0 + npm-run-all: ^4.1.5 + peggy: 3.0.2 + prettier: ~2.8.7 + rimraf: ^3.0.2 + ts-jest: ~29.1.0 + typescript: ~5.0.4 + webpack: ~5.78.0 + peerDependencies: + peggy: "*" + webpack: "*" + languageName: unknown + linkType: soft + "@rocket.chat/poplib@workspace:^, @rocket.chat/poplib@workspace:packages/node-poplib": version: 0.0.0-use.local resolution: "@rocket.chat/poplib@workspace:packages/node-poplib" @@ -10430,7 +9885,7 @@ __metadata: "@rocket.chat/apps-engine": 1.41.0 "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/message-parser": ~0.31.28 + "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/ui-kit": "workspace:~" "@types/jest": ~29.5.7 ajv: ^8.11.0 @@ -14027,17 +13482,7 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:*, @types/jest@npm:~29.5.7": - version: 29.5.7 - resolution: "@types/jest@npm:29.5.7" - dependencies: - expect: ^29.0.0 - pretty-format: ^29.0.0 - checksum: e28624ccb0ef1255a03fbbb4b5bc3e5cbcdc450d39e0739985ff679b124198f808c38c8c3e67859c6efc0e848196deeb8cfed028e12a821c511dfc1112a2d6e9 - languageName: node - linkType: hard - -"@types/jest@npm:^29.5.12": +"@types/jest@npm:*, @types/jest@npm:^29.5.12, @types/jest@npm:~29.5.0, @types/jest@npm:~29.5.7": version: 29.5.12 resolution: "@types/jest@npm:29.5.12" dependencies: @@ -14047,16 +13492,6 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:~29.5.0": - version: 29.5.10 - resolution: "@types/jest@npm:29.5.10" - dependencies: - expect: ^29.0.0 - pretty-format: ^29.0.0 - checksum: ef385905787db528de9b6beb2688865c0bb276e64256ed60b9a1a6ffc0b75737456cb5e27e952a3241c5845b6a1da487470010dd30f3ca59c8581624c564a823 - languageName: node - linkType: hard - "@types/jquery@npm:*": version: 3.5.14 resolution: "@types/jquery@npm:3.5.14" @@ -14375,7 +13810,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^14.0.26, @types/node@npm:^14.14.37, @types/node@npm:^14.18.63": +"@types/node@npm:^14.0.26, @types/node@npm:^14.14.37, @types/node@npm:^14.18.63, @types/node@npm:~14.18.42": version: 14.18.63 resolution: "@types/node@npm:14.18.63" checksum: be909061a54931778c71c49dc562586c32f909c4b6197e3d71e6dac726d8bd9fccb9f599c0df99f52742b68153712b5097c0f00cac4e279fa894b0ea6719a8fd @@ -14498,14 +13933,7 @@ __metadata: languageName: node linkType: hard -"@types/prop-types@npm:*": - version: 15.7.4 - resolution: "@types/prop-types@npm:15.7.4" - checksum: ef6e1899e59b876c273811b1bd845022fc66d5a3d11cb38a25b6c566b30514ae38fe20a40f67622f362a4f4f7f9224e22d8da101cff3d6e97e11d7b4c307cfc1 - languageName: node - linkType: hard - -"@types/prop-types@npm:^15.7.2": +"@types/prop-types@npm:*, @types/prop-types@npm:^15.7.2": version: 15.7.11 resolution: "@types/prop-types@npm:15.7.11" checksum: 7519ff11d06fbf6b275029fe03fff9ec377b4cb6e864cac34d87d7146c7f5a7560fd164bdc1d2dbe00b60c43713631251af1fd3d34d46c69cd354602bc0c7c54 @@ -15116,6 +14544,23 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/parser@npm:~5.58.0": + version: 5.58.0 + resolution: "@typescript-eslint/parser@npm:5.58.0" + dependencies: + "@typescript-eslint/scope-manager": 5.58.0 + "@typescript-eslint/types": 5.58.0 + "@typescript-eslint/typescript-estree": 5.58.0 + debug: ^4.3.4 + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 38681da48a40132c0538579c818ceef9ba2793ab8f79236c3f64980ba1649bb87cb367cd79d37bf2982b8bfbc28f91846b8676f9bd333e8b691c9befffd8874a + languageName: node + linkType: hard + "@typescript-eslint/parser@npm:~5.60.1": version: 5.60.1 resolution: "@typescript-eslint/parser@npm:5.60.1" @@ -15133,6 +14578,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.1": version: 5.60.1 resolution: "@typescript-eslint/scope-manager@npm:5.60.1" @@ -15160,6 +14615,13 @@ __metadata: 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.1": version: 5.60.1 resolution: "@typescript-eslint/types@npm:5.60.1" @@ -15167,6 +14629,24 @@ __metadata: 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/types": 5.58.0 + "@typescript-eslint/visitor-keys": 5.58.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: 51b668ec858db0c040a71dff526273945cee4ba5a9b240528d503d02526685882d900cf071c6636a4d9061ed3fd4a7274f7f1a23fba55c4b48b143344b4009c7 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:5.60.1": version: 5.60.1 resolution: "@typescript-eslint/typescript-estree@npm:5.60.1" @@ -15203,6 +14683,16 @@ __metadata: 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.1": version: 5.60.1 resolution: "@typescript-eslint/visitor-keys@npm:5.60.1" @@ -15252,6 +14742,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" @@ -15273,6 +14773,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" @@ -15287,6 +14794,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" @@ -15301,6 +14815,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" @@ -15340,6 +14861,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" @@ -15351,6 +14883,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" @@ -15365,6 +14904,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" @@ -15389,6 +14940,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" @@ -15407,6 +14967,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" @@ -15425,6 +14994,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" @@ -15439,6 +15015,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" @@ -15471,6 +15063,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" @@ -15497,6 +15102,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" @@ -15521,6 +15138,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" @@ -15563,6 +15194,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" @@ -15584,7 +15225,7 @@ __metadata: languageName: node linkType: hard -"@webpack-cli/configtest@npm:^2.1.1": +"@webpack-cli/configtest@npm:^2.0.1, @webpack-cli/configtest@npm:^2.1.1": version: 2.1.1 resolution: "@webpack-cli/configtest@npm:2.1.1" peerDependencies: @@ -15594,7 +15235,7 @@ __metadata: languageName: node linkType: hard -"@webpack-cli/info@npm:^2.0.2": +"@webpack-cli/info@npm:^2.0.1, @webpack-cli/info@npm:^2.0.2": version: 2.0.2 resolution: "@webpack-cli/info@npm:2.0.2" peerDependencies: @@ -15604,7 +15245,7 @@ __metadata: languageName: node linkType: hard -"@webpack-cli/serve@npm:^2.0.5": +"@webpack-cli/serve@npm:^2.0.2, @webpack-cli/serve@npm:^2.0.5": version: 2.0.5 resolution: "@webpack-cli/serve@npm:2.0.5" peerDependencies: @@ -15719,7 +15360,7 @@ __metadata: languageName: node linkType: hard -"acorn-import-assertions@npm:^1.9.0": +"acorn-import-assertions@npm:^1.7.6, acorn-import-assertions@npm:^1.9.0": version: 1.9.0 resolution: "acorn-import-assertions@npm:1.9.0" peerDependencies: @@ -16062,6 +15703,13 @@ __metadata: languageName: node linkType: hard +"ansi-sequence-parser@npm:^1.1.0": + version: 1.1.1 + resolution: "ansi-sequence-parser@npm:1.1.1" + checksum: ead5b15c596e8e85ca02951a844366c6776769dcc9fd1bd3a0db11bb21364554822c6a439877fb599e7e1ffa0b5f039f1e5501423950457f3dcb2f480c30b188 + languageName: node + linkType: hard + "ansi-styles@npm:^2.2.1": version: 2.2.1 resolution: "ansi-styles@npm:2.2.1" @@ -17827,21 +17475,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.9": - version: 4.21.9 - resolution: "browserslist@npm:4.21.9" - dependencies: - caniuse-lite: ^1.0.30001503 - electron-to-chromium: ^1.4.431 - node-releases: ^2.0.12 - update-browserslist-db: ^1.0.11 - bin: - browserslist: cli.js - checksum: 80d3820584e211484ad1b1a5cfdeca1dd00442f47be87e117e1dda34b628c87e18b81ae7986fa5977b3e6a03154f6d13cd763baa6b8bf5dd9dd19f4926603698 - languageName: node - linkType: hard - -"browserslist@npm:^4.22.1": +"browserslist@npm:^4.0.0, browserslist@npm:^4.12.0, browserslist@npm:^4.14.5, browserslist@npm:^4.21.9, browserslist@npm:^4.22.1": version: 4.22.1 resolution: "browserslist@npm:4.22.1" dependencies: @@ -18341,7 +17975,7 @@ __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.30001541": +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001541": version: 1.0.30001577 resolution: "caniuse-lite@npm:1.0.30001577" checksum: 26d2b4a498a2a6ad5a33c44c18a32497b59a3bb1963b8b9221ddcbfe166ed7f7a1f75a3de040870cdc2467ce35199c643cfe8c45e7208d8bc033e7877214b0f9 @@ -19576,7 +19210,7 @@ __metadata: languageName: node linkType: hard -"core-js-compat@npm:^3.25.1": +"core-js-compat@npm:^3.25.1, core-js-compat@npm:^3.31.0, core-js-compat@npm:^3.8.1": version: 3.33.3 resolution: "core-js-compat@npm:3.33.3" dependencies: @@ -19585,15 +19219,6 @@ __metadata: languageName: node linkType: hard -"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.9 - checksum: 9a16d6992621f4e099169297381a28d5712cdef7df1fa85352a7c285a5885d5d7a117ec2eae9ad715ed88c7cc774787a22cdb8aceababf6775fbc8b0cbeccdb7 - languageName: node - linkType: hard - "core-js-pure@npm:^3.8.1": version: 3.21.1 resolution: "core-js-pure@npm:3.21.1" @@ -21644,13 +21269,6 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.431": - version: 1.4.433 - resolution: "electron-to-chromium@npm:1.4.433" - checksum: 106e3bc2fb4ee5eddd4b141363900d5cd731c7579aa6bebd02509c52d6b598a1684aba1b75791e838dfa54dec0a40ddd17ea01199041ea46310aafb206395e43 - languageName: node - linkType: hard - "electron-to-chromium@npm:^1.4.535": version: 1.4.597 resolution: "electron-to-chromium@npm:1.4.597" @@ -21848,7 +21466,7 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.0.0, enhanced-resolve@npm:^5.15.0": +"enhanced-resolve@npm:^5.0.0, enhanced-resolve@npm:^5.10.0, enhanced-resolve@npm:^5.15.0": version: 5.15.0 resolution: "enhanced-resolve@npm:5.15.0" dependencies: @@ -22037,6 +21655,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" @@ -24172,14 +23797,7 @@ __metadata: languageName: node linkType: hard -"function-bind@npm:^1.1.1": - version: 1.1.1 - resolution: "function-bind@npm:1.1.1" - checksum: b32fbaebb3f8ec4969f033073b43f5c8befbb58f1a79e12f1d7490358150359ebd92f49e72ff0144f65f2c48ea2a605bff2d07965f548f6474fd8efd95bf361a - languageName: node - linkType: hard - -"function-bind@npm:^1.1.2": +"function-bind@npm:^1.1.1, function-bind@npm:^1.1.2": version: 1.1.2 resolution: "function-bind@npm:1.1.2" checksum: 2b0ff4ce708d99715ad14a6d1f894e2a83242e4a52ccfcefaee5e40050562e5f6dafc1adbb4ce2d4ab47279a45dc736ab91ea5042d843c3c092820dfe032efb1 @@ -26745,16 +26363,7 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.11.0, is-core-module@npm:^2.5.0, is-core-module@npm:^2.8.1, is-core-module@npm:^2.9.0": - version: 2.12.0 - resolution: "is-core-module@npm:2.12.0" - dependencies: - has: ^1.0.3 - checksum: f7f7eb2ab71fd769ee9fb2385c095d503aa4b5ce0028c04557de03f1e67a87c85e5bac1f215945fc3c955867a139a415a3ec4c4234a0bffdf715232660f440a6 - languageName: node - linkType: hard - -"is-core-module@npm:^2.13.0": +"is-core-module@npm:^2.11.0, is-core-module@npm:^2.13.0, is-core-module@npm:^2.5.0, is-core-module@npm:^2.8.1, is-core-module@npm:^2.9.0": version: 2.13.1 resolution: "is-core-module@npm:2.13.1" dependencies: @@ -29606,6 +29215,13 @@ __metadata: languageName: node linkType: hard +"lunr@npm:^2.3.9": + version: 2.3.9 + resolution: "lunr@npm:2.3.9" + checksum: 176719e24fcce7d3cf1baccce9dd5633cd8bdc1f41ebe6a180112e5ee99d80373fe2454f5d4624d437e5a8319698ca6837b9950566e15d2cae5f2a543a3db4b8 + languageName: node + linkType: hard + "lz-string@npm:^1.4.4, lz-string@npm:^1.5.0": version: 1.5.0 resolution: "lz-string@npm:1.5.0" @@ -29786,12 +29402,12 @@ __metadata: languageName: node linkType: hard -"marked@npm:^4.2.5": - version: 4.2.5 - resolution: "marked@npm:4.2.5" +"marked@npm:^4.2.5, marked@npm:^4.3.0": + version: 4.3.0 + resolution: "marked@npm:4.3.0" bin: marked: bin/marked.js - checksum: dd7da20a3983c66b516463fad5dc8d15dc70e137d20b6dc491e134f671e84bd2ed5f859e2c35f21e56830a122e4356b9e574bcde49b72b7ad6bc121a215a1a98 + checksum: 0db6817893952c3ec710eb9ceafb8468bf5ae38cb0f92b7b083baa13d70b19774674be04db5b817681fa7c5c6a088f61300815e4dd75a59696f4716ad69f6260 languageName: node linkType: hard @@ -30521,6 +30137,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.0": + version: 9.0.3 + resolution: "minimatch@npm:9.0.3" + dependencies: + brace-expansion: ^2.0.1 + checksum: 253487976bf485b612f16bf57463520a14f512662e592e95c571afdab1442a6a6864b6c88f248ce6fc4ff0b6de04ac7aa6c8bb51e868e99d1d65eb0658a708b5 + languageName: node + linkType: hard + "minimatch@npm:~3.0.4": version: 3.0.8 resolution: "minimatch@npm:3.0.8" @@ -30888,7 +30513,7 @@ __metadata: languageName: node linkType: hard -"mongodb@npm:^4.17.2": +"mongodb@npm:^4.17.2, mongodb@npm:^4.3.1": version: 4.17.2 resolution: "mongodb@npm:4.17.2" dependencies: @@ -30906,24 +30531,6 @@ __metadata: languageName: node linkType: hard -"mongodb@npm:^4.3.1": - version: 4.17.1 - resolution: "mongodb@npm:4.17.1" - dependencies: - "@aws-sdk/credential-providers": ^3.186.0 - "@mongodb-js/saslprep": ^1.1.0 - bson: ^4.7.2 - mongodb-connection-string-url: ^2.6.0 - socks: ^2.7.1 - dependenciesMeta: - "@aws-sdk/credential-providers": - optional: true - "@mongodb-js/saslprep": - optional: true - checksum: e7f280570d0f23d60c308b2a484ed55762ec8e523946c0de1a0b3b398f24efcf1916a745e5407f32cd1c105b2f19d8ac75474c92f73cdf651affe3430a963f54 - languageName: node - linkType: hard - "moo@npm:^0.5.0, moo@npm:^0.5.1": version: 0.5.1 resolution: "moo@npm:0.5.1" @@ -31334,7 +30941,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2": +"node-fetch@npm:^2, 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.7.0 resolution: "node-fetch@npm:2.7.0" dependencies: @@ -31348,20 +30955,6 @@ __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": - version: 2.6.11 - resolution: "node-fetch@npm:2.6.11" - dependencies: - whatwg-url: ^5.0.0 - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - checksum: 249d0666a9497553384d46b5ab296ba223521ac88fed4d8a17d6ee6c2efb0fc890f3e8091cafe7f9fba8151a5b8d925db2671543b3409a56c3cd522b468b47b3 - languageName: node - linkType: hard - "node-forge@npm:^0.7.1": version: 0.7.6 resolution: "node-forge@npm:0.7.6" @@ -31506,13 +31099,6 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.12": - version: 2.0.12 - resolution: "node-releases@npm:2.0.12" - checksum: b8c56db82c4642a0f443332b331a4396dae452a2ac5a65c8dbd93ef89ecb2fbb0da9d42ac5366d4764973febadca816cf7587dad492dce18d2a6b2af59cda260 - languageName: node - linkType: hard - "node-releases@npm:^2.0.13": version: 2.0.13 resolution: "node-releases@npm:2.0.13" @@ -33033,6 +32619,18 @@ __metadata: languageName: node linkType: hard +"peggy@npm:3.0.2": + version: 3.0.2 + resolution: "peggy@npm:3.0.2" + dependencies: + commander: ^10.0.0 + source-map-generator: 0.8.0 + bin: + peggy: bin/peggy.js + checksum: bdca2291a2453e35fb9499bd414f25d44093aecaeb992ef90151ab126514c053c30d4bf5b54f68cbce22a4420eef75219a6deb1209b300630bff38e7640115d9 + languageName: node + linkType: hard + "pend@npm:~1.2.0": version: 1.2.0 resolution: "pend@npm:1.2.0" @@ -34272,6 +33870,15 @@ __metadata: languageName: node linkType: hard +"prettier-plugin-pegjs@npm:~0.5.4": + version: 0.5.4 + resolution: "prettier-plugin-pegjs@npm:0.5.4" + dependencies: + prettier: ^2.8.4 + checksum: 25e89f6718601b232ddc6f6215f7222ede1bb3d756106635a73a24047f518771d36017dd8c2b8160809a49ad561fa155e4bcba9973d72951f6c6d2017787ba6f + languageName: node + linkType: hard + "prettier@npm:>=2.2.1 <=2.3.0": version: 2.3.0 resolution: "prettier@npm:2.3.0" @@ -34281,7 +33888,7 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^2.7.1, prettier@npm:~2.8.8": +"prettier@npm:^2.7.1, prettier@npm:^2.8.4, prettier@npm:~2.8.7, prettier@npm:~2.8.8": version: 2.8.8 resolution: "prettier@npm:2.8.8" bin: @@ -34412,16 +34019,7 @@ __metadata: languageName: node linkType: hard -"prom-client@npm:^14.0.0": - version: 14.0.1 - resolution: "prom-client@npm:14.0.1" - dependencies: - tdigest: ^0.1.1 - checksum: 864c19b7086eda8fae652385bc8b8aeb155f85922e58672d07a64918a603341e120e65e08f9d77ccab546518dc18930284da8743c2aac3c968f626d7063d6bba - languageName: node - linkType: hard - -"prom-client@npm:^14.2.0": +"prom-client@npm:^14.0.0, prom-client@npm:^14.2.0": version: 14.2.0 resolution: "prom-client@npm:14.2.0" dependencies: @@ -36372,20 +35970,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6, resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.11.1, resolve@npm:^1.12.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.0, resolve@npm:^1.22.1, resolve@npm:^1.3.2": - version: 1.22.2 - resolution: "resolve@npm:1.22.2" - dependencies: - is-core-module: ^2.11.0 - path-parse: ^1.0.7 - supports-preserve-symlinks-flag: ^1.0.0 - bin: - resolve: bin/resolve - checksum: 7e5df75796ebd429445d102d5824482ee7e567f0070b2b45897b29bb4f613dcbc262e0257b8aeedb3089330ccaea0d6a0464df1a77b2992cf331dcda0f4cb549 - languageName: node - linkType: hard - -"resolve@npm:^1.22.2": +"resolve@npm:^1.1.6, resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.11.1, resolve@npm:^1.12.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.0, resolve@npm:^1.22.1, resolve@npm:^1.22.2, resolve@npm:^1.3.2": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -36411,20 +35996,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.1.7#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.11.1#~builtin, resolve@patch:resolve@^1.12.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.3.2#~builtin": - version: 1.22.2 - resolution: "resolve@patch:resolve@npm%3A1.22.2#~builtin::version=1.22.2&hash=c3c19d" - dependencies: - is-core-module: ^2.11.0 - path-parse: ^1.0.7 - supports-preserve-symlinks-flag: ^1.0.0 - bin: - resolve: bin/resolve - checksum: 66cc788f13b8398de18eb4abb3aed90435c84bb8935953feafcf7231ba4cd191b2c10b4a87b1e9681afc34fb138c705f91f7330ff90bfa36f457e5584076a2b8 - languageName: node - linkType: hard - -"resolve@patch:resolve@^1.22.2#~builtin": +"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.1.7#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.11.1#~builtin, resolve@patch:resolve@^1.12.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.22.2#~builtin, resolve@patch:resolve@^1.3.2#~builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -36614,7 +36186,7 @@ __metadata: "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/emitter": ~0.31.25 "@rocket.chat/icons": ^0.33.0 - "@rocket.chat/message-parser": ~0.31.28 + "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/model-typings": "workspace:^" "@rocket.chat/models": "workspace:^" "@rocket.chat/rest-typings": "workspace:^" @@ -36975,7 +36547,7 @@ __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, schema-utils@npm:^3.2.0": version: 3.3.0 resolution: "schema-utils@npm:3.3.0" dependencies: @@ -37444,6 +37016,18 @@ __metadata: languageName: node linkType: hard +"shiki@npm:^0.14.1": + version: 0.14.7 + resolution: "shiki@npm:0.14.7" + dependencies: + ansi-sequence-parser: ^1.1.0 + jsonc-parser: ^3.2.0 + vscode-oniguruma: ^1.7.0 + vscode-textmate: ^8.0.0 + checksum: 2aec3b3519df977c4391df9e1825cb496e9a4d7e11395f05a0da77e4fa2f7c3d9d6e6ee94029ac699533017f2b25637ee68f6d39f05f311535c2704d0329b520 + languageName: node + linkType: hard + "shimmer@npm:^1.1.0, shimmer@npm:^1.2.0": version: 1.2.1 resolution: "shimmer@npm:1.2.1" @@ -37763,6 +37347,13 @@ __metadata: languageName: node linkType: hard +"source-map-generator@npm:0.8.0": + version: 0.8.0 + resolution: "source-map-generator@npm:0.8.0" + checksum: 15e7854c04a4b1bc8a477664a65c58316d792fb24a7b427219ac597eed7f57a3905637c783126b123cf05f9d4fdc45a8fadb8bc2fbf010a82d92e35c193aba51 + languageName: node + linkType: hard + "source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.2": version: 1.0.2 resolution: "source-map-js@npm:1.0.2" @@ -39326,15 +38917,15 @@ __metadata: languageName: node linkType: hard -"terser-webpack-plugin@npm:^5.0.3, terser-webpack-plugin@npm:^5.3.7": - version: 5.3.9 - resolution: "terser-webpack-plugin@npm:5.3.9" +"terser-webpack-plugin@npm:^5.0.3, terser-webpack-plugin@npm:^5.1.3, terser-webpack-plugin@npm:^5.3.7": + version: 5.3.10 + resolution: "terser-webpack-plugin@npm:5.3.10" dependencies: - "@jridgewell/trace-mapping": ^0.3.17 + "@jridgewell/trace-mapping": ^0.3.20 jest-worker: ^27.4.5 schema-utils: ^3.1.1 serialize-javascript: ^6.0.1 - terser: ^5.16.8 + terser: ^5.26.0 peerDependencies: webpack: ^5.1.0 peerDependenciesMeta: @@ -39344,7 +38935,7 @@ __metadata: optional: true uglify-js: optional: true - checksum: 41705713d6f9cb83287936b21e27c658891c78c4392159f5148b5623f0e8c48559869779619b058382a4c9758e7820ea034695e57dc7c474b4962b79f553bc5f + checksum: bd6e7596cf815f3353e2a53e79cbdec959a1b0276f5e5d4e63e9d7c3c5bb5306df567729da287d1c7b39d79093e56863c569c42c6c24cc34c76aa313bd2cbcea languageName: node linkType: hard @@ -39361,9 +38952,9 @@ __metadata: languageName: node linkType: hard -"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" +"terser@npm:^5.10.0, terser@npm:^5.26.0, terser@npm:^5.3.4": + version: 5.27.2 + resolution: "terser@npm:5.27.2" dependencies: "@jridgewell/source-map": ^0.3.3 acorn: ^8.8.2 @@ -39371,7 +38962,7 @@ __metadata: source-map-support: ~0.5.20 bin: terser: bin/terser - checksum: d01eb9805a978b3338b68fd2d9e35c1cd4cad78ea093dc92c7b3c38965232f0af0f95e0c6d90920ecf600a74135c608aebae26302c036c01393a590e1918bb90 + checksum: 0da083942b10e79b2ed20947c8ebb8dbef729096afdcb82b2f0d730801fb416fd6b1fb4a2869b39679b06cb9b5f27be6d3ffac0c77f329822bd9a4e72016660a languageName: node linkType: hard @@ -39838,40 +39429,7 @@ __metadata: languageName: node linkType: hard -"ts-jest@npm:^29.1.1, ts-jest@npm:~29.1.1": - version: 29.1.1 - resolution: "ts-jest@npm:29.1.1" - dependencies: - bs-logger: 0.x - fast-json-stable-stringify: 2.x - jest-util: ^29.0.0 - json5: ^2.2.3 - lodash.memoize: 4.x - make-error: 1.x - semver: ^7.5.3 - yargs-parser: ^21.0.1 - peerDependencies: - "@babel/core": ">=7.0.0-beta.0 <8" - "@jest/types": ^29.0.0 - babel-jest: ^29.0.0 - jest: ^29.0.0 - typescript: ">=4.3 <6" - peerDependenciesMeta: - "@babel/core": - optional: true - "@jest/types": - optional: true - babel-jest: - optional: true - esbuild: - optional: true - bin: - ts-jest: cli.js - checksum: a8c9e284ed4f819526749f6e4dc6421ec666f20ab44d31b0f02b4ed979975f7580b18aea4813172d43e39b29464a71899f8893dd29b06b4a351a3af8ba47b402 - languageName: node - linkType: hard - -"ts-jest@npm:^29.1.2": +"ts-jest@npm:^29.1.1, ts-jest@npm:^29.1.2, ts-jest@npm:~29.1.0, ts-jest@npm:~29.1.1": version: 29.1.2 resolution: "ts-jest@npm:29.1.2" dependencies: @@ -40433,6 +39991,22 @@ __metadata: languageName: node linkType: hard +"typedoc@npm:~0.24.1": + version: 0.24.8 + resolution: "typedoc@npm:0.24.8" + dependencies: + lunr: ^2.3.9 + marked: ^4.3.0 + minimatch: ^9.0.0 + shiki: ^0.14.1 + peerDependencies: + typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x + bin: + typedoc: bin/typedoc + checksum: a46a14497f789fb3594e6c3af2e45276934ac46df40b7ed15a504ee51dc7a8013a2ffb3a54fd73abca6a2b71f97d3ec9ad356fa9aa81d29743e4645a965a2ae0 + languageName: node + linkType: hard + "typescript@npm:^5.3.2, typescript@npm:~5.3.2": version: 5.3.2 resolution: "typescript@npm:5.3.2" @@ -40443,13 +40017,13 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.3.3": - version: 5.3.3 - resolution: "typescript@npm:5.3.3" +"typescript@npm:~5.0.4": + version: 5.0.4 + resolution: "typescript@npm:5.0.4" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 2007ccb6e51bbbf6fde0a78099efe04dc1c3dfbdff04ca3b6a8bc717991862b39fd6126c0c3ebf2d2d98ac5e960bcaa873826bb2bb241f14277034148f41f6a2 + checksum: 82b94da3f4604a8946da585f7d6c3025fff8410779e5bde2855ab130d05e4fd08938b9e593b6ebed165bda6ad9292b230984f10952cf82f0a0ca07bbeaa08172 languageName: node linkType: hard @@ -40463,13 +40037,13 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@^5.3.3#~builtin": - version: 5.3.3 - resolution: "typescript@patch:typescript@npm%3A5.3.3#~builtin::version=5.3.3&hash=85af82" +"typescript@patch:typescript@~5.0.4#~builtin": + version: 5.0.4 + resolution: "typescript@patch:typescript@npm%3A5.0.4#~builtin::version=5.0.4&hash=85af82" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: f61375590b3162599f0f0d5b8737877ac0a7bc52761dbb585d67e7b8753a3a4c42d9a554c4cc929f591ffcf3a2b0602f65ae3ce74714fd5652623a816862b610 + checksum: bb309d320c59a26565fb3793dba550576ab861018ff3fd1b7fccabbe46ae4a35546bc45f342c0a0b6f265c801ccdf64ffd68f548f117ceb7f0eac4b805cd52a9 languageName: node linkType: hard @@ -40903,20 +40477,6 @@ __metadata: languageName: node linkType: hard -"update-browserslist-db@npm:^1.0.11": - version: 1.0.11 - resolution: "update-browserslist-db@npm:1.0.11" - dependencies: - escalade: ^3.1.1 - picocolors: ^1.0.0 - peerDependencies: - browserslist: ">= 4.21.0" - bin: - update-browserslist-db: cli.js - checksum: b98327518f9a345c7cad5437afae4d2ae7d865f9779554baf2a200fdf4bac4969076b679b1115434bd6557376bdd37ca7583d0f9b8f8e302d7d4cc1e91b5f231 - languageName: node - linkType: hard - "update-browserslist-db@npm:^1.0.13": version: 1.0.13 resolution: "update-browserslist-db@npm:1.0.13" @@ -41555,6 +41115,20 @@ __metadata: languageName: node linkType: hard +"vscode-oniguruma@npm:^1.7.0": + version: 1.7.0 + resolution: "vscode-oniguruma@npm:1.7.0" + checksum: 53519d91d90593e6fb080260892e87d447e9b200c4964d766772b5053f5699066539d92100f77f1302c91e8fc5d9c772fbe40fe4c90f3d411a96d5a9b1e63f42 + languageName: node + linkType: hard + +"vscode-textmate@npm:^8.0.0": + version: 8.0.0 + resolution: "vscode-textmate@npm:8.0.0" + checksum: 127780dfea89559d70b8326df6ec344cfd701312dd7f3f591a718693812b7852c30b6715e3cfc8b3200a4e2515b4c96f0843c0eacc0a3020969b5de262c2a4bb + languageName: node + linkType: hard + "w3c-hr-time@npm:^1.0.2": version: 1.0.2 resolution: "w3c-hr-time@npm:1.0.2" @@ -41802,6 +41376,38 @@ __metadata: languageName: node linkType: hard +"webpack-cli@npm:~5.0.1": + version: 5.0.2 + resolution: "webpack-cli@npm:5.0.2" + dependencies: + "@discoveryjs/json-ext": ^0.5.0 + "@webpack-cli/configtest": ^2.0.1 + "@webpack-cli/info": ^2.0.1 + "@webpack-cli/serve": ^2.0.2 + colorette: ^2.0.14 + commander: ^10.0.1 + cross-spawn: ^7.0.3 + envinfo: ^7.7.3 + fastest-levenshtein: ^1.0.12 + import-local: ^3.0.2 + interpret: ^3.1.1 + rechoir: ^0.8.0 + webpack-merge: ^5.7.3 + peerDependencies: + webpack: 5.x.x + peerDependenciesMeta: + "@webpack-cli/generators": + optional: true + webpack-bundle-analyzer: + optional: true + webpack-dev-server: + optional: true + bin: + webpack-cli: bin/cli.js + checksum: 98816d84c51487b90e1008ddbcda8827dcb7ae9ab7892f4d742f0d7b93f90a4a18a6ec2671b380e5a7ee4b0917cf0c43921119419d8ba9e1b14ea060115684fd + languageName: node + linkType: hard + "webpack-dev-middleware@npm:^3.7.3": version: 3.7.3 resolution: "webpack-dev-middleware@npm:3.7.3" @@ -42091,6 +41697,43 @@ __metadata: languageName: node linkType: hard +"webpack@npm:~5.78.0": + version: 5.78.0 + resolution: "webpack@npm:5.78.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: 4213e5bcc23e54c2f2a589e8e96f1fb71a2c05d5033ffda6dd8bae32284abfa0eb6b6d0707806e8dcfa48a8fcda2448d3af6c4539061679251d94c0996bebf99 + 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" From 96273d12db7ca265d96060dc425b059fdc4dda82 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Wed, 21 Feb 2024 18:14:44 -0300 Subject: [PATCH 058/207] test: make apps api tests fully independent (#31787) --- .../tests/end-to-end/apps/00-installation.js | 4 +- .../tests/end-to-end/apps/01-send-messages.js | 94 +++++++------ .../apps/02-send-messages-as-user.js | 127 ++++++++++-------- .../apps/03-slash-command-test-simple.js | 4 +- .../04-slash-command-test-with-arguments.js | 4 +- .../end-to-end/apps/05-video-conferences.ts | 6 +- .../tests/end-to-end/apps/apps-uninstall.js | 4 +- 7 files changed, 141 insertions(+), 102 deletions(-) diff --git a/apps/meteor/tests/end-to-end/apps/00-installation.js b/apps/meteor/tests/end-to-end/apps/00-installation.js index 06eab838bffd..f8633439a702 100644 --- a/apps/meteor/tests/end-to-end/apps/00-installation.js +++ b/apps/meteor/tests/end-to-end/apps/00-installation.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { after, before, describe, it } from 'mocha'; import { getCredentials, request, credentials, api } from '../../data/api-data.js'; import { APP_URL, apps, APP_USERNAME } from '../../data/apps/apps-data.js'; @@ -14,6 +14,8 @@ describe('Apps - Installation', function () { before(async () => cleanupApps()); + after(() => Promise.all([cleanupApps(), updatePermission('manage-apps', ['admin'])])); + describe('[Installation]', () => { it('should throw an error when trying to install an app and the apps framework is enabled but the user does not have the permission', (done) => { updatePermission('manage-apps', []).then(() => { diff --git a/apps/meteor/tests/end-to-end/apps/01-send-messages.js b/apps/meteor/tests/end-to-end/apps/01-send-messages.js index c81cf121df23..3f729715915f 100644 --- a/apps/meteor/tests/end-to-end/apps/01-send-messages.js +++ b/apps/meteor/tests/end-to-end/apps/01-send-messages.js @@ -1,11 +1,11 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { after, before, describe, it } from 'mocha'; import { getCredentials, request, credentials } from '../../data/api-data.js'; import { apps } from '../../data/apps/apps-data.js'; import { cleanupApps, installTestApp } from '../../data/apps/helper.js'; import { getMessageById } from '../../data/chat.helper.js'; -import { createRoom } from '../../data/rooms.helper'; +import { createRoom, deleteRoom } from '../../data/rooms.helper'; describe('Apps - Send Messages As APP User', function () { this.retries(0); @@ -17,6 +17,8 @@ describe('Apps - Send Messages As APP User', function () { app = await installTestApp(); }); + after(() => cleanupApps()); + describe('[Send Message as app user]', () => { it('should return an error when the room is not found', (done) => { request @@ -58,25 +60,33 @@ describe('Apps - Send Messages As APP User', function () { }); describe('Send to a Private Channel', () => { let privateMessageId; + let group; + + before(async () => { + group = ( + await createRoom({ + type: 'p', + name: `apps-e2etest-room-${Date.now()}`, + }) + ).body.group; + }); + + after(() => deleteRoom({ type: 'p', roomId: group._id })); + it('should send a message as app user', (done) => { - createRoom({ - type: 'p', - name: `apps-e2etest-room-${Date.now()}`, - }).end((err, createdRoom) => { - request - .post(apps(`/public/${app.id}/send-message-as-app-user`)) - .set(credentials) - .send({ - roomId: createdRoom.body.group._id, - }) - .expect(200) - .expect((res) => { - const response = JSON.parse(res.text); - expect(response).to.have.a.property('messageId'); - privateMessageId = response.messageId; - }) - .end(done); - }); + request + .post(apps(`/public/${app.id}/send-message-as-app-user`)) + .set(credentials) + .send({ + roomId: group._id, + }) + .expect(200) + .expect((res) => { + const response = JSON.parse(res.text); + expect(response).to.have.a.property('messageId'); + privateMessageId = response.messageId; + }) + .end(done); }); it('should be a valid message', async () => { const message = await getMessageById({ msgId: privateMessageId }); @@ -85,25 +95,33 @@ describe('Apps - Send Messages As APP User', function () { }); describe('Send to a DM Channel', () => { let DMMessageId; + let dmRoom; + + before(async () => { + dmRoom = ( + await createRoom({ + type: 'd', + username: 'rocket.cat', + }) + ).body.room; + }); + + after(() => deleteRoom({ type: 'd', roomId: dmRoom._id })); + it('should send a message as app user', (done) => { - createRoom({ - type: 'd', - username: 'rocket.cat', - }).end((err, createdRoom) => { - request - .post(apps(`/public/${app.id}/send-message-as-app-user`)) - .set(credentials) - .send({ - roomId: createdRoom.body.room._id, - }) - .expect(200) - .expect((res) => { - const response = JSON.parse(res.text); - expect(response).to.have.a.property('messageId'); - DMMessageId = response.messageId; - }) - .end(done); - }); + request + .post(apps(`/public/${app.id}/send-message-as-app-user`)) + .set(credentials) + .send({ + roomId: dmRoom._id, + }) + .expect(200) + .expect((res) => { + const response = JSON.parse(res.text); + expect(response).to.have.a.property('messageId'); + DMMessageId = response.messageId; + }) + .end(done); }); it('should be a valid message', async () => { const message = await getMessageById({ msgId: DMMessageId }); diff --git a/apps/meteor/tests/end-to-end/apps/02-send-messages-as-user.js b/apps/meteor/tests/end-to-end/apps/02-send-messages-as-user.js index 98279d6093a0..a0be32734306 100644 --- a/apps/meteor/tests/end-to-end/apps/02-send-messages-as-user.js +++ b/apps/meteor/tests/end-to-end/apps/02-send-messages-as-user.js @@ -1,13 +1,13 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { after, before, describe, it } from 'mocha'; import { getCredentials, request, credentials } from '../../data/api-data.js'; import { apps } from '../../data/apps/apps-data.js'; import { cleanupApps, installTestApp } from '../../data/apps/helper.js'; import { getMessageById } from '../../data/chat.helper.js'; -import { createRoom } from '../../data/rooms.helper'; +import { createRoom, deleteRoom } from '../../data/rooms.helper'; import { adminUsername, password } from '../../data/user'; -import { createUser, login } from '../../data/users.helper.js'; +import { createUser, deleteUser, login } from '../../data/users.helper.js'; describe('Apps - Send Messages As User', function () { this.retries(0); @@ -19,6 +19,8 @@ describe('Apps - Send Messages As User', function () { app = await installTestApp(); }); + after(() => cleanupApps()); + describe('[Send Message as user]', () => { it('should return an error when the room is not found', (done) => { request @@ -76,46 +78,47 @@ describe('Apps - Send Messages As User', function () { }); describe('Send to a Private Channel', () => { let privateMessageId; + let group; + let user; + let userCredentials; + + before(async () => { + group = ( + await createRoom({ + type: 'p', + name: `apps-e2etest-room-${Date.now()}`, + }) + ).body.group; + user = await createUser(); + userCredentials = await login(user.username, password); + }); + + after(() => Promise.all([deleteRoom({ type: 'p', roomId: group._id }), deleteUser(user)])); + it('should send a message as app user', (done) => { - createRoom({ - type: 'p', - name: `apps-e2etest-room-${Date.now()}`, - }).end((err, createdRoom) => { - createUser().then((createdUser) => { - const user = createdUser; - login(user.username, password).then((credentials) => { - const userCredentials = credentials; - request - .post(apps(`/public/${app.id}/send-message-as-user?userId=${user._id}`)) - .set(userCredentials) - .send({ - roomId: createdRoom.body.group._id, - }) - .expect(500) - .end(done); - }); - }); - }); + request + .post(apps(`/public/${app.id}/send-message-as-user?userId=${user._id}`)) + .set(userCredentials) + .send({ + roomId: group._id, + }) + .expect(500) + .end(done); }); it('should send a message as app user', (done) => { - createRoom({ - type: 'p', - name: `apps-e2etest-room-${Date.now()}`, - }).end((err, createdRoom) => { - request - .post(apps(`/public/${app.id}/send-message-as-user?userId=${adminUsername}`)) - .set(credentials) - .send({ - roomId: createdRoom.body.group._id, - }) - .expect(200) - .expect((res) => { - const response = JSON.parse(res.text); - expect(response).to.have.a.property('messageId'); - privateMessageId = response.messageId; - }) - .end(done); - }); + request + .post(apps(`/public/${app.id}/send-message-as-user?userId=${adminUsername}`)) + .set(credentials) + .send({ + roomId: group._id, + }) + .expect(200) + .expect((res) => { + const response = JSON.parse(res.text); + expect(response).to.have.a.property('messageId'); + privateMessageId = response.messageId; + }) + .end(done); }); it('should be a valid message', async () => { const message = await getMessageById({ msgId: privateMessageId }); @@ -124,25 +127,33 @@ describe('Apps - Send Messages As User', function () { }); describe('Send to a DM Channel', () => { let DMMessageId; + let dmRoom; + + before(async () => { + dmRoom = ( + await createRoom({ + type: 'd', + username: 'rocket.cat', + }) + ).body.room; + }); + + after(() => deleteRoom({ type: 'd', roomId: dmRoom._id })); + it('should send a message as app user', (done) => { - createRoom({ - type: 'd', - username: 'rocket.cat', - }).end((err, createdRoom) => { - request - .post(apps(`/public/${app.id}/send-message-as-user?userId=${adminUsername}`)) - .set(credentials) - .send({ - roomId: createdRoom.body.room._id, - }) - .expect(200) - .expect((res) => { - const response = JSON.parse(res.text); - expect(response).to.have.a.property('messageId'); - DMMessageId = response.messageId; - }) - .end(done); - }); + request + .post(apps(`/public/${app.id}/send-message-as-user?userId=${adminUsername}`)) + .set(credentials) + .send({ + roomId: dmRoom._id, + }) + .expect(200) + .expect((res) => { + const response = JSON.parse(res.text); + expect(response).to.have.a.property('messageId'); + DMMessageId = response.messageId; + }) + .end(done); }); it('should be a valid message', async () => { const message = await getMessageById({ msgId: DMMessageId }); diff --git a/apps/meteor/tests/end-to-end/apps/03-slash-command-test-simple.js b/apps/meteor/tests/end-to-end/apps/03-slash-command-test-simple.js index 5e074c1aa6cf..64f25778fac2 100644 --- a/apps/meteor/tests/end-to-end/apps/03-slash-command-test-simple.js +++ b/apps/meteor/tests/end-to-end/apps/03-slash-command-test-simple.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { after, before, describe, it } from 'mocha'; import { getCredentials, request, credentials, api } from '../../data/api-data.js'; import { cleanupApps, installTestApp } from '../../data/apps/helper.js'; @@ -13,6 +13,8 @@ describe('Apps - Slash Command "test-simple"', function () { await installTestApp(); }); + after(() => cleanupApps()); + describe('[Slash command "test-simple"]', () => { it('should return an error when no command is provided', (done) => { request diff --git a/apps/meteor/tests/end-to-end/apps/04-slash-command-test-with-arguments.js b/apps/meteor/tests/end-to-end/apps/04-slash-command-test-with-arguments.js index 030ee5013e31..58ee4ab0d9c8 100644 --- a/apps/meteor/tests/end-to-end/apps/04-slash-command-test-with-arguments.js +++ b/apps/meteor/tests/end-to-end/apps/04-slash-command-test-with-arguments.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { after, before, describe, it } from 'mocha'; import { getCredentials, request, credentials, api } from '../../data/api-data.js'; import { cleanupApps, installTestApp } from '../../data/apps/helper.js'; @@ -13,6 +13,8 @@ describe('Apps - Slash Command "test-with-arguments"', function () { await installTestApp(); }); + after(() => cleanupApps()); + describe('[Slash command "test-with-arguments"]', () => { const params = 'argument'; it('should execute the slash command successfully', (done) => { diff --git a/apps/meteor/tests/end-to-end/apps/05-video-conferences.ts b/apps/meteor/tests/end-to-end/apps/05-video-conferences.ts index e15a535ee3ad..579e9894ab7b 100644 --- a/apps/meteor/tests/end-to-end/apps/05-video-conferences.ts +++ b/apps/meteor/tests/end-to-end/apps/05-video-conferences.ts @@ -1,11 +1,11 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { after, before, describe, it } from 'mocha'; import type { Response } from 'supertest'; import { getCredentials, request, api, credentials } from '../../data/api-data.js'; import { cleanupApps, installTestApp } from '../../data/apps/helper.js'; import { updateSetting } from '../../data/permissions.helper'; -import { createRoom } from '../../data/rooms.helper'; +import { createRoom, deleteRoom } from '../../data/rooms.helper'; import { adminUsername } from '../../data/user'; describe('Apps - Video Conferences', function () { @@ -30,6 +30,8 @@ describe('Apps - Video Conferences', function () { roomId = res.body.group._id; }); + after(() => Promise.all([cleanupApps(), deleteRoom({ type: 'p', roomId }), updateSetting('VideoConf_Default_Provider', '')])); + describe('[With No App]', () => { before(async () => { await cleanupApps(); diff --git a/apps/meteor/tests/end-to-end/apps/apps-uninstall.js b/apps/meteor/tests/end-to-end/apps/apps-uninstall.js index e00f179dd601..15130c79e129 100644 --- a/apps/meteor/tests/end-to-end/apps/apps-uninstall.js +++ b/apps/meteor/tests/end-to-end/apps/apps-uninstall.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { before, describe, it } from 'mocha'; +import { after, before, describe, it } from 'mocha'; import { getCredentials, request, credentials } from '../../data/api-data.js'; import { apps } from '../../data/apps/apps-data.js'; @@ -16,6 +16,8 @@ describe('Apps - Uninstall', function () { app = await installTestApp(); }); + after(() => cleanupApps()); + describe('[Uninstall]', () => { it('should throw an error when trying to uninstall an invalid app', (done) => { request From f8e6801baa342a613a675be2471d927998484d28 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Thu, 22 Feb 2024 14:35:32 -0300 Subject: [PATCH 059/207] bump meteor 2.15 (#31799) --- apps/meteor/.meteor/packages | 6 +++--- apps/meteor/.meteor/release | 2 +- apps/meteor/.meteor/versions | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/meteor/.meteor/packages b/apps/meteor/.meteor/packages index fd33bef3892f..8107c249add2 100644 --- a/apps/meteor/.meteor/packages +++ b/apps/meteor/.meteor/packages @@ -15,7 +15,7 @@ rocketchat:streamer rocketchat:version rocketchat:user-presence -accounts-base@2.2.9 +accounts-base@2.2.10 accounts-facebook@1.3.3 accounts-github@1.5.0 accounts-google@1.4.0 @@ -37,12 +37,12 @@ http@2.0.0 meteor-base@1.5.1 ddp-common@1.4.0 -webapp@1.13.6 +webapp@1.13.8 mongo@1.16.8 reload@1.3.1 -service-configuration@1.3.2 +service-configuration@1.3.3 session@1.2.1 shell-server@0.5.0 diff --git a/apps/meteor/.meteor/release b/apps/meteor/.meteor/release index c500c39d6da2..966586ce54fe 100644 --- a/apps/meteor/.meteor/release +++ b/apps/meteor/.meteor/release @@ -1 +1 @@ -METEOR@2.14 +METEOR@2.15 diff --git a/apps/meteor/.meteor/versions b/apps/meteor/.meteor/versions index 8c135becc110..a4483a5cf40e 100644 --- a/apps/meteor/.meteor/versions +++ b/apps/meteor/.meteor/versions @@ -1,4 +1,4 @@ -accounts-base@2.2.9 +accounts-base@2.2.10 accounts-facebook@1.3.3 accounts-github@1.5.0 accounts-google@1.4.0 @@ -46,7 +46,7 @@ inter-process-messaging@0.1.1 kadira:flow-router@2.12.1 localstorage@1.2.0 logging@1.3.3 -meteor@1.11.4 +meteor@1.11.5 meteor-base@1.5.1 meteor-developer-oauth@1.3.2 meteorhacks:inject-initial@1.0.5 @@ -84,7 +84,7 @@ rocketchat:streamer@1.1.0 rocketchat:user-presence@2.6.3 rocketchat:version@1.0.0 routepolicy@1.1.1 -service-configuration@1.3.2 +service-configuration@1.3.3 session@1.2.1 sha@1.0.9 shell-server@0.5.0 @@ -93,9 +93,9 @@ standard-minifier-css@1.9.2 tracker@1.3.3 twitter-oauth@1.3.3 typescript@4.9.5 -underscore@1.0.13 +underscore@1.6.0 url@1.3.2 -webapp@1.13.6 +webapp@1.13.8 webapp-hashing@1.1.1 zodern:caching-minifier@0.5.0 zodern:standard-minifier-js@5.3.1 From bf0b7347fa663cbbf0b785ced909159c1e71d01c Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Fri, 23 Feb 2024 00:52:02 +0530 Subject: [PATCH 060/207] fix: Correct display of discussion names in sidebar search (#31701) --- .changeset/rude-avocados-notice.md | 5 ++ apps/meteor/server/lib/spotlight.js | 1 + .../page-objects/fragments/home-sidenav.ts | 8 ++++ .../tests/e2e/search-discussion.spec.ts | 47 +++++++++++++++++++ .../tests/e2e/utils/create-target-channel.ts | 11 +++++ .../tests/e2e/utils/getSettingValueById.ts | 7 +++ 6 files changed, 79 insertions(+) create mode 100644 .changeset/rude-avocados-notice.md create mode 100644 apps/meteor/tests/e2e/search-discussion.spec.ts create mode 100644 apps/meteor/tests/e2e/utils/getSettingValueById.ts diff --git a/.changeset/rude-avocados-notice.md b/.changeset/rude-avocados-notice.md new file mode 100644 index 000000000000..8b8b8715657c --- /dev/null +++ b/.changeset/rude-avocados-notice.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed discussion names displaying as IDs in sidebar search results diff --git a/apps/meteor/server/lib/spotlight.js b/apps/meteor/server/lib/spotlight.js index 62218bd5928f..dc182ee9d3a6 100644 --- a/apps/meteor/server/lib/spotlight.js +++ b/apps/meteor/server/lib/spotlight.js @@ -34,6 +34,7 @@ export class Spotlight { joinCodeRequired: 1, lastMessage: 1, federated: true, + prid: 1, }, sort: { name: 1, 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 0a4444c073c6..12a38f3b6185 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-sidenav.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-sidenav.ts @@ -33,6 +33,10 @@ export class HomeSidenav { return this.page.locator('role=button[name="Create"]'); } + get inputSearch(): Locator { + return this.page.locator('[placeholder="Search (Ctrl+K)"]').first(); + } + getSidebarItemByName(name: string): Locator { return this.page.locator(`[data-qa="sidebar-item"][aria-label="${name}"]`); } @@ -59,6 +63,10 @@ export class HomeSidenav { await this.page.locator(`role=menuitem[name="${text}"]`).click(); } + async openSearch(): Promise { + await this.page.locator('role=button[name="Search"]').click(); + } + async logout(): Promise { await this.page.locator('[data-qa="sidebar-avatar-button"]').click(); await this.page.locator('//*[contains(@class, "rcx-option__content") and contains(text(), "Logout")]').click(); diff --git a/apps/meteor/tests/e2e/search-discussion.spec.ts b/apps/meteor/tests/e2e/search-discussion.spec.ts new file mode 100644 index 000000000000..e14813bc2dc3 --- /dev/null +++ b/apps/meteor/tests/e2e/search-discussion.spec.ts @@ -0,0 +1,47 @@ +import { Page } from '@playwright/test'; + +import { Users } from './fixtures/userStates'; +import { HomeChannel } from './page-objects'; +import { createTargetDiscussion } from './utils'; +import { getSettingValueById } from './utils/getSettingValueById'; +import { setSettingValueById } from './utils/setSettingValueById'; +import { test, expect } from './utils/test'; + +test.use({ storageState: Users.user1.state }); + +test.describe.serial('search-discussion', () => { + let settingDefaultValue: unknown; + let poHomeChannel: HomeChannel; + let discussionName: string; + + test.beforeAll(async ({ api }) => { + settingDefaultValue = await getSettingValueById(api, 'UI_Allow_room_names_with_special_chars'); + }); + + test.beforeEach(async ({ page, api }) => { + discussionName = await createTargetDiscussion(api); + poHomeChannel = new HomeChannel(page); + await page.goto('/home'); + }); + + test.afterAll(async ({ api }) => { + await setSettingValueById(api, 'UI_Allow_room_names_with_special_chars', settingDefaultValue); + }); + + const testDiscussionSearch = async (page: Page) => { + await poHomeChannel.sidenav.openSearch(); + await poHomeChannel.sidenav.inputSearch.type(discussionName); + const targetSearchItem = page.locator('role=listbox').getByText(discussionName).first(); + await expect(targetSearchItem).toBeVisible(); + } + + test('expect search discussion to show fname when UI_Allow_room_names_with_special_chars=true', async ({ page, api }) => { + await setSettingValueById(api, 'UI_Allow_room_names_with_special_chars', true); + await testDiscussionSearch(page); + }); + + test('expect search discussion to show fname when UI_Allow_room_names_with_special_chars=false', async ({ page, api }) => { + await setSettingValueById(api, 'UI_Allow_room_names_with_special_chars', false); + await testDiscussionSearch(page); + }); +}); diff --git a/apps/meteor/tests/e2e/utils/create-target-channel.ts b/apps/meteor/tests/e2e/utils/create-target-channel.ts index 856972eeffb8..ce145f4233bd 100644 --- a/apps/meteor/tests/e2e/utils/create-target-channel.ts +++ b/apps/meteor/tests/e2e/utils/create-target-channel.ts @@ -33,3 +33,14 @@ export async function createDirectMessage(api: BaseTest['api']): Promise { usernames: 'user1,user2', }); } + +export async function createTargetDiscussion(api: BaseTest['api']): Promise { + const channelName = faker.string.uuid(); + const discussionName = faker.string.uuid(); + + const response = await api.post('/channels.create', { name: channelName }); + const { channel } = await response.json(); + await api.post('/rooms.createDiscussion', { t_name: discussionName, prid: channel._id}); + + return discussionName; +} diff --git a/apps/meteor/tests/e2e/utils/getSettingValueById.ts b/apps/meteor/tests/e2e/utils/getSettingValueById.ts new file mode 100644 index 000000000000..a4737024a5e6 --- /dev/null +++ b/apps/meteor/tests/e2e/utils/getSettingValueById.ts @@ -0,0 +1,7 @@ +import type { BaseTest } from './test'; + +export const getSettingValueById = async (api: BaseTest['api'], settingId: string): Promise => { + const response = await api.get(`/settings/${settingId}`); + const { value } = await response.json(); + return value; +}; From 1f93a8a15c9dc17830f1e6379381efe82315835f Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Fri, 23 Feb 2024 10:36:59 -0300 Subject: [PATCH 061/207] test: make calendar api tests fully independent (#31785) --- .../tests/end-to-end/api/30-calendar.ts | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/30-calendar.ts b/apps/meteor/tests/end-to-end/api/30-calendar.ts index 73c534d98ebd..6ea4947fa9db 100644 --- a/apps/meteor/tests/end-to-end/api/30-calendar.ts +++ b/apps/meteor/tests/end-to-end/api/30-calendar.ts @@ -4,7 +4,7 @@ import type { Response } from 'supertest'; import { getCredentials, api, request, credentials } from '../../data/api-data.js'; import { password } from '../../data/user'; -import { createUser, login } from '../../data/users.helper'; +import { createUser, deleteUser, login } from '../../data/users.helper'; describe('[Calendar Events]', function () { this.retries(0); @@ -19,6 +19,8 @@ describe('[Calendar Events]', function () { userCredentials = await login(user2.username, password); }); + after(() => deleteUser(user2)); + describe('[/calendar-events.create]', () => { it('should successfully create an event in the calendar', async () => { let eventId: string | undefined; @@ -155,11 +157,13 @@ describe('[Calendar Events]', function () { }); }); - after(async () => { - 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 }); - }); + after(() => + Promise.all([ + request.post(api('calendar-events.delete')).set(credentials).send({ eventId }), + request.post(api('calendar-events.delete')).set(credentials).send({ eventId: eventId2 }), + request.post(api('calendar-events.delete')).set(userCredentials).send({ eventId: eventId3 }), + ]), + ); it('should list only the events with the same date', async () => { await request @@ -233,10 +237,12 @@ describe('[Calendar Events]', function () { }); }); - after(async () => { - await request.post(api('calendar-events.delete')).set(credentials).send({ eventId }); - await request.post(api('calendar-events.delete')).set(userCredentials).send({ eventId: eventId2 }); - }); + after(() => + Promise.all([ + request.post(api('calendar-events.delete')).set(credentials).send({ eventId }), + request.post(api('calendar-events.delete')).set(userCredentials).send({ eventId: eventId2 }), + ]), + ); it('should return the event information', async () => { await request @@ -292,6 +298,14 @@ describe('[Calendar Events]', function () { }); describe('[/calendar-events.import]', () => { + const eventsToRemove: Record[] = []; + + after(() => + Promise.all([ + ...eventsToRemove.map(({ credentials, eventId }) => request.post(api('calendar-events.delete')).set(credentials).send({ eventId })), + ]), + ); + it('should successfully import an event to the calendar', async () => { let eventId: string | undefined; const externalId = `calendar-events.import-${Date.now()}`; @@ -311,11 +325,8 @@ describe('[Calendar Events]', function () { .expect((res: Response) => { expect(res.body).to.have.property('success', true); eventId = res.body.id; + eventsToRemove.push({ credentials, eventId }); }); - - after(async () => { - await request.post(api('calendar-events.delete')).set(credentials).send({ eventId }); - }); }); it('should fail to import an event without an external id', async () => { @@ -400,11 +411,6 @@ describe('[Calendar Events]', function () { let eventId2: string | undefined; const externalId = `calendar-events.import-${Date.now()}`; - after(async () => { - 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) @@ -417,6 +423,7 @@ describe('[Calendar Events]', function () { }) .then((res) => { eventId = res.body.id; + eventsToRemove.push({ credentials: userCredentials, eventId }); }); await request @@ -435,6 +442,7 @@ describe('[Calendar Events]', function () { expect(res.body).to.have.property('success', true); expect(res.body.id).to.not.be.equal(eventId); eventId2 = res.body.id; + eventsToRemove.push({ credentials, eventId: eventId2 }); }); await request @@ -464,10 +472,6 @@ describe('[Calendar Events]', function () { let eventId: string | undefined; const externalId = `calendar-events.import-twice-${Date.now()}`; - after(async () => { - await request.post(api('calendar-events.delete')).set(credentials).send({ eventId }); - }); - await request .post(api('calendar-events.import')) .set(credentials) @@ -480,6 +484,7 @@ describe('[Calendar Events]', function () { }) .then((res) => { eventId = res.body.id; + eventsToRemove.push({ credentials, eventId }); }); await request From fbe6a49a86ead26b2daba67ae9a19a64ce7bb58c Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 23 Feb 2024 14:49:56 -0300 Subject: [PATCH 062/207] chor: allow offline development build --- .../packages/rocketchat-version/plugin/compile-version.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/meteor/packages/rocketchat-version/plugin/compile-version.js b/apps/meteor/packages/rocketchat-version/plugin/compile-version.js index d22aa55437ff..e737f07663f3 100644 --- a/apps/meteor/packages/rocketchat-version/plugin/compile-version.js +++ b/apps/meteor/packages/rocketchat-version/plugin/compile-version.js @@ -123,6 +123,13 @@ class VersionCompiler { break; } case file.getDisplayPath().endsWith('rocketchat-supported-versions.info'): { + if (process.env.NODE_ENV === 'development') { + file.addJavaScript({ + data: `exports.supportedVersions = {}`, + path: `${file.getPathInPackage()}.js`, + }); + break; + } await processVersionFile(file); break; } From 7d331f115874bb1be9943b571f040c4d6e8fc47b Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Fri, 23 Feb 2024 14:52:26 -0300 Subject: [PATCH 063/207] regression: Prevent storing lastFocused message by click event (#31819) --- .../room/hooks/useMessageListNavigation.ts | 12 ++++++--- apps/meteor/tests/e2e/messaging.spec.ts | 25 +++++++++++++------ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/apps/meteor/client/views/room/hooks/useMessageListNavigation.ts b/apps/meteor/client/views/room/hooks/useMessageListNavigation.ts index f446e867531d..a0ed9c103e4f 100644 --- a/apps/meteor/client/views/room/hooks/useMessageListNavigation.ts +++ b/apps/meteor/client/views/room/hooks/useMessageListNavigation.ts @@ -23,6 +23,7 @@ export const useMessageListNavigation = (): { messageListRef: RefCallback { let lastMessageFocused: HTMLElement | null = null; + let triggeredByKeyboard = false; if (!node) { return; @@ -61,21 +62,22 @@ export const useMessageListNavigation = (): { messageListRef: RefCallback isListItem(node) }); + massageListFocusManager.focusPrevious({ accept: (node) => isListItem(node) }); } if (e.key === 'ArrowDown') { - massageListFocusManager.focusNext({ wrap: true, accept: (node) => isListItem(node) }); + massageListFocusManager.focusNext({ accept: (node) => isListItem(node) }); } lastMessageFocused = document.activeElement as HTMLElement; + triggeredByKeyboard = true; } }); node.addEventListener( 'blur', (e) => { - if (!(e.currentTarget instanceof HTMLElement && e.relatedTarget instanceof HTMLElement)) { + if (!triggeredByKeyboard || !(e.currentTarget instanceof HTMLElement && e.relatedTarget instanceof HTMLElement)) { return; } @@ -89,12 +91,14 @@ export const useMessageListNavigation = (): { messageListRef: RefCallback { - if (!(e.currentTarget instanceof HTMLElement && e.relatedTarget instanceof HTMLElement)) { + if (!triggeredByKeyboard || !(e.currentTarget instanceof HTMLElement && e.relatedTarget instanceof HTMLElement)) { return; } + if (lastMessageFocused && !e.currentTarget.contains(e.relatedTarget) && node.contains(e.target as HTMLElement)) { lastMessageFocused?.focus(); lastMessageFocused = null; + triggeredByKeyboard = false; } }, { capture: true }, diff --git a/apps/meteor/tests/e2e/messaging.spec.ts b/apps/meteor/tests/e2e/messaging.spec.ts index f71c1dd04b8c..a333e3d5b3ce 100644 --- a/apps/meteor/tests/e2e/messaging.spec.ts +++ b/apps/meteor/tests/e2e/messaging.spec.ts @@ -29,8 +29,13 @@ test.describe.serial('Messaging', () => { await page.keyboard.press('Shift+Tab'); await expect(page.locator('[data-qa-type="message"]').last()).toBeFocused(); - // move focus to the first typed message + // move focus to the first system message + await page.keyboard.press('ArrowUp'); await page.keyboard.press('ArrowUp'); + await expect(page.locator('[data-qa="system-message"]').first()).toBeFocused(); + + // move focus to the first typed message + await page.keyboard.press('ArrowDown'); await expect(page.locator('[data-qa-type="message"]:has-text("msg1")')).toBeFocused(); // move focus to the favorite icon @@ -48,17 +53,23 @@ test.describe.serial('Messaging', () => { await page.keyboard.press('Tab'); await page.keyboard.press('Tab'); await expect(page.locator('[data-qa-type="message"]:has-text("msg1")').locator('[role=toolbar][aria-label="Message actions"]').getByRole('button', { name: 'Add reaction' })).toBeFocused(); - - // move focus to the first system message - await page.keyboard.press('Tab'); - await page.keyboard.press('ArrowDown'); - await expect(page.locator('[data-qa="system-message"]').first()).toBeFocused(); - + // move focus to the composer await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); await expect(poHomeChannel.composer).toBeFocused(); }); + test('should not restore focus on the last focused if it was triggered by click', async ({ page }) => { + await poHomeChannel.sidenav.openChat(targetChannel); + await page.locator('[data-qa-type="message"]:has-text("msg1")').click(); + await poHomeChannel.composer.click(); + await page.locator('[data-qa-type="message"]:has-text("msg2")').click(); + + await expect(page.locator('[data-qa-type="message"]:has-text("msg2")')).toBeFocused(); + }); + test('expect show "hello word" in both contexts (targetChannel)', async ({ browser }) => { await poHomeChannel.sidenav.openChat(targetChannel); const { page } = await createAuxContext(browser, Users.user2); From f577751d02dc6fae6337db1b1443d1567695a381 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Fri, 23 Feb 2024 16:30:35 -0300 Subject: [PATCH 064/207] chore: `UserCard` focus improvements (#31812) --- .../client/lib/messageActionDefault.ts | 4 +- apps/meteor/app/ui/client/lib/ChatMessages.ts | 11 ----- .../client/components/GazzodownText.tsx | 9 ++-- .../client/components/UserCard/UserCard.tsx | 47 +++++++++---------- .../components/UserCard/UserCardContainer.tsx | 23 --------- .../components/UserCard/UserCardDialog.tsx | 30 ++++++++++++ .../components/UserCard/UserCardSkeleton.tsx | 12 ++--- .../components/message/MessageHeader.tsx | 19 ++++---- .../message/variants/RoomMessage.tsx | 13 +++-- .../message/variants/SystemMessage.tsx | 43 ++++++----------- .../message/variants/ThreadMessage.tsx | 12 ++--- apps/meteor/client/lib/chats/ChatAPI.ts | 6 --- .../MessageList/ContactHistoryMessage.tsx | 16 ++++--- .../views/room/UserCard/UserCardWithData.tsx | 27 ++++------- .../client/views/room/body/LeaderBar.tsx | 6 ++- .../client/views/room/body/RoomBody.tsx | 7 ++- .../views/room/contexts/UserCardContext.ts | 10 +++- .../ReactionListModal/ReactionListModal.tsx | 7 ++- .../ReactionListModal/ReactionUserTag.tsx | 31 +++--------- .../modals/ReactionListModal/Reactions.tsx | 16 ++----- .../views/room/providers/UserCardProvider.tsx | 30 ++++++++---- .../hooks/useChatMessagesInstance.ts | 2 - apps/meteor/package.json | 2 +- .../rocketchat-i18n/i18n/en.i18n.json | 1 + apps/meteor/tests/e2e/messaging.spec.ts | 26 ++++++++++ .../tests/e2e/page-objects/home-channel.ts | 4 ++ ee/packages/ui-theming/package.json | 2 +- packages/fuselage-ui-kit/package.json | 2 +- packages/gazzodown/package.json | 2 +- .../gazzodown/src/MarkupInteractionContext.ts | 2 + .../src/mentions/ChannelMentionElement.tsx | 14 +++++- .../src/mentions/UserMentionElement.tsx | 11 ++++- packages/ui-avatar/package.json | 2 +- packages/ui-client/package.json | 2 +- packages/ui-composer/package.json | 2 +- packages/ui-video-conf/package.json | 2 +- packages/uikit-playground/package.json | 2 +- yarn.lock | 26 +++++----- 38 files changed, 247 insertions(+), 236 deletions(-) delete mode 100644 apps/meteor/client/components/UserCard/UserCardContainer.tsx create mode 100644 apps/meteor/client/components/UserCard/UserCardDialog.tsx diff --git a/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts b/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts index 834fd0bb05a2..f3cf0be67ca9 100644 --- a/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts +++ b/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts @@ -266,10 +266,10 @@ Meteor.startup(async () => { label: 'Reactions', context: ['message', 'message-mobile', 'threads', 'videoconf', 'videoconf-threads'], type: 'interaction', - action(this: unknown, _, { message: { reactions = {} } = messageArgs(this).msg, chat }) { + action(this: unknown, _, { message: { reactions = {} } = messageArgs(this).msg }) { imperativeModal.open({ component: ReactionListModal, - props: { reactions, onOpenUserCard: chat?.userCard.openUserCard, onClose: imperativeModal.close }, + props: { reactions, onClose: imperativeModal.close }, }); }, condition({ message: { reactions } }) { diff --git a/apps/meteor/app/ui/client/lib/ChatMessages.ts b/apps/meteor/app/ui/client/lib/ChatMessages.ts index 59efeff49af1..5d78fd1e1d98 100644 --- a/apps/meteor/app/ui/client/lib/ChatMessages.ts +++ b/apps/meteor/app/ui/client/lib/ChatMessages.ts @@ -1,7 +1,6 @@ import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; import { isVideoConfMessage } from '@rocket.chat/core-typings'; import type { IActionManager } from '@rocket.chat/ui-contexts'; -import type { UIEvent } from 'react'; import type { ChatAPI, ComposerAPI, DataAPI, UploadsAPI } from '../../../../client/lib/chats/ChatAPI'; import { createDataAPI } from '../../../../client/lib/chats/data'; @@ -45,11 +44,6 @@ export class ChatMessages implements ChatAPI { public ActionManager: any; - public userCard: { - openUserCard(event: UIEvent, username: string): void; - closeUserCard(): void; - }; - public emojiPicker: { open(el: Element, cb: (emoji: string) => void): void; close(): void; @@ -162,11 +156,6 @@ export class ChatMessages implements ChatAPI { this.readStateManager = new ReadStateManager(rid); - this.userCard = { - openUserCard: unimplemented, - closeUserCard: unimplemented, - }; - this.emojiPicker = { open: unimplemented, close: unimplemented, diff --git a/apps/meteor/client/components/GazzodownText.tsx b/apps/meteor/client/components/GazzodownText.tsx index 76232984a594..f69bbe1abdc0 100644 --- a/apps/meteor/client/components/GazzodownText.tsx +++ b/apps/meteor/client/components/GazzodownText.tsx @@ -8,7 +8,7 @@ import React, { useCallback, memo, useMemo } from 'react'; import { detectEmoji } from '../lib/utils/detectEmoji'; import { fireGlobalEvent } from '../lib/utils/fireGlobalEvent'; -import { useChat } from '../views/room/contexts/ChatContext'; +import { useUserCard } from '../views/room/contexts/UserCardContext'; import { useGoToRoom } from '../views/room/hooks/useGoToRoom'; import { useMessageListHighlights } from './message/list/MessageListContext'; @@ -25,8 +25,8 @@ type GazzodownTextProps = { }; const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTextProps) => { - const chat = useChat(); const highlights = useMessageListHighlights(); + const { triggerProps, openUserCard } = useUserCard(); const highlightRegex = useMemo(() => { if (!highlights?.length) { @@ -75,10 +75,10 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe return (event: UIEvent): void => { event.stopPropagation(); - chat?.userCard.openUserCard(event, username); + openUserCard(event, username); }; }, - [chat?.userCard], + [openUserCard], ); const goToRoom = useGoToRoom(); @@ -124,6 +124,7 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe isMobile, ownUserId, showMentionSymbol, + triggerProps, }} > {children} diff --git a/apps/meteor/client/components/UserCard/UserCard.tsx b/apps/meteor/client/components/UserCard/UserCard.tsx index 2d440427ccbd..bf143229cf65 100644 --- a/apps/meteor/client/components/UserCard/UserCard.tsx +++ b/apps/meteor/client/components/UserCard/UserCard.tsx @@ -3,13 +3,13 @@ import { Box, Button, IconButton } from '@rocket.chat/fuselage'; import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactNode, ComponentProps } from 'react'; -import React, { forwardRef } from 'react'; +import React from 'react'; import { useEmbeddedLayout } from '../../hooks/useEmbeddedLayout'; import MarkdownText from '../MarkdownText'; import * as Status from '../UserStatus'; import UserCardActions from './UserCardActions'; -import UserCardContainer from './UserCardContainer'; +import UserCardDialog from './UserCardDialog'; import UserCardInfo from './UserCardInfo'; import UserCardRoles from './UserCardRoles'; import UserCardUsername from './UserCardUsername'; @@ -35,35 +35,32 @@ type UserCardProps = { localTime?: ReactNode; onClose?: () => void; nickname?: string; -} & ComponentProps; +} & ComponentProps; -const UserCard = forwardRef(function UserCard( - { - onOpenUserInfo, - name, - username, - etag, - customStatus, - roles, - bio, - status = , - actions, - localTime, - onClose, - nickname, - ...props - }, - ref, -) { +const UserCard = ({ + onOpenUserInfo, + name, + username, + etag, + customStatus, + roles, + bio, + status = , + actions, + localTime, + onClose, + nickname, + ...props +}: UserCardProps) => { const t = useTranslation(); const isLayoutEmbedded = useEmbeddedLayout(); return ( - +
          {username && } - {actions} + {actions}
          @@ -100,8 +97,8 @@ const UserCard = forwardRef(function UserCard( )} {onClose && } -
          + ); -}); +}; export default UserCard; diff --git a/apps/meteor/client/components/UserCard/UserCardContainer.tsx b/apps/meteor/client/components/UserCard/UserCardContainer.tsx deleted file mode 100644 index 2434077922e3..000000000000 --- a/apps/meteor/client/components/UserCard/UserCardContainer.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Box } from '@rocket.chat/fuselage'; -import type { ComponentProps } from 'react'; -import React, { forwardRef } from 'react'; - -const UserCardContainer = forwardRef(function UserCardContainer(props: ComponentProps, ref) { - return ( - - ); -}); - -export default UserCardContainer; diff --git a/apps/meteor/client/components/UserCard/UserCardDialog.tsx b/apps/meteor/client/components/UserCard/UserCardDialog.tsx new file mode 100644 index 000000000000..0f1c66e4a6e6 --- /dev/null +++ b/apps/meteor/client/components/UserCard/UserCardDialog.tsx @@ -0,0 +1,30 @@ +import { Box } from '@rocket.chat/fuselage'; +import type { ComponentProps } from 'react'; +import React, { useRef } from 'react'; +import type { AriaDialogProps } from 'react-aria'; +import { useDialog } from 'react-aria'; + +type UserCardDialogProps = AriaDialogProps & ComponentProps; + +const UserCardDialog = (props: UserCardDialogProps) => { + const ref = useRef(null); + const { dialogProps } = useDialog(props, ref); + + return ( + + ); +}; + +export default UserCardDialog; diff --git a/apps/meteor/client/components/UserCard/UserCardSkeleton.tsx b/apps/meteor/client/components/UserCard/UserCardSkeleton.tsx index 9339eca8f665..b85f672caa12 100644 --- a/apps/meteor/client/components/UserCard/UserCardSkeleton.tsx +++ b/apps/meteor/client/components/UserCard/UserCardSkeleton.tsx @@ -1,12 +1,12 @@ import { Box, Skeleton } from '@rocket.chat/fuselage'; import type { ComponentProps } from 'react'; -import React, { forwardRef } from 'react'; +import React from 'react'; -import UserCardContainer from './UserCardContainer'; +import UserCardDialog from './UserCardDialog'; -const UserCardSkeleton = forwardRef>(function UserCardSkeleton(props, ref) { +const UserCardSkeleton = (props: ComponentProps) => { return ( - + @@ -27,8 +27,8 @@ const UserCardSkeleton = forwardRef ))} - + ); -}); +}; export default UserCardSkeleton; diff --git a/apps/meteor/client/components/message/MessageHeader.tsx b/apps/meteor/client/components/message/MessageHeader.tsx index d708d933ac6d..a6ad5feaff96 100644 --- a/apps/meteor/client/components/message/MessageHeader.tsx +++ b/apps/meteor/client/components/message/MessageHeader.tsx @@ -16,7 +16,7 @@ import { useFormatDateAndTime } from '../../hooks/useFormatDateAndTime'; import { useFormatTime } from '../../hooks/useFormatTime'; import { useUserData } from '../../hooks/useUserData'; import type { UserPresence } from '../../lib/presence'; -import { useChat } from '../../views/room/contexts/ChatContext'; +import { useUserCard } from '../../views/room/contexts/UserCardContext'; import StatusIndicators from './StatusIndicators'; import MessageRoles from './header/MessageRoles'; import { useMessageRoles } from './header/hooks/useMessageRoles'; @@ -31,6 +31,7 @@ const MessageHeader = ({ message }: MessageHeaderProps): ReactElement => { const formatTime = useFormatTime(); const formatDateAndTime = useFormatDateAndTime(); + const { triggerProps, openUserCard } = useUserCard(); const showRealName = useMessageListShowRealName(); const user: UserPresence = { ...message.u, roles: [], ...useUserData(message.u._id) }; @@ -41,22 +42,18 @@ const MessageHeader = ({ message }: MessageHeaderProps): ReactElement => { const roles = useMessageRoles(message.u._id, message.rid, showRoles); const shouldShowRolesList = roles.length > 0; - const chat = useChat(); - return ( chat?.userCard.openUserCard(e, message.u.username), - onKeyDown: (e: KeyboardEvent) => { - (e.code === 'Enter' || e.code === 'Space') && chat?.userCard.openUserCard(e, message.u.username); - }, - style: { cursor: 'pointer' }, - })} + onClick={(e) => openUserCard(e, message.u.username)} + onKeyDown={(e: KeyboardEvent) => { + (e.code === 'Enter' || e.code === 'Space') && openUserCard(e, message.u.username); + }} + style={{ cursor: 'pointer' }} + {...triggerProps} > chat?.userCard.openUserCard(e, message.u.username), - style: { cursor: 'pointer' }, - })} + onClick={(e) => openUserCard(e, message.u.username)} + style={{ cursor: 'pointer' }} + role='button' + {...triggerProps} /> )} {selecting && } diff --git a/apps/meteor/client/components/message/variants/SystemMessage.tsx b/apps/meteor/client/components/message/variants/SystemMessage.tsx index 9bb254beaec5..d69b1ada10dc 100644 --- a/apps/meteor/client/components/message/variants/SystemMessage.tsx +++ b/apps/meteor/client/components/message/variants/SystemMessage.tsx @@ -14,7 +14,7 @@ import { import { UserAvatar } from '@rocket.chat/ui-avatar'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ComponentProps, ReactElement } from 'react'; +import type { ComponentProps, ReactElement, KeyboardEvent } from 'react'; import React, { memo } from 'react'; import { MessageTypes } from '../../../../app/ui-utils/client'; @@ -29,7 +29,7 @@ import { useIsSelectedMessage, useCountSelected, } from '../../../views/room/MessageList/contexts/SelectedMessagesContext'; -import { useChat } from '../../../views/room/contexts/ChatContext'; +import { useUserCard } from '../../../views/room/contexts/UserCardContext'; import Attachments from '../content/Attachments'; import MessageActions from '../content/MessageActions'; import { useMessageListShowRealName, useMessageListShowUsername } from '../list/MessageListContext'; @@ -43,7 +43,7 @@ const SystemMessage = ({ message, showUserAvatar, ...props }: SystemMessageProps const t = useTranslation(); const formatTime = useFormatTime(); const formatDateAndTime = useFormatDateAndTime(); - const chat = useChat(); + const { triggerProps, openUserCard } = useUserCard(); const showRealName = useMessageListShowRealName(); const user: UserPresence = { ...message.u, roles: [], ...useUserData(message.u._id) }; @@ -74,31 +74,18 @@ const SystemMessage = ({ message, showUserAvatar, ...props }: SystemMessageProps - - user.username && chat?.userCard.openUserCard(e, user.username), - style: { cursor: 'pointer' }, - })} - > - {getUserDisplayName(user.name, user.username, showRealName)} - - {showUsername && ( - <> - {' '} - user.username && chat?.userCard.openUserCard(e, user.username), - style: { cursor: 'pointer' }, - })} - > - @{user.username} - - - )} + user.username && openUserCard(e, user.username)} + onKeyDown={(e: KeyboardEvent) => { + (e.code === 'Enter' || e.code === 'Space') && openUserCard(e, message.u.username); + }} + style={{ cursor: 'pointer' }} + {...triggerProps} + > + {getUserDisplayName(user.name, user.username, showRealName)} + {showUsername && @{user.username}} {messageType && ( chat?.userCard.openUserCard(e, message.u.username), - style: { cursor: 'pointer' }, - })} + onClick={(e) => openUserCard(e, message.u.username)} + style={{ cursor: 'pointer' }} + role='button' + {...triggerProps} /> )} {sequential && } diff --git a/apps/meteor/client/lib/chats/ChatAPI.ts b/apps/meteor/client/lib/chats/ChatAPI.ts index d2364cfcd72c..ceffab987a64 100644 --- a/apps/meteor/client/lib/chats/ChatAPI.ts +++ b/apps/meteor/client/lib/chats/ChatAPI.ts @@ -1,6 +1,5 @@ import type { IMessage, IRoom, ISubscription } from '@rocket.chat/core-typings'; import type { IActionManager } from '@rocket.chat/ui-contexts'; -import type { UIEvent } from 'react'; import type { FormattingButton } from '../../../app/ui-message/client/messageBox/messageBoxFormatting'; import type { Subscribable } from '../../definitions/Subscribable'; @@ -126,11 +125,6 @@ export type ChatAPI = { } | undefined; - readonly userCard: { - openUserCard(event: UIEvent, username: string): void; - closeUserCard(): void; - }; - readonly emojiPicker: { open(el: Element, cb: (emoji: string) => void): void; close(): void; diff --git a/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx b/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx index a1b9ba1ca3af..9c779d1d5ea3 100644 --- a/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx +++ b/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx @@ -29,7 +29,7 @@ import Attachments from '../../../../components/message/content/Attachments'; import UiKitMessageBlock from '../../../../components/message/uikit/UiKitMessageBlock'; import { useFormatDate } from '../../../../hooks/useFormatDate'; import { useFormatTime } from '../../../../hooks/useFormatTime'; -import { useChat } from '../../../room/contexts/ChatContext'; +import { useUserCard } from '../../../room/contexts/UserCardContext'; const ContactHistoryMessage: FC<{ message: IMessage; @@ -37,12 +37,12 @@ const ContactHistoryMessage: FC<{ isNewDay: boolean; showUserAvatar: boolean; }> = ({ message, sequential, isNewDay, showUserAvatar }) => { + const t = useTranslation(); + const { triggerProps, openUserCard } = useUserCard(); + const format = useFormatDate(); const formatTime = useFormatTime(); - const t = useTranslation(); - const chat = useChat(); - if (message.t === 'livechat-close') { return ( @@ -52,8 +52,10 @@ const ContactHistoryMessage: FC<{ url={message.avatar} username={message.u.username} size='x18' - onClick={(e) => chat?.userCard.openUserCard(e, message.u.username)} + onClick={(e) => openUserCard(e, message.u.username)} style={{ cursor: 'pointer' }} + role='button' + {...triggerProps} /> )} @@ -80,8 +82,10 @@ const ContactHistoryMessage: FC<{ url={message.avatar} username={message.u.username} size='x36' - onClick={(e) => chat?.userCard.openUserCard(e, message.u.username)} + onClick={(e) => openUserCard(e, message.u.username)} style={{ cursor: 'pointer' }} + role='button' + {...triggerProps} /> )} {sequential && } diff --git a/apps/meteor/client/views/room/UserCard/UserCardWithData.tsx b/apps/meteor/client/views/room/UserCard/UserCardWithData.tsx index 0337c794f245..40244b04beba 100644 --- a/apps/meteor/client/views/room/UserCard/UserCardWithData.tsx +++ b/apps/meteor/client/views/room/UserCard/UserCardWithData.tsx @@ -1,12 +1,10 @@ import type { IRoom } from '@rocket.chat/core-typings'; -import { PositionAnimated, AnimatedVisibility } from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useSetting, useRolesDescription, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; -import React, { useMemo, useRef } from 'react'; +import React, { useMemo } from 'react'; import { getUserDisplayName } from '../../../../lib/getUserDisplayName'; -import { Backdrop } from '../../../components/Backdrop'; import GenericMenu from '../../../components/GenericMenu/GenericMenu'; import LocalTime from '../../../components/LocalTime'; import { UserCard, UserCardAction, UserCardRole, UserCardSkeleton } from '../../../components/UserCard'; @@ -16,22 +14,18 @@ import { useUserInfoActions } from '../hooks/useUserInfoActions'; type UserCardWithDataProps = { username: string; - target: Element; rid: IRoom['_id']; onOpenUserInfo: () => void; onClose: () => void; }; -const UserCardWithData = ({ username, target, rid, onOpenUserInfo, onClose }: UserCardWithDataProps) => { +const UserCardWithData = ({ username, rid, onOpenUserInfo, onClose }: UserCardWithDataProps) => { const t = useTranslation(); - const ref = useRef(target); const getRoles = useRolesDescription(); const showRealNames = Boolean(useSetting('UI_Use_Real_Name')); const { data, isLoading } = useUserInfoQuery({ username }); - ref.current = target; - const user = useMemo(() => { const defaultValue = isLoading ? undefined : null; @@ -60,7 +54,7 @@ const UserCardWithData = ({ username, target, rid, onOpenUserInfo, onClose }: Us }; }, [data, username, showRealNames, isLoading, getRoles]); - const handleOpenUserInfo = useMutableCallback(() => { + const handleOpenUserInfo = useEffectEvent(() => { onOpenUserInfo(); onClose(); }); @@ -86,14 +80,11 @@ const UserCardWithData = ({ username, target, rid, onOpenUserInfo, onClose }: Us return [...actionsDefinition.map(mapAction), menu].filter(Boolean); }, [actionsDefinition, menu]); - return ( - <> - - - {isLoading ? : } - - - ); + if (isLoading) { + return ; + } + + return ; }; export default UserCardWithData; diff --git a/apps/meteor/client/views/room/body/LeaderBar.tsx b/apps/meteor/client/views/room/body/LeaderBar.tsx index 6de7c3d52e8e..4cf5266b89d8 100644 --- a/apps/meteor/client/views/room/body/LeaderBar.tsx +++ b/apps/meteor/client/views/room/body/LeaderBar.tsx @@ -5,6 +5,7 @@ import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement, UIEvent } from 'react'; import React, { memo, useCallback, useMemo } from 'react'; +import type { AriaButtonProps } from 'react-aria'; import { isTruthy } from '../../../../lib/isTruthy'; import { ReactiveUserStatus } from '../../../components/UserStatus'; @@ -16,9 +17,10 @@ type LeaderBarProps = { username: IUser['username']; visible: boolean; onAvatarClick?: (event: UIEvent, username: IUser['username']) => void; + triggerProps: AriaButtonProps<'button'>; }; -const LeaderBar = ({ _id, name, username, visible, onAvatarClick }: LeaderBarProps): ReactElement => { +const LeaderBar = ({ _id, name, username, visible, onAvatarClick, triggerProps }: LeaderBarProps): ReactElement => { const t = useTranslation(); const chatNowLink = useMemo(() => roomCoordinator.getRouteLink('d', { name: username }) || undefined, [username]); @@ -64,7 +66,7 @@ const LeaderBar = ({ _id, name, username, visible, onAvatarClick }: LeaderBarPro className={[roomLeaderStyle, 'room-leader', !visible && 'animated-hidden'].filter(isTruthy)} > - + diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index 4ec51aa67fda..983695b1fa70 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -35,6 +35,7 @@ import RoomComposer from '../composer/RoomComposer/RoomComposer'; import { useChat } from '../contexts/ChatContext'; import { useRoom, useRoomSubscription, useRoomMessages } from '../contexts/RoomContext'; import { useRoomToolbox } from '../contexts/RoomToolboxContext'; +import { useUserCard } from '../contexts/UserCardContext'; import { useMessageListNavigation } from '../hooks/useMessageListNavigation'; import { useScrollMessageList } from '../hooks/useScrollMessageList'; import DropTargetOverlay from './DropTargetOverlay'; @@ -75,6 +76,7 @@ const RoomBody = (): ReactElement => { const lastScrollTopRef = useRef(0); const chat = useChat(); + const { openUserCard, triggerProps } = useUserCard(); if (!chat) { throw new Error('No ChatContext provided'); @@ -181,9 +183,9 @@ const RoomBody = (): ReactElement => { return; } - chat?.userCard.openUserCard(event, username); + openUserCard(event, username); }, - [chat?.userCard], + [openUserCard], ); const handleUnreadBarJumpToButtonClick = useCallback(() => { @@ -588,6 +590,7 @@ const RoomBody = (): ReactElement => { name={roomLeader.name} visible={!hideLeaderHeader} onAvatarClick={handleOpenUserCard} + triggerProps={triggerProps} /> ) : null}
          void; closeUserCard: () => void; + triggerProps: AriaButtonProps<'button'>; + triggerRef: MutableRefObject; + state: OverlayTriggerState; }; export const UserCardContext = createContext({ openUserCard: () => undefined, closeUserCard: () => undefined, + triggerProps: {}, + triggerRef: { current: null }, + state: { isOpen: false, setOpen: () => undefined, open: () => undefined, close: () => undefined, toggle: () => undefined }, }); export const useUserCard = () => useContext(UserCardContext); diff --git a/apps/meteor/client/views/room/modals/ReactionListModal/ReactionListModal.tsx b/apps/meteor/client/views/room/modals/ReactionListModal/ReactionListModal.tsx index 596d74977b3d..70a91df0e555 100644 --- a/apps/meteor/client/views/room/modals/ReactionListModal/ReactionListModal.tsx +++ b/apps/meteor/client/views/room/modals/ReactionListModal/ReactionListModal.tsx @@ -1,6 +1,6 @@ import type { IMessage } from '@rocket.chat/core-typings'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactElement, UIEvent } from 'react'; +import type { ReactElement } from 'react'; import React from 'react'; import GenericModal from '../../../../components/GenericModal'; @@ -8,16 +8,15 @@ import Reactions from './Reactions'; type ReactionListModalProps = { reactions: Required['reactions']; - onOpenUserCard?: (e: UIEvent, username: string) => void; onClose: () => void; }; -const ReactionListModal = ({ reactions, onOpenUserCard, onClose }: ReactionListModalProps): ReactElement => { +const ReactionListModal = ({ reactions, onClose }: ReactionListModalProps): ReactElement => { const t = useTranslation(); return ( - + ); }; diff --git a/apps/meteor/client/views/room/modals/ReactionListModal/ReactionUserTag.tsx b/apps/meteor/client/views/room/modals/ReactionListModal/ReactionUserTag.tsx index 4c13ac8af532..0cc2ef3aa4bc 100644 --- a/apps/meteor/client/views/room/modals/ReactionListModal/ReactionUserTag.tsx +++ b/apps/meteor/client/views/room/modals/ReactionListModal/ReactionUserTag.tsx @@ -1,30 +1,11 @@ -import type { IUser } from '@rocket.chat/core-typings'; import { Box, Tag } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; -import type { ReactElement, UIEvent } from 'react'; +import type { ReactElement } from 'react'; import React from 'react'; -type ReactionUserTagProps = { - username: IUser['username']; - displayName: string; - onOpenUserCard?: (e: UIEvent, username: string) => void; -}; +const ReactionUserTag = ({ displayName }: { displayName: string }): ReactElement => ( + + {displayName} + +); -const ReactionUserTag = ({ username, displayName, onOpenUserCard }: ReactionUserTagProps): ReactElement => { - const handleOpenCard = useEffectEvent((e: UIEvent) => { - if (!username) { - return; - } - - onOpenUserCard?.(e, username); - }); - - return ( - - - {displayName} - - - ); -}; export default ReactionUserTag; diff --git a/apps/meteor/client/views/room/modals/ReactionListModal/Reactions.tsx b/apps/meteor/client/views/room/modals/ReactionListModal/Reactions.tsx index d766661de3e5..1e4d997bdfbc 100644 --- a/apps/meteor/client/views/room/modals/ReactionListModal/Reactions.tsx +++ b/apps/meteor/client/views/room/modals/ReactionListModal/Reactions.tsx @@ -1,18 +1,13 @@ import type { IMessage } from '@rocket.chat/core-typings'; import { Box } from '@rocket.chat/fuselage'; import { useSetting } from '@rocket.chat/ui-contexts'; -import type { ReactElement, UIEvent } from 'react'; +import type { ReactElement } from 'react'; import React from 'react'; import Emoji from '../../../../components/Emoji'; import ReactionUserTag from './ReactionUserTag'; -type ReactionsProps = { - reactions: Required['reactions']; - onOpenUserCard?: (e: UIEvent, username: string) => void; -}; - -const Reactions = ({ reactions, onOpenUserCard }: ReactionsProps): ReactElement => { +const Reactions = ({ reactions }: { reactions: Required['reactions'] }): ReactElement => { const useRealName = useSetting('UI_Use_Real_Name'); return ( @@ -22,12 +17,7 @@ const Reactions = ({ reactions, onOpenUserCard }: ReactionsProps): ReactElement {usernames.map((username, i: number) => ( - + ))} diff --git a/apps/meteor/client/views/room/providers/UserCardProvider.tsx b/apps/meteor/client/views/room/providers/UserCardProvider.tsx index 47a9f1cd744e..004cfbfda5aa 100644 --- a/apps/meteor/client/views/room/providers/UserCardProvider.tsx +++ b/apps/meteor/client/views/room/providers/UserCardProvider.tsx @@ -1,6 +1,9 @@ -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { Popover } from '@rocket.chat/fuselage'; +import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import type { ComponentProps, ReactNode } from 'react'; -import React, { Suspense, lazy, useCallback, useMemo, useState } from 'react'; +import React, { Suspense, lazy, useCallback, useMemo, useRef, useState } from 'react'; +import { useOverlayTrigger } from 'react-aria'; +import { useOverlayTriggerState } from 'react-stately'; import { useRoom } from '../contexts/RoomContext'; import { useRoomToolbox } from '../contexts/RoomToolboxContext'; @@ -12,9 +15,14 @@ const UserCardProvider = ({ children }: { children: ReactNode }) => { const room = useRoom(); const [userCardData, setUserCardData] = useState | null>(null); + const triggerRef = useRef(null); + const state = useOverlayTriggerState({}); + const { triggerProps, overlayProps } = useOverlayTrigger({ type: 'dialog' }, state, triggerRef); + delete triggerProps.onPress; + const { openTab } = useRoomToolbox(); - const openUserInfo = useMutableCallback((username?: string) => { + const openUserInfo = useEffectEvent((username?: string) => { switch (room.t) { case 'l': openTab('room-info', username); @@ -36,31 +44,37 @@ const UserCardProvider = ({ children }: { children: ReactNode }) => { const handleSetUserCard = useCallback( (e, username) => { + triggerRef.current = e.target; + state.open(); setUserCardData({ username, rid: room._id, - target: e.target, onOpenUserInfo: () => openUserInfo(username), onClose: () => setUserCardData(null), }); }, - [openUserInfo, room._id], + [openUserInfo, room._id, state], ); const contextValue = useMemo( () => ({ openUserCard: handleSetUserCard, closeUserCard: () => setUserCardData(null), + triggerProps, + triggerRef, + state, }), - [handleSetUserCard], + [handleSetUserCard, state, triggerProps], ); return ( {children} - {userCardData && ( + {state.isOpen && userCardData && ( - + + + )} diff --git a/apps/meteor/client/views/room/providers/hooks/useChatMessagesInstance.ts b/apps/meteor/client/views/room/providers/hooks/useChatMessagesInstance.ts index 023e99a99dd9..290b1c4be680 100644 --- a/apps/meteor/client/views/room/providers/hooks/useChatMessagesInstance.ts +++ b/apps/meteor/client/views/room/providers/hooks/useChatMessagesInstance.ts @@ -7,7 +7,6 @@ import { useEmojiPicker } from '../../../../contexts/EmojiPickerContext'; import type { ChatAPI } from '../../../../lib/chats/ChatAPI'; import { useUiKitActionManager } from '../../../../uikit/hooks/useUiKitActionManager'; import { useRoomSubscription } from '../../contexts/RoomContext'; -import { useUserCard } from '../../contexts/UserCardContext'; import { useInstance } from './useInstance'; export function useChatMessagesInstance({ rid, tmid }: { rid: IRoom['_id']; tmid?: IMessage['_id'] }): ChatAPI { @@ -27,7 +26,6 @@ export function useChatMessagesInstance({ rid, tmid }: { rid: IRoom['_id']; tmid }, [subscription, chatMessages?.readStateManager]); chatMessages.emojiPicker = useEmojiPicker(); - chatMessages.userCard = useUserCard(); return chatMessages; } diff --git a/apps/meteor/package.json b/apps/meteor/package.json index de5e4eacb9b8..0d9d0ecd3b70 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -240,7 +240,7 @@ "@rocket.chat/favicon": "workspace:^", "@rocket.chat/forked-matrix-appservice-bridge": "^4.0.2", "@rocket.chat/forked-matrix-bot-sdk": "^0.6.0-beta.3", - "@rocket.chat/fuselage": "^0.47.0", + "@rocket.chat/fuselage": "^0.47.1", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/fuselage-toastbar": "~0.31.25", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 60ffa84825f0..f54d8c1aac28 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -5439,6 +5439,7 @@ "Use_url_for_avatar": "Use URL for avatar", "Use_User_Preferences_or_Global_Settings": "Use User Preferences or Global Settings", "User": "User", + "User_card_actions": "User card actions", "User_menu": "User menu", "User Search": "User Search", "User Search (Group Validation)": "User Search (Group Validation)", diff --git a/apps/meteor/tests/e2e/messaging.spec.ts b/apps/meteor/tests/e2e/messaging.spec.ts index a333e3d5b3ce..4fa248e72183 100644 --- a/apps/meteor/tests/e2e/messaging.spec.ts +++ b/apps/meteor/tests/e2e/messaging.spec.ts @@ -61,6 +61,32 @@ test.describe.serial('Messaging', () => { await expect(poHomeChannel.composer).toBeFocused(); }); + test('should navigate properly on the user card', async ({ page }) => { + await poHomeChannel.sidenav.openChat(targetChannel); + + // open UserCard + await page.keyboard.press('Shift+Tab'); + await page.keyboard.press('ArrowUp'); + await page.keyboard.press('Tab'); + await page.keyboard.press('Space'); + await expect(poHomeChannel.userCardToolbar).toBeVisible(); + + // close UserCard with Esc + await page.keyboard.press('Escape'); + await expect(poHomeChannel.userCardToolbar).not.toBeVisible(); + + // with focus restored reopen toolbar + await page.keyboard.press('Space'); + await expect(poHomeChannel.userCardToolbar).toBeVisible(); + + // close UserCard with button + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await page.keyboard.press('Space'); + await expect(poHomeChannel.userCardToolbar).not.toBeVisible(); + }) + test('should not restore focus on the last focused if it was triggered by click', async ({ page }) => { await poHomeChannel.sidenav.openChat(targetChannel); await page.locator('[data-qa-type="message"]:has-text("msg1")').click(); diff --git a/apps/meteor/tests/e2e/page-objects/home-channel.ts b/apps/meteor/tests/e2e/page-objects/home-channel.ts index 01dcc302d3e0..3b17d7d7ddcc 100644 --- a/apps/meteor/tests/e2e/page-objects/home-channel.ts +++ b/apps/meteor/tests/e2e/page-objects/home-channel.ts @@ -45,6 +45,10 @@ export class HomeChannel { return this.page.locator('textarea[name="msg"]'); } + get userCardToolbar(): Locator { + return this.page.locator('[role=toolbar][aria-label="User card actions"]'); + } + get composerToolbar(): Locator { return this.page.locator('[role=toolbar][aria-label="Composer Primary Actions"]'); } diff --git a/ee/packages/ui-theming/package.json b/ee/packages/ui-theming/package.json index 87cab03460df..63732ae16db5 100644 --- a/ee/packages/ui-theming/package.json +++ b/ee/packages/ui-theming/package.json @@ -4,7 +4,7 @@ "private": true, "devDependencies": { "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.47.0", + "@rocket.chat/fuselage": "^0.47.1", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/ui-contexts": "workspace:~", diff --git a/packages/fuselage-ui-kit/package.json b/packages/fuselage-ui-kit/package.json index ecd7e25fa256..69a1f1525edd 100644 --- a/packages/fuselage-ui-kit/package.json +++ b/packages/fuselage-ui-kit/package.json @@ -63,7 +63,7 @@ "@babel/preset-typescript": "~7.22.15", "@rocket.chat/apps-engine": "1.41.0", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.47.0", + "@rocket.chat/fuselage": "^0.47.1", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/icons": "^0.33.0", diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index 108329360d05..da2d978b46c5 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.47.0", + "@rocket.chat/fuselage": "^0.47.1", "@rocket.chat/fuselage-tokens": "~0.32.0", "@rocket.chat/message-parser": "workspace:^", "@rocket.chat/styled": "~0.31.25", diff --git a/packages/gazzodown/src/MarkupInteractionContext.ts b/packages/gazzodown/src/MarkupInteractionContext.ts index 70fc32d33496..b2fb11f5378b 100644 --- a/packages/gazzodown/src/MarkupInteractionContext.ts +++ b/packages/gazzodown/src/MarkupInteractionContext.ts @@ -1,6 +1,7 @@ import type { MessageMention } from '@rocket.chat/core-typings'; import type * as MessageParser from '@rocket.chat/message-parser'; import { createContext, FormEvent, UIEvent } from 'react'; +import { AriaButtonProps } from 'react-aria'; export type UserMention = MessageMention; export type ChannelMention = MessageMention; @@ -20,6 +21,7 @@ type MarkupInteractionContextValue = { isMobile?: boolean; ownUserId?: string | null; showMentionSymbol?: boolean; + triggerProps?: AriaButtonProps<'button'>; }; export const MarkupInteractionContext = createContext({}); diff --git a/packages/gazzodown/src/mentions/ChannelMentionElement.tsx b/packages/gazzodown/src/mentions/ChannelMentionElement.tsx index c871b332824a..6b8e8518a422 100644 --- a/packages/gazzodown/src/mentions/ChannelMentionElement.tsx +++ b/packages/gazzodown/src/mentions/ChannelMentionElement.tsx @@ -1,6 +1,6 @@ import { Message } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import { memo, ReactElement, useContext, useMemo } from 'react'; +import { memo, ReactElement, useContext, useMemo, KeyboardEvent } from 'react'; import { MarkupInteractionContext } from '../MarkupInteractionContext'; @@ -22,7 +22,17 @@ const ChannelMentionElement = ({ mention }: ChannelMentionElementProps): ReactEl } return ( - + ): void => { + (e.code === 'Enter' || e.code === 'Space') && handleClick?.(e); + }} + > {handleChannelMention(resolved.fname ?? mention, showMentionSymbol)} ); diff --git a/packages/gazzodown/src/mentions/UserMentionElement.tsx b/packages/gazzodown/src/mentions/UserMentionElement.tsx index a25106918af8..a9ba5567541f 100644 --- a/packages/gazzodown/src/mentions/UserMentionElement.tsx +++ b/packages/gazzodown/src/mentions/UserMentionElement.tsx @@ -1,6 +1,6 @@ import { Message } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import { memo, ReactElement, useContext, useMemo } from 'react'; +import { memo, ReactElement, useContext, useMemo, KeyboardEvent } from 'react'; import { MarkupInteractionContext } from '../MarkupInteractionContext'; @@ -13,7 +13,8 @@ const handleUserMention = (mention: string | undefined, withSymbol: boolean | un const UserMentionElement = ({ mention }: UserMentionElementProps): ReactElement => { const t = useTranslation(); - const { resolveUserMention, onUserMentionClick, ownUserId, useRealName, showMentionSymbol } = useContext(MarkupInteractionContext); + const { resolveUserMention, onUserMentionClick, ownUserId, useRealName, showMentionSymbol, triggerProps } = + useContext(MarkupInteractionContext); const resolved = useMemo(() => resolveUserMention?.(mention), [mention, resolveUserMention]); const handleClick = useMemo(() => (resolved ? onUserMentionClick?.(resolved) : undefined), [resolved, onUserMentionClick]); @@ -43,7 +44,13 @@ const UserMentionElement = ({ mention }: UserMentionElementProps): ReactElement variant={resolved._id === ownUserId ? 'critical' : 'other'} title={resolved._id === ownUserId ? t('Mentions_you') : t('Mentions_user')} clickable + tabIndex={0} + role='button' onClick={handleClick} + onKeyDown={(e: KeyboardEvent): void => { + (e.code === 'Enter' || e.code === 'Space') && handleClick?.(e); + }} + {...triggerProps} data-uid={resolved._id} > {handleUserMention((useRealName ? resolved.name : resolved.username) ?? mention, showMentionSymbol)} diff --git a/packages/ui-avatar/package.json b/packages/ui-avatar/package.json index 853178aae3a9..e968d4567d9c 100644 --- a/packages/ui-avatar/package.json +++ b/packages/ui-avatar/package.json @@ -4,7 +4,7 @@ "private": true, "devDependencies": { "@babel/core": "~7.22.20", - "@rocket.chat/fuselage": "^0.47.0", + "@rocket.chat/fuselage": "^0.47.1", "@rocket.chat/ui-contexts": "workspace:^", "@types/babel__core": "~7.20.3", "@types/react": "~17.0.69", diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index 85b3fac423ac..64b39555e3a2 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.47.0", + "@rocket.chat/fuselage": "^0.47.1", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/mock-providers": "workspace:^", diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json index a1fbe39a6142..f3ac9ea3d555 100644 --- a/packages/ui-composer/package.json +++ b/packages/ui-composer/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.47.0", + "@rocket.chat/fuselage": "^0.47.1", "@rocket.chat/icons": "^0.33.0", "@storybook/addon-actions": "~6.5.16", "@storybook/addon-docs": "~6.5.16", diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index 283630b6e9d6..156d5594fabe 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.47.0", + "@rocket.chat/fuselage": "^0.47.1", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/styled": "~0.31.25", diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index f623c71210bf..6028c769f189 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -15,7 +15,7 @@ "@codemirror/tooltip": "^0.19.16", "@lezer/highlight": "^1.1.6", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.47.0", + "@rocket.chat/fuselage": "^0.47.1", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/fuselage-toastbar": "^0.31.25", diff --git a/yarn.lock b/yarn.lock index 066187e54b31..8b8833493494 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8717,7 +8717,7 @@ __metadata: "@babel/preset-typescript": ~7.22.15 "@rocket.chat/apps-engine": 1.41.0 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.47.0 + "@rocket.chat/fuselage": ^0.47.1 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/gazzodown": "workspace:^" @@ -8772,9 +8772,9 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/fuselage@npm:^0.47.0": - version: 0.47.0 - resolution: "@rocket.chat/fuselage@npm:0.47.0" +"@rocket.chat/fuselage@npm:^0.47.1": + version: 0.47.1 + resolution: "@rocket.chat/fuselage@npm:0.47.1" dependencies: "@rocket.chat/css-in-js": ^0.31.25 "@rocket.chat/css-supports": ^0.31.25 @@ -8792,7 +8792,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 4ca06bd467db88b5c4c3eed70bbd8879923cfbe7f5a92bd934ed0f800b37825592d84753e124a3f2c70d4e9d82bf5dac6bc2735ea93ccfa26fddc6ab49ea8094 + checksum: d812e4ed52b53372268b4f5d396fb8d6aeb3f46a4f7e276642edf287ca2038f88e2a6d8bc0264f95fee1772aadc849419f7695e35cc6a278111efcbc18e1472d languageName: node linkType: hard @@ -8803,7 +8803,7 @@ __metadata: "@babel/core": ~7.22.20 "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.47.0 + "@rocket.chat/fuselage": ^0.47.1 "@rocket.chat/fuselage-tokens": ~0.32.0 "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/styled": ~0.31.25 @@ -9158,7 +9158,7 @@ __metadata: "@rocket.chat/favicon": "workspace:^" "@rocket.chat/forked-matrix-appservice-bridge": ^4.0.2 "@rocket.chat/forked-matrix-bot-sdk": ^0.6.0-beta.3 - "@rocket.chat/fuselage": ^0.47.0 + "@rocket.chat/fuselage": ^0.47.1 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/fuselage-toastbar": ~0.31.25 @@ -10037,7 +10037,7 @@ __metadata: resolution: "@rocket.chat/ui-avatar@workspace:packages/ui-avatar" dependencies: "@babel/core": ~7.22.20 - "@rocket.chat/fuselage": ^0.47.0 + "@rocket.chat/fuselage": ^0.47.1 "@rocket.chat/ui-contexts": "workspace:^" "@types/babel__core": ~7.20.3 "@types/react": ~17.0.69 @@ -10063,7 +10063,7 @@ __metadata: "@babel/core": ~7.22.20 "@react-aria/toolbar": ^3.0.0-beta.1 "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.47.0 + "@rocket.chat/fuselage": ^0.47.1 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/mock-providers": "workspace:^" @@ -10116,7 +10116,7 @@ __metadata: "@babel/core": ~7.22.20 "@react-aria/toolbar": ^3.0.0-beta.1 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.47.0 + "@rocket.chat/fuselage": ^0.47.1 "@rocket.chat/icons": ^0.33.0 "@storybook/addon-actions": ~6.5.16 "@storybook/addon-docs": ~6.5.16 @@ -10208,7 +10208,7 @@ __metadata: resolution: "@rocket.chat/ui-theming@workspace:ee/packages/ui-theming" dependencies: "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.47.0 + "@rocket.chat/fuselage": ^0.47.1 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/ui-contexts": "workspace:~" @@ -10251,7 +10251,7 @@ __metadata: "@rocket.chat/css-in-js": ~0.31.25 "@rocket.chat/emitter": ~0.31.25 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.47.0 + "@rocket.chat/fuselage": ^0.47.1 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/styled": ~0.31.25 @@ -10296,7 +10296,7 @@ __metadata: "@codemirror/tooltip": ^0.19.16 "@lezer/highlight": ^1.1.6 "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.47.0 + "@rocket.chat/fuselage": ^0.47.1 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/fuselage-toastbar": ^0.31.25 From cd5cbe2ac60939d4d94a62926b43322be9168ce0 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 23 Feb 2024 18:27:19 -0300 Subject: [PATCH 065/207] fix: revert status websocket payload changes (#31823) --- .changeset/strange-lamps-taste.md | 6 ++++++ .../app/notifications/client/lib/Presence.ts | 9 +++++++-- .../server/modules/listeners/listeners.module.ts | 14 +++++++++++--- .../modules/notifications/notifications.module.ts | 4 ++-- ee/packages/ddp-client/src/types/streams.ts | 5 ++--- 5 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 .changeset/strange-lamps-taste.md diff --git a/.changeset/strange-lamps-taste.md b/.changeset/strange-lamps-taste.md new file mode 100644 index 000000000000..88b61f5e5dec --- /dev/null +++ b/.changeset/strange-lamps-taste.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/ddp-client': patch +'@rocket.chat/meteor': patch +--- + +Revert unintentional changes real time presence data payload diff --git a/apps/meteor/app/notifications/client/lib/Presence.ts b/apps/meteor/app/notifications/client/lib/Presence.ts index 8cff2ed84f61..dff4ecb0392f 100644 --- a/apps/meteor/app/notifications/client/lib/Presence.ts +++ b/apps/meteor/app/notifications/client/lib/Presence.ts @@ -1,4 +1,4 @@ -import type { UserStatus } from '@rocket.chat/core-typings'; +import { UserStatus } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { Presence } from '../../../../client/lib/presence'; @@ -10,6 +10,11 @@ new Meteor.Streamer('user-presence'); type args = [username: string, statusChanged?: UserStatus, statusText?: string]; +export const STATUS_MAP = [UserStatus.OFFLINE, UserStatus.ONLINE, UserStatus.AWAY, UserStatus.BUSY, UserStatus.DISABLED]; + Meteor.StreamerCentral.on('stream-user-presence', (uid: string, [username, statusChanged, statusText]: args) => { - Presence.notify({ _id: uid, username, status: statusChanged, statusText }); + if (!statusChanged) { + return; + } + Presence.notify({ _id: uid, username, status: STATUS_MAP[statusChanged as any], statusText }); }); diff --git a/apps/meteor/server/modules/listeners/listeners.module.ts b/apps/meteor/server/modules/listeners/listeners.module.ts index 49a3017af81c..ecaab84b2fac 100644 --- a/apps/meteor/server/modules/listeners/listeners.module.ts +++ b/apps/meteor/server/modules/listeners/listeners.module.ts @@ -2,7 +2,7 @@ import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; import type { ISetting as AppsSetting } from '@rocket.chat/apps-engine/definition/settings'; import type { IServiceClass } from '@rocket.chat/core-services'; import { EnterpriseSettings } from '@rocket.chat/core-services'; -import { isSettingColor, isSettingEnterprise } from '@rocket.chat/core-typings'; +import { isSettingColor, isSettingEnterprise, UserStatus } from '@rocket.chat/core-typings'; import type { IUser, IRoom, VideoConference, ISetting, IOmnichannelRoom } from '@rocket.chat/core-typings'; import { Logger } from '@rocket.chat/logger'; import { parse } from '@rocket.chat/message-parser'; @@ -12,6 +12,14 @@ import type { NotificationsModule } from '../notifications/notifications.module' const isMessageParserDisabled = process.env.DISABLE_MESSAGE_PARSER === 'true'; +const STATUS_MAP: Record = { + [UserStatus.OFFLINE]: 0, + [UserStatus.ONLINE]: 1, + [UserStatus.AWAY]: 2, + [UserStatus.BUSY]: 3, + [UserStatus.DISABLED]: 0, +} as const; + const minimongoChangeMap: Record = { inserted: 'added', updated: 'changed', @@ -145,10 +153,10 @@ export class ListenersModule { return; } - notifications.notifyLoggedInThisInstance('user-status', [_id, username, status, statusText, name, roles]); + notifications.notifyLoggedInThisInstance('user-status', [_id, username, STATUS_MAP[status], statusText, name, roles]); if (_id) { - notifications.sendPresence(_id, username, status, statusText); + notifications.sendPresence(_id, username, STATUS_MAP[status], statusText); } }); diff --git a/apps/meteor/server/modules/notifications/notifications.module.ts b/apps/meteor/server/modules/notifications/notifications.module.ts index 73cede2cd36c..75a2935d5616 100644 --- a/apps/meteor/server/modules/notifications/notifications.module.ts +++ b/apps/meteor/server/modules/notifications/notifications.module.ts @@ -1,5 +1,5 @@ import { Authorization, VideoConf } from '@rocket.chat/core-services'; -import type { ISubscription, IOmnichannelRoom, IUser, UserStatus } from '@rocket.chat/core-typings'; +import type { ISubscription, IOmnichannelRoom, IUser } from '@rocket.chat/core-typings'; import { Rooms, Subscriptions, Users, Settings } from '@rocket.chat/models'; import type { StreamerCallbackArgs, StreamKeys, StreamNames } from '@rocket.chat/ui-contexts'; import type { IStreamer, IStreamerConstructor, IPublication } from 'meteor/rocketchat:streamer'; @@ -531,7 +531,7 @@ export class NotificationsModule { return this.streamUser.emitWithoutBroadcast(`${userId}/${eventName}`, ...args); } - sendPresence(uid: string, ...args: [username: string, status?: UserStatus, statusText?: string]): void { + sendPresence(uid: string, ...args: [username: string, status?: 0 | 1 | 2 | 3, statusText?: string]): void { emit(uid, [args]); return this.streamPresence.emitWithoutBroadcast(uid, args); } diff --git a/ee/packages/ddp-client/src/types/streams.ts b/ee/packages/ddp-client/src/types/streams.ts index 87a3c0352c10..8d9d3b5302dd 100644 --- a/ee/packages/ddp-client/src/types/streams.ts +++ b/ee/packages/ddp-client/src/types/streams.ts @@ -23,7 +23,6 @@ import type { IBanner, LicenseLimitKind, ICustomUserStatus, - UserStatus, IWebdavAccount, } from '@rocket.chat/core-typings'; import type * as UiKit from '@rocket.chat/ui-kit'; @@ -243,7 +242,7 @@ export interface StreamerEvents { [ uid: IUser['_id'], username: IUser['username'], - status: UserStatus, + status: 0 | 1 | 2 | 3, statusText: IUser['statusText'], name: IUser['name'], roles: IUser['roles'], @@ -325,7 +324,7 @@ export interface StreamerEvents { }, ]; - 'user-presence': [{ key: string; args: [[username: string, statusChanged?: UserStatus, statusText?: string]] }]; + 'user-presence': [{ key: string; args: [[username: string, statusChanged?: 0 | 1 | 2 | 3, statusText?: string]] }]; // TODO: rename to 'integration-history' 'integrationHistory': [ From 9c06e1d6803c59088c1436dc04f0ae734f0e3772 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Tue, 27 Feb 2024 13:29:41 -0300 Subject: [PATCH 066/207] chore: `VideoConfPopup` keyboard navigability (#31831) --- .../VideoConfPopup/StartCallPopup.tsx | 41 ++++++++++++++----- .../VideoConfPopup/VideoConfPopupRoomInfo.tsx | 11 +++-- .../VideoConfPopups/VideoConfPopups.tsx | 13 +++--- .../hooks/useVideoConfRoomName.ts | 12 ++++++ .../rocketchat-i18n/i18n/de.i18n.json | 1 - .../rocketchat-i18n/i18n/en.i18n.json | 3 +- .../rocketchat-i18n/i18n/fi.i18n.json | 1 - .../rocketchat-i18n/i18n/hu.i18n.json | 1 - .../rocketchat-i18n/i18n/no.i18n.json | 1 - .../rocketchat-i18n/i18n/pl.i18n.json | 1 - .../rocketchat-i18n/i18n/ru.i18n.json | 1 - .../rocketchat-i18n/i18n/sv.i18n.json | 1 - .../tests/e2e/channel-management.spec.ts | 13 +++++- .../ui-video-conf/src/VideoConfController.tsx | 1 + .../src/VideoConfPopup/VideoConfPopup.tsx | 4 +- .../VideoConfPopup/VideoConfPopupFooter.tsx | 5 ++- .../VideoConfPopupFooterButtons.tsx | 8 +--- 17 files changed, 78 insertions(+), 40 deletions(-) create mode 100644 apps/meteor/client/views/room/contextualBar/VideoConference/hooks/useVideoConfRoomName.ts diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/StartCallPopup.tsx b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/StartCallPopup.tsx index 7a2a3120a255..d1bc3a29231b 100644 --- a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/StartCallPopup.tsx +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/StartCallPopup.tsx @@ -1,5 +1,5 @@ import type { IRoom } from '@rocket.chat/core-typings'; -import { useOutsideClick, useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useOutsideClick, useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useTranslation } from '@rocket.chat/ui-contexts'; import { VideoConfPopup, @@ -13,40 +13,61 @@ import { VideoConfPopupTitle, VideoConfPopupFooterButtons, } from '@rocket.chat/ui-video-conf'; -import type { ReactElement } from 'react'; -import React, { useRef } from 'react'; +import type { KeyboardEvent, ReactElement } from 'react'; +import React, { useCallback, useRef } from 'react'; import { useVideoConfSetPreferences, useVideoConfCapabilities, useVideoConfPreferences } from '../../../../../../contexts/VideoConfContext'; +import { useVideoConfRoomName } from '../../hooks/useVideoConfRoomName'; import VideoConfPopupRoomInfo from './VideoConfPopupRoomInfo'; type StartCallPopupProps = { id: string; + loading: boolean; room: IRoom; onClose: () => void; onConfirm: () => void; - loading: boolean; }; -const StartCallPopup = ({ loading, room, onClose, onConfirm }: StartCallPopupProps): ReactElement => { - const ref = useRef(null); - useOutsideClick([ref], !loading ? onClose : (): void => undefined); - +const StartCallPopup = ({ id, loading, room, onClose, onConfirm }: StartCallPopupProps): ReactElement => { const t = useTranslation(); + const ref = useRef(null); + useOutsideClick([ref], !loading ? onClose : () => undefined); + const setPreferences = useVideoConfSetPreferences(); const videoConfPreferences = useVideoConfPreferences(); const { controllersConfig, handleToggleMic, handleToggleCam } = useVideoConfControllers(videoConfPreferences); const capabilities = useVideoConfCapabilities(); + const roomName = useVideoConfRoomName(room); + + const dialogLabel = + room.t === 'd' ? `${t('Start_a_call_with__roomName__', { roomName })}` : `${t('Start_a_call_in__roomName__', { roomName })}`; const showCam = !!capabilities.cam; const showMic = !!capabilities.mic; - const handleStartCall = useMutableCallback(() => { + const handleStartCall = useEffectEvent(() => { setPreferences(controllersConfig); onConfirm(); }); + const callbackRef = useCallback( + (node) => { + if (!node) { + return; + } + + ref.current = node; + node.addEventListener('keydown', (e: KeyboardEvent) => { + if (e.key === 'Escape') { + onClose(); + } + }); + }, + [onClose], + ); + return ( - + {(showCam || showMic) && ( diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/VideoConfPopupRoomInfo.tsx b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/VideoConfPopupRoomInfo.tsx index 3d1f52746510..0765c22688e5 100644 --- a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/VideoConfPopupRoomInfo.tsx +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/VideoConfPopupRoomInfo.tsx @@ -1,20 +1,19 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { isDirectMessageRoom, isMultipleDirectMessageRoom } from '@rocket.chat/core-typings'; import { RoomAvatar } from '@rocket.chat/ui-avatar'; -import { useUser, useUserSubscription } from '@rocket.chat/ui-contexts'; +import { useUser } from '@rocket.chat/ui-contexts'; import { VideoConfPopupInfo } from '@rocket.chat/ui-video-conf'; import type { ReactElement } from 'react'; import React from 'react'; import { RoomIcon } from '../../../../../../components/RoomIcon'; import ReactiveUserStatus from '../../../../../../components/UserStatus/ReactiveUserStatus'; -import { useUserDisplayName } from '../../../../../../hooks/useUserDisplayName'; +import { useVideoConfRoomName } from '../../hooks/useVideoConfRoomName'; const VideoConfPopupRoomInfo = ({ room }: { room: IRoom }): ReactElement => { const ownUser = useUser(); const [userId] = room?.uids?.filter((uid) => uid !== ownUser?._id) || []; - const subscription = useUserSubscription(room._id); - const username = useUserDisplayName({ name: subscription?.fname, username: subscription?.name }); + const roomName = useVideoConfRoomName(room); const avatar = ; if (isDirectMessageRoom(room)) { @@ -25,14 +24,14 @@ const VideoConfPopupRoomInfo = ({ room }: { room: IRoom }): ReactElement => { icon: isMultipleDirectMessageRoom(room) ? : , })} > - {username} + {roomName} ); } return ( }> - {room.fname || room.name} + {roomName} ); }; diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopups.tsx b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopups.tsx index f5ec35a5ca4e..9a4b8a21ce18 100644 --- a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopups.tsx +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopups.tsx @@ -2,6 +2,7 @@ import { useCustomSound } from '@rocket.chat/ui-contexts'; import { VideoConfPopupBackdrop } from '@rocket.chat/ui-video-conf'; import type { ReactElement } from 'react'; import React, { useEffect, useMemo } from 'react'; +import { FocusScope } from 'react-aria'; import type { VideoConfPopupPayload } from '../../../../../contexts/VideoConfContext'; import { useVideoConfIsCalling, useVideoConfIsRinging, useVideoConfIncomingCalls } from '../../../../../contexts/VideoConfContext'; @@ -41,11 +42,13 @@ const VideoConfPopups = ({ children }: { children?: VideoConfPopupPayload }): Re <> {(children || popups?.length > 0) && ( - - {(children ? [children, ...popups] : popups).map(({ id, rid, isReceiving }, index = 1) => ( - - ))} - + + + {(children ? [children, ...popups] : popups).map(({ id, rid, isReceiving }, index = 1) => ( + + ))} + + )} diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/hooks/useVideoConfRoomName.ts b/apps/meteor/client/views/room/contextualBar/VideoConference/hooks/useVideoConfRoomName.ts new file mode 100644 index 000000000000..a8676ebc5c13 --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/hooks/useVideoConfRoomName.ts @@ -0,0 +1,12 @@ +import type { IRoom } from '@rocket.chat/core-typings'; +import { isDirectMessageRoom } from '@rocket.chat/core-typings'; +import { useUserSubscription } from '@rocket.chat/ui-contexts'; + +import { useUserDisplayName } from '../../../../../hooks/useUserDisplayName'; + +export const useVideoConfRoomName = (room: IRoom): string | undefined => { + const subscription = useUserSubscription(room._id); + const username = useUserDisplayName({ name: subscription?.fname, username: subscription?.name }); + + return isDirectMessageRoom(room) ? username : room.fname || room.name; +}; diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json index 94d69b121469..a21fd248ecfb 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json @@ -4448,7 +4448,6 @@ "Starred_Messages": "Favorisierte Nachrichten", "Start": "Starten", "Start_a_call": "Anruf starten", - "Start_a_call_with": "Beginnen Sie ein Gespräch mit", "Start_audio_call": "Anruf starten", "Start_call": "Anruf starten", "Start_Chat": "Chat beginnen", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index f54d8c1aac28..9088b80f4672 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -4918,7 +4918,8 @@ "Starred_messages_are_only_visible_to_you": "Starred messages are only visible to you", "Start": "Start", "Start_a_call": "Start a call", - "Start_a_call_with": "Start a call with", + "Start_a_call_in__roomName__": "Start a call in {{roomName}}", + "Start_a_call_with__roomName__": "Start a call with {{roomName}}", "Start_a_free_trial": "Start a free trial", "Start_audio_call": "Start audio call", "Start_call": "Start call", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json index 8fa7e2cfa32b..6783b74a9ea9 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json @@ -4546,7 +4546,6 @@ "Starred_Messages": "Tähdellä merkityt viestit", "Start": "Aloita", "Start_a_call": "Aloita puhelu", - "Start_a_call_with": "Aloita puhelu", "Start_audio_call": "Aloita äänipuhelu", "Start_call": "Aloita puhelu", "Start_Chat": "Aloita keskustelu", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json index 9c43825fe02f..5d724f8d69f9 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json @@ -4365,7 +4365,6 @@ "Starred_Messages": "Csillagozott üzenetek", "Start": "Kezdés", "Start_a_call": "Hívás indítása", - "Start_a_call_with": "Hívás indítása ezzel", "Start_audio_call": "Hanghívás indítása", "Start_call": "Hívás indítása", "Start_Chat": "Csevegés indítása", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json index 2c27c0fca972..7d25488a7c40 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json @@ -3815,7 +3815,6 @@ "Starred_Messages": "Stjernemerkede meldinger", "Start": "Start", "Start_a_call": "Start en samtale", - "Start_a_call_with": "Start en samtale med", "Start_a_free_trial": "Start en gratis prøveperiode", "Start_audio_call": "Start lydanrop", "Start_call": "Start samtale", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json index 1b4b1e7721d7..4461e54d899c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json @@ -4307,7 +4307,6 @@ "Starred_Messages": "Ulubione wiadomości", "Start": "Początek", "Start_a_call": "Rozpocznij połączenie", - "Start_a_call_with": "Rozpocznij połączenie z", "Start_audio_call": "Rozpocznij rozmowę audio", "Start_call": "Rozpocząć połączenie", "Start_Chat": "Rozpocznij czat", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json index 3cb8c39659b7..da6f72b0e15b 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json @@ -4122,7 +4122,6 @@ "Starred_Messages": "Отмеченные сообщения", "Start": "Начать", "Start_a_call": "Начать звонок", - "Start_a_call_with": "Начать звонок с", "Start_audio_call": "Начать голосовой вызов", "Start_call": "Начать звонок", "Start_Chat": "Начать чат", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json index d0a7678ab920..83471ec5983c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json @@ -4548,7 +4548,6 @@ "Starred_Messages": "Stjärnmarkerade meddelanden", "Start": "Start", "Start_a_call": "Starta ett samtal", - "Start_a_call_with": "Starta ett samtal med", "Start_audio_call": "Starta ljudsamtal", "Start_call": "Starta samtal", "Start_Chat": "Starta chatt", diff --git a/apps/meteor/tests/e2e/channel-management.spec.ts b/apps/meteor/tests/e2e/channel-management.spec.ts index 6bf7084ac494..96d50bae6151 100644 --- a/apps/meteor/tests/e2e/channel-management.spec.ts +++ b/apps/meteor/tests/e2e/channel-management.spec.ts @@ -36,7 +36,6 @@ test.describe.serial('channel-management', () => { test('should move the focus away from toolbar using tab key', async ({ page }) => { await poHomeChannel.sidenav.openChat(targetChannel); - await poHomeChannel.content.sendMessage('hello composer'); await poHomeChannel.roomHeaderFavoriteBtn.focus(); await page.keyboard.press('Tab'); @@ -45,6 +44,18 @@ test.describe.serial('channel-management', () => { await expect(poHomeChannel.roomHeaderToolbar.getByRole('button', { name: 'Call' })).not.toBeFocused(); }); + test('should be able to navigate on call popup with keyboard', async ({ page }) => { + await poHomeChannel.sidenav.openChat(targetChannel); + await poHomeChannel.roomHeaderFavoriteBtn.focus(); + + await page.keyboard.press('Tab'); + await page.keyboard.press('Space'); + await poHomeChannel.content.btnStartCall.waitFor(); + await page.keyboard.press('Tab'); + + await expect(page.getByRole('button', { name: 'Start call' })).toBeFocused(); + }); + test('expect add "user1" to "targetChannel"', async () => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.tabs.btnTabMembers.click(); diff --git a/packages/ui-video-conf/src/VideoConfController.tsx b/packages/ui-video-conf/src/VideoConfController.tsx index b13ce13df55f..b3813e329535 100644 --- a/packages/ui-video-conf/src/VideoConfController.tsx +++ b/packages/ui-video-conf/src/VideoConfController.tsx @@ -16,6 +16,7 @@ const VideoConfController = ({ icon, active, secondary, disabled, small = true, return ( ; const VideoConfPopup = forwardRef(function VideoConfPopup( - { children, position }: VideoConfPopupProps, + { children, position, ...props }: VideoConfPopupProps, ref: Ref, ): ReactElement { return ( - + {children} diff --git a/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopupFooter.tsx b/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopupFooter.tsx index f86bd0ef947f..949b6f8b8863 100644 --- a/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopupFooter.tsx +++ b/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopupFooter.tsx @@ -1,5 +1,6 @@ -import type { ReactElement, HTMLProps } from 'react'; +import { Box } from '@rocket.chat/fuselage'; +import type { ReactElement, ComponentProps } from 'react'; -const VideoConfPopupFooter = (props: HTMLProps): ReactElement =>
          ; +const VideoConfPopupFooter = (props: ComponentProps): ReactElement => ; export default VideoConfPopupFooter; diff --git a/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopupFooterButtons.tsx b/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopupFooterButtons.tsx index 4dba91cc6350..e1c0d25f3fbf 100644 --- a/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopupFooterButtons.tsx +++ b/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopupFooterButtons.tsx @@ -1,10 +1,6 @@ -import { Box, ButtonGroup } from '@rocket.chat/fuselage'; +import { ButtonGroup } from '@rocket.chat/fuselage'; import type { ReactElement, ComponentProps } from 'react'; -const VideoConfPopupFooterButtons = (props: ComponentProps): ReactElement => ( - - - -); +const VideoConfPopupFooterButtons = (props: ComponentProps): ReactElement => ; export default VideoConfPopupFooterButtons; From 31123f7250ac763012bf1112760ddfb1ef920b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9ssica=20Souza?= Date: Tue, 27 Feb 2024 17:07:36 -0300 Subject: [PATCH 067/207] test: saml login custom role (#31830) --- apps/meteor/ee/server/configuration/saml.ts | 2 +- .../saml/config/simplesamlphp/authsources.php | 9 ++- apps/meteor/tests/e2e/fixtures/userStates.ts | 1 + apps/meteor/tests/e2e/saml.spec.ts | 59 ++++++++++++++++++- apps/meteor/tests/e2e/utils/custom-role.ts | 11 ++++ 5 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 apps/meteor/tests/e2e/utils/custom-role.ts diff --git a/apps/meteor/ee/server/configuration/saml.ts b/apps/meteor/ee/server/configuration/saml.ts index 96dca07829c6..34430b4418b9 100644 --- a/apps/meteor/ee/server/configuration/saml.ts +++ b/apps/meteor/ee/server/configuration/saml.ts @@ -24,7 +24,7 @@ await License.onLicense('saml-enterprise', () => { const savedRoles = await Roles.findInIdsOrNames(ensureArray(value)).toArray(); - userObject.roles = savedRoles.map((role) => role.name); + userObject.roles = savedRoles.map((role) => role._id); } }); diff --git a/apps/meteor/tests/e2e/containers/saml/config/simplesamlphp/authsources.php b/apps/meteor/tests/e2e/containers/saml/config/simplesamlphp/authsources.php index 867d66049b3a..ee5a1928f3aa 100644 --- a/apps/meteor/tests/e2e/containers/saml/config/simplesamlphp/authsources.php +++ b/apps/meteor/tests/e2e/containers/saml/config/simplesamlphp/authsources.php @@ -29,6 +29,13 @@ 'eduPersonAffiliation' => array('group2'), 'email' => 'samluser3@example.com', ), + 'samluser4:password' => array( + 'uid' => array('4'), + 'username' => 'samluser4', + 'cn' => 'Saml User 4', + 'eduPersonAffiliation' => array('group4'), + 'email' => 'samluser4@example.com', + 'role' => 'saml-role', + ), ), - ); \ No newline at end of file diff --git a/apps/meteor/tests/e2e/fixtures/userStates.ts b/apps/meteor/tests/e2e/fixtures/userStates.ts index 860242d57693..4541f69d06e8 100644 --- a/apps/meteor/tests/e2e/fixtures/userStates.ts +++ b/apps/meteor/tests/e2e/fixtures/userStates.ts @@ -88,6 +88,7 @@ export const Users = { user3: generateContext('user3'), samluser1: generateContext('samluser1'), samluser2: generateContext('samluser2'), + samluser4: generateContext('samluser4'), userForSamlMerge: generateContext('user_for_saml_merge'), userForSamlMerge2: generateContext('user_for_saml_merge2'), admin: generateContext('rocketchat.internal.admin.test'), diff --git a/apps/meteor/tests/e2e/saml.spec.ts b/apps/meteor/tests/e2e/saml.spec.ts index 1b5e57250ea1..92c56a32723a 100644 --- a/apps/meteor/tests/e2e/saml.spec.ts +++ b/apps/meteor/tests/e2e/saml.spec.ts @@ -9,16 +9,23 @@ import * as constants from './config/constants'; import { createUserFixture } from './fixtures/collections/users'; import { Users } from './fixtures/userStates'; import { Registration } from './page-objects'; +import { createCustomRole, deleteCustomRole } from './utils/custom-role'; import { getUserInfo } from './utils/getUserInfo'; import { setSettingValueById } from './utils/setSettingValueById'; -import { test, expect } from './utils/test'; +import { test, expect, BaseTest } from './utils/test'; const resetTestData = async (cleanupOnly = false) => { // Reset saml users' data on mongo in the beforeAll hook to allow re-running the tests within the same playwright session // This is needed because those tests will modify this data and running them a second time would trigger different code paths const connection = await MongoClient.connect(constants.URL_MONGODB); - const usernamesToDelete = [Users.userForSamlMerge, Users.userForSamlMerge2, Users.samluser1, Users.samluser2].map(({ data: { username }}) => username); + const usernamesToDelete = [ + Users.userForSamlMerge, + Users.userForSamlMerge2, + Users.samluser1, + Users.samluser2, + Users.samluser4, + ].map(({ data: { username } }) => username); await connection .db() .collection('users') @@ -57,6 +64,14 @@ const resetTestData = async (cleanupOnly = false) => { _id: 'SAML_Custom_Default', value: false, }, + { + _id: 'SAML_Custom_Default_role_attribute_sync', + value: true, + }, + { + _id: 'SAML_Custom_Default_role_attribute_name', + value: 'role', + }, ].map((setting) => connection .db() @@ -66,8 +81,17 @@ const resetTestData = async (cleanupOnly = false) => { ); }; +const setupCustomRole = async (api: BaseTest['api']) => { + const roleResponse = await createCustomRole(api, { name: 'saml-role' }) + expect(roleResponse.status()).toBe(200); + + const { role } = await roleResponse.json(); + return role._id; +} + test.describe('SAML', () => { let poRegistration: Registration; + let samlRoleId: string; const containerPath = path.join(__dirname, 'containers', 'saml'); @@ -77,6 +101,11 @@ test.describe('SAML', () => { // Only one setting updated through the API to avoid refreshing the service configurations several times await expect((await setSettingValueById(api, 'SAML_Custom_Default', true)).status()).toBe(200); + // Create a new custom role + if (constants.IS_EE) { + samlRoleId = await setupCustomRole(api) + } + await compose.buildOne('testsamlidp_idp', { cwd: containerPath, }); @@ -86,7 +115,7 @@ test.describe('SAML', () => { }); }); - test.afterAll(async () => { + test.afterAll(async ({ api }) => { await compose.down({ cwd: containerPath, }); @@ -102,6 +131,11 @@ test.describe('SAML', () => { // Remove saml test users so they don't interfere with other tests await resetTestData(true); + + // Remove created custom role + if (constants.IS_EE) { + expect((await deleteCustomRole(api, 'saml-role')).status()).toBe(200); + } }); test.beforeEach(async ({ page }) => { @@ -260,6 +294,25 @@ test.describe('SAML', () => { }); }); + test('User Mapping - Custom Role', async ({ page, api }) => { + test.skip(!constants.IS_EE); + + await doLoginStep(page, 'samluser4'); + + await test.step('expect users role to have been mapped correctly', async () => { + const user = await getUserInfo(api, 'samluser4'); + + expect(user).toBeDefined(); + expect(user?.username).toBe('samluser4'); + expect(user?.name).toBe('Saml User 4'); + expect(user?.emails).toBeDefined(); + expect(user?.emails?.[0].address).toBe('samluser4@example.com'); + expect(user?.roles).toBeDefined(); + expect(user?.roles?.length).toBe(1); + expect(user?.roles).toContain(samlRoleId); + }); + }); + test.fixme('User Merge - By Custom Identifier', async () => { // Test user merge with a custom identifier configured in the fieldmap }); diff --git a/apps/meteor/tests/e2e/utils/custom-role.ts b/apps/meteor/tests/e2e/utils/custom-role.ts new file mode 100644 index 000000000000..472ba4c7c299 --- /dev/null +++ b/apps/meteor/tests/e2e/utils/custom-role.ts @@ -0,0 +1,11 @@ +import { Endpoints } from '@rocket.chat/rest-typings'; + +import type { BaseTest } from './test'; + +export async function createCustomRole(api: BaseTest['api'], data: Parameters[0]) { + return api.post('/roles.create', data); +} + +export async function deleteCustomRole(api: BaseTest['api'], roleId: string) { + return api.post('/roles.delete', { roleId }); +} From 01f208a1193637d576349e1d0de74b0fb88c96c0 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 28 Feb 2024 10:32:26 -0300 Subject: [PATCH 068/207] fix: client ignoring offline presence updates (#31833) --- .changeset/young-doors-bathe.md | 6 ++++++ apps/meteor/app/notifications/client/lib/Presence.ts | 3 --- ee/apps/ddp-streamer/package.json | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 .changeset/young-doors-bathe.md diff --git a/.changeset/young-doors-bathe.md b/.changeset/young-doors-bathe.md new file mode 100644 index 000000000000..24d004b5214e --- /dev/null +++ b/.changeset/young-doors-bathe.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/meteor': patch +'@rocket.chat/ddp-streamer': patch +--- + +Fix web UI not showing users presence updating to offline diff --git a/apps/meteor/app/notifications/client/lib/Presence.ts b/apps/meteor/app/notifications/client/lib/Presence.ts index dff4ecb0392f..09d0e3be1693 100644 --- a/apps/meteor/app/notifications/client/lib/Presence.ts +++ b/apps/meteor/app/notifications/client/lib/Presence.ts @@ -13,8 +13,5 @@ type args = [username: string, statusChanged?: UserStatus, statusText?: string]; export const STATUS_MAP = [UserStatus.OFFLINE, UserStatus.ONLINE, UserStatus.AWAY, UserStatus.BUSY, UserStatus.DISABLED]; Meteor.StreamerCentral.on('stream-user-presence', (uid: string, [username, statusChanged, statusText]: args) => { - if (!statusChanged) { - return; - } Presence.notify({ _id: uid, username, status: STATUS_MAP[statusChanged as any], statusText }); }); diff --git a/ee/apps/ddp-streamer/package.json b/ee/apps/ddp-streamer/package.json index 1a399758265c..65340aa2887e 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.2.5", + "version": "0.2.5-next.1", "description": "Rocket.Chat DDP-Streamer service", "scripts": { "build": "tsc -p tsconfig.json", From 201e97aba39afc30a8fd15d0bc98328b84693fb7 Mon Sep 17 00:00:00 2001 From: Debdut Chakraborty Date: Wed, 28 Feb 2024 19:03:16 +0530 Subject: [PATCH 069/207] fix: make lifecycle methods part of inherited class in federation services (#31844) --- .changeset/shy-bananas-repeat.md | 5 +++++ .../meteor/ee/server/local-services/federation/service.ts | 8 ++++++++ apps/meteor/server/services/federation/service.ts | 8 ++++++++ 3 files changed, 21 insertions(+) create mode 100644 .changeset/shy-bananas-repeat.md diff --git a/.changeset/shy-bananas-repeat.md b/.changeset/shy-bananas-repeat.md new file mode 100644 index 000000000000..8fe1388897a2 --- /dev/null +++ b/.changeset/shy-bananas-repeat.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed Federation not working with Microservice deployments diff --git a/apps/meteor/ee/server/local-services/federation/service.ts b/apps/meteor/ee/server/local-services/federation/service.ts index a1272f5c8661..15f661a29e63 100644 --- a/apps/meteor/ee/server/local-services/federation/service.ts +++ b/apps/meteor/ee/server/local-services/federation/service.ts @@ -207,4 +207,12 @@ export class FederationServiceEE extends AbstractBaseFederationServiceEE impleme await federationService.initialize(); return federationService; } + + async created(): Promise { + return super.created(); + } + + async stopped(): Promise { + return super.stopped(); + } } diff --git a/apps/meteor/server/services/federation/service.ts b/apps/meteor/server/services/federation/service.ts index be154cee4a68..931cb9248e6e 100644 --- a/apps/meteor/server/services/federation/service.ts +++ b/apps/meteor/server/services/federation/service.ts @@ -330,4 +330,12 @@ export class FederationService extends AbstractBaseFederationService implements await federationService.initialize(); return federationService; } + + public async stopped(): Promise { + return super.stopped(); + } + + public async created(): Promise { + return super.created(); + } } From b5171a2b29c486799561a157f6ad1296a95c30af Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 28 Feb 2024 13:53:47 -0300 Subject: [PATCH 070/207] chore: cache ui-context package (#31805) --- apps/meteor/packages/rocketchat-i18n/i18n | 1 + packages/i18n/src/locales | 1 - .../i18n => packages/i18n/src/locales}/af.i18n.json | 0 .../i18n => packages/i18n/src/locales}/ar.i18n.json | 0 .../i18n => packages/i18n/src/locales}/az.i18n.json | 0 .../i18n => packages/i18n/src/locales}/be-BY.i18n.json | 0 .../i18n => packages/i18n/src/locales}/bg.i18n.json | 0 .../i18n => packages/i18n/src/locales}/bn-BD.i18n.json | 0 .../i18n => packages/i18n/src/locales}/bn-IN.i18n.json | 0 .../i18n => packages/i18n/src/locales}/bs.i18n.json | 0 .../i18n => packages/i18n/src/locales}/ca.i18n.json | 0 .../i18n => packages/i18n/src/locales}/cs.i18n.json | 0 .../i18n => packages/i18n/src/locales}/cy.i18n.json | 0 .../i18n => packages/i18n/src/locales}/da.i18n.json | 0 .../i18n => packages/i18n/src/locales}/de-AT.i18n.json | 0 .../i18n => packages/i18n/src/locales}/de-IN.i18n.json | 0 .../i18n => packages/i18n/src/locales}/de.i18n.json | 0 .../i18n => packages/i18n/src/locales}/el.i18n.json | 0 .../i18n => packages/i18n/src/locales}/en.i18n.json | 0 .../i18n => packages/i18n/src/locales}/eo.i18n.json | 0 .../i18n => packages/i18n/src/locales}/es.i18n.json | 0 .../i18n => packages/i18n/src/locales}/et.i18n.json | 0 .../i18n => packages/i18n/src/locales}/eu.i18n.json | 0 .../i18n => packages/i18n/src/locales}/fa.i18n.json | 0 .../i18n => packages/i18n/src/locales}/fi.i18n.json | 0 .../i18n => packages/i18n/src/locales}/fr.i18n.json | 0 .../i18n => packages/i18n/src/locales}/gl.i18n.json | 0 .../i18n => packages/i18n/src/locales}/he.i18n.json | 0 .../i18n => packages/i18n/src/locales}/hi.i18n.json | 0 .../i18n => packages/i18n/src/locales}/hr.i18n.json | 0 .../i18n => packages/i18n/src/locales}/hu.i18n.json | 0 .../i18n => packages/i18n/src/locales}/id.i18n.json | 0 .../i18n => packages/i18n/src/locales}/it.i18n.json | 0 .../i18n => packages/i18n/src/locales}/ja.i18n.json | 0 .../i18n => packages/i18n/src/locales}/ka-GE.i18n.json | 0 .../i18n => packages/i18n/src/locales}/km.i18n.json | 0 .../i18n => packages/i18n/src/locales}/ko.i18n.json | 0 .../i18n => packages/i18n/src/locales}/ku.i18n.json | 0 .../i18n => packages/i18n/src/locales}/lo.i18n.json | 0 .../i18n => packages/i18n/src/locales}/lt.i18n.json | 0 .../i18n => packages/i18n/src/locales}/lv.i18n.json | 0 .../i18n => packages/i18n/src/locales}/mn.i18n.json | 0 .../i18n => packages/i18n/src/locales}/ms-MY.i18n.json | 0 .../i18n => packages/i18n/src/locales}/nl.i18n.json | 0 .../i18n => packages/i18n/src/locales}/no.i18n.json | 0 .../i18n => packages/i18n/src/locales}/pl.i18n.json | 0 .../i18n => packages/i18n/src/locales}/pt-BR.i18n.json | 0 .../i18n => packages/i18n/src/locales}/pt.i18n.json | 0 .../i18n => packages/i18n/src/locales}/ro.i18n.json | 0 .../i18n => packages/i18n/src/locales}/ru.i18n.json | 0 .../i18n => packages/i18n/src/locales}/si.i18n.json | 0 .../i18n => packages/i18n/src/locales}/sk-SK.i18n.json | 0 .../i18n => packages/i18n/src/locales}/sl-SI.i18n.json | 0 .../i18n => packages/i18n/src/locales}/sq.i18n.json | 0 .../i18n => packages/i18n/src/locales}/sr.i18n.json | 0 .../i18n => packages/i18n/src/locales}/sv.i18n.json | 0 .../i18n => packages/i18n/src/locales}/ta-IN.i18n.json | 0 .../i18n => packages/i18n/src/locales}/th-TH.i18n.json | 0 .../i18n => packages/i18n/src/locales}/tr.i18n.json | 0 .../i18n => packages/i18n/src/locales}/ug.i18n.json | 0 .../i18n => packages/i18n/src/locales}/uk.i18n.json | 0 .../i18n => packages/i18n/src/locales}/vi-VN.i18n.json | 0 .../i18n => packages/i18n/src/locales}/zh-HK.i18n.json | 0 .../i18n => packages/i18n/src/locales}/zh-TW.i18n.json | 0 .../i18n => packages/i18n/src/locales}/zh.i18n.json | 0 turbo.json | 8 -------- 66 files changed, 1 insertion(+), 9 deletions(-) create mode 120000 apps/meteor/packages/rocketchat-i18n/i18n delete mode 120000 packages/i18n/src/locales rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/af.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/ar.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/az.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/be-BY.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/bg.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/bn-BD.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/bn-IN.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/bs.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/ca.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/cs.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/cy.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/da.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/de-AT.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/de-IN.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/de.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/el.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/en.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/eo.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/es.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/et.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/eu.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/fa.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/fi.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/fr.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/gl.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/he.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/hi.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/hr.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/hu.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/id.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/it.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/ja.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/ka-GE.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/km.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/ko.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/ku.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/lo.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/lt.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/lv.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/mn.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/ms-MY.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/nl.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/no.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/pl.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/pt-BR.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/pt.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/ro.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/ru.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/si.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/sk-SK.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/sl-SI.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/sq.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/sr.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/sv.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/ta-IN.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/th-TH.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/tr.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/ug.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/uk.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/vi-VN.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/zh-HK.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/zh-TW.i18n.json (100%) rename {apps/meteor/packages/rocketchat-i18n/i18n => packages/i18n/src/locales}/zh.i18n.json (100%) diff --git a/apps/meteor/packages/rocketchat-i18n/i18n b/apps/meteor/packages/rocketchat-i18n/i18n new file mode 120000 index 000000000000..160d6db904d7 --- /dev/null +++ b/apps/meteor/packages/rocketchat-i18n/i18n @@ -0,0 +1 @@ +../../../../packages/i18n/src/locales \ No newline at end of file diff --git a/packages/i18n/src/locales b/packages/i18n/src/locales deleted file mode 120000 index fbf7fcee14cc..000000000000 --- a/packages/i18n/src/locales +++ /dev/null @@ -1 +0,0 @@ -../../../apps/meteor/packages/rocketchat-i18n/i18n \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json b/packages/i18n/src/locales/af.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json rename to packages/i18n/src/locales/af.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json b/packages/i18n/src/locales/ar.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json rename to packages/i18n/src/locales/ar.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json b/packages/i18n/src/locales/az.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json rename to packages/i18n/src/locales/az.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json b/packages/i18n/src/locales/be-BY.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json rename to packages/i18n/src/locales/be-BY.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json b/packages/i18n/src/locales/bg.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json rename to packages/i18n/src/locales/bg.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bn-BD.i18n.json b/packages/i18n/src/locales/bn-BD.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/bn-BD.i18n.json rename to packages/i18n/src/locales/bn-BD.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bn-IN.i18n.json b/packages/i18n/src/locales/bn-IN.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/bn-IN.i18n.json rename to packages/i18n/src/locales/bn-IN.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json b/packages/i18n/src/locales/bs.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json rename to packages/i18n/src/locales/bs.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json b/packages/i18n/src/locales/ca.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json rename to packages/i18n/src/locales/ca.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json b/packages/i18n/src/locales/cs.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json rename to packages/i18n/src/locales/cs.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json b/packages/i18n/src/locales/cy.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json rename to packages/i18n/src/locales/cy.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json b/packages/i18n/src/locales/da.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json rename to packages/i18n/src/locales/da.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json b/packages/i18n/src/locales/de-AT.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json rename to packages/i18n/src/locales/de-AT.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/de-IN.i18n.json b/packages/i18n/src/locales/de-IN.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/de-IN.i18n.json rename to packages/i18n/src/locales/de-IN.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json b/packages/i18n/src/locales/de.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json rename to packages/i18n/src/locales/de.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json b/packages/i18n/src/locales/el.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json rename to packages/i18n/src/locales/el.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/i18n/src/locales/en.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json rename to packages/i18n/src/locales/en.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json b/packages/i18n/src/locales/eo.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json rename to packages/i18n/src/locales/eo.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json b/packages/i18n/src/locales/es.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json rename to packages/i18n/src/locales/es.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/et.i18n.json b/packages/i18n/src/locales/et.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/et.i18n.json rename to packages/i18n/src/locales/et.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/eu.i18n.json b/packages/i18n/src/locales/eu.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/eu.i18n.json rename to packages/i18n/src/locales/eu.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json b/packages/i18n/src/locales/fa.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json rename to packages/i18n/src/locales/fa.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json b/packages/i18n/src/locales/fi.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json rename to packages/i18n/src/locales/fi.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json b/packages/i18n/src/locales/fr.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json rename to packages/i18n/src/locales/fr.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json b/packages/i18n/src/locales/gl.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json rename to packages/i18n/src/locales/gl.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json b/packages/i18n/src/locales/he.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json rename to packages/i18n/src/locales/he.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hi.i18n.json b/packages/i18n/src/locales/hi.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/hi.i18n.json rename to packages/i18n/src/locales/hi.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json b/packages/i18n/src/locales/hr.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json rename to packages/i18n/src/locales/hr.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json b/packages/i18n/src/locales/hu.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json rename to packages/i18n/src/locales/hu.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json b/packages/i18n/src/locales/id.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json rename to packages/i18n/src/locales/id.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json b/packages/i18n/src/locales/it.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json rename to packages/i18n/src/locales/it.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json b/packages/i18n/src/locales/ja.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json rename to packages/i18n/src/locales/ja.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json b/packages/i18n/src/locales/ka-GE.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json rename to packages/i18n/src/locales/ka-GE.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json b/packages/i18n/src/locales/km.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json rename to packages/i18n/src/locales/km.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json b/packages/i18n/src/locales/ko.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json rename to packages/i18n/src/locales/ko.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json b/packages/i18n/src/locales/ku.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json rename to packages/i18n/src/locales/ku.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json b/packages/i18n/src/locales/lo.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json rename to packages/i18n/src/locales/lo.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json b/packages/i18n/src/locales/lt.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json rename to packages/i18n/src/locales/lt.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json b/packages/i18n/src/locales/lv.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json rename to packages/i18n/src/locales/lv.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json b/packages/i18n/src/locales/mn.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json rename to packages/i18n/src/locales/mn.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json b/packages/i18n/src/locales/ms-MY.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json rename to packages/i18n/src/locales/ms-MY.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json b/packages/i18n/src/locales/nl.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json rename to packages/i18n/src/locales/nl.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json b/packages/i18n/src/locales/no.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json rename to packages/i18n/src/locales/no.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json b/packages/i18n/src/locales/pl.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json rename to packages/i18n/src/locales/pl.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/packages/i18n/src/locales/pt-BR.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json rename to packages/i18n/src/locales/pt-BR.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json b/packages/i18n/src/locales/pt.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json rename to packages/i18n/src/locales/pt.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json b/packages/i18n/src/locales/ro.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json rename to packages/i18n/src/locales/ro.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json b/packages/i18n/src/locales/ru.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json rename to packages/i18n/src/locales/ru.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/si.i18n.json b/packages/i18n/src/locales/si.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/si.i18n.json rename to packages/i18n/src/locales/si.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json b/packages/i18n/src/locales/sk-SK.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json rename to packages/i18n/src/locales/sk-SK.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json b/packages/i18n/src/locales/sl-SI.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json rename to packages/i18n/src/locales/sl-SI.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json b/packages/i18n/src/locales/sq.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json rename to packages/i18n/src/locales/sq.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json b/packages/i18n/src/locales/sr.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json rename to packages/i18n/src/locales/sr.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json b/packages/i18n/src/locales/sv.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json rename to packages/i18n/src/locales/sv.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json b/packages/i18n/src/locales/ta-IN.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json rename to packages/i18n/src/locales/ta-IN.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json b/packages/i18n/src/locales/th-TH.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json rename to packages/i18n/src/locales/th-TH.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json b/packages/i18n/src/locales/tr.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json rename to packages/i18n/src/locales/tr.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json b/packages/i18n/src/locales/ug.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json rename to packages/i18n/src/locales/ug.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json b/packages/i18n/src/locales/uk.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json rename to packages/i18n/src/locales/uk.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json b/packages/i18n/src/locales/vi-VN.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json rename to packages/i18n/src/locales/vi-VN.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json b/packages/i18n/src/locales/zh-HK.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json rename to packages/i18n/src/locales/zh-HK.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json b/packages/i18n/src/locales/zh-TW.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json rename to packages/i18n/src/locales/zh-TW.i18n.json diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json b/packages/i18n/src/locales/zh.i18n.json similarity index 100% rename from apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json rename to packages/i18n/src/locales/zh.i18n.json diff --git a/turbo.json b/turbo.json index 1eee804197d7..18a97bc0d47c 100644 --- a/turbo.json +++ b/turbo.json @@ -32,18 +32,10 @@ "ms": { "dependsOn": ["^build"] }, - "@rocket.chat/ui-contexts#build": { - "dependsOn": ["^build"], - "cache": false - }, "@rocket.chat/meteor#build": { "dependsOn": ["^build"], "cache": false }, - "@rocket.chat/i18n#build": { - "dependsOn": ["^build"], - "cache": false - }, "@rocket.chat/meteor#build:ci": { "dependsOn": ["^build"], "cache": false From 7ac473df7a16588254281bae6c1fa3a1ba97b598 Mon Sep 17 00:00:00 2001 From: Hugo Costa Date: Wed, 28 Feb 2024 15:14:52 -0300 Subject: [PATCH 071/207] fix(eslint-config): react config file export (#31845) Co-authored-by: Tasso Evangelista <2263066+tassoevan@users.noreply.github.com> --- .changeset/silly-socks-divide.md | 5 + apps/meteor/ee/server/services/package.json | 2 +- apps/meteor/package.json | 2 +- ee/apps/account-service/package.json | 2 +- ee/apps/authorization-service/package.json | 2 +- ee/apps/ddp-streamer/package.json | 2 +- ee/apps/omnichannel-transcript/package.json | 2 +- ee/apps/presence-service/package.json | 2 +- ee/apps/queue-worker/package.json | 2 +- ee/apps/stream-hub-service/package.json | 2 +- ee/packages/api-client/package.json | 2 +- ee/packages/ddp-client/package.json | 2 +- ee/packages/license/package.json | 2 +- ee/packages/omnichannel-services/package.json | 2 +- ee/packages/pdf-worker/package.json | 2 +- ee/packages/presence/package.json | 2 +- ee/packages/ui-theming/package.json | 2 +- packages/account-utils/package.json | 2 +- packages/agenda/package.json | 2 +- packages/base64/package.json | 2 +- packages/cas-validate/package.json | 2 +- packages/core-services/package.json | 2 +- packages/core-typings/package.json | 2 +- packages/cron/package.json | 2 +- packages/eslint-config/package.json | 2 +- packages/favicon/package.json | 2 +- packages/fuselage-ui-kit/package.json | 2 +- packages/gazzodown/package.json | 2 +- packages/i18n/package.json | 2 +- packages/instance-status/package.json | 2 +- packages/jwt/package.json | 2 +- packages/livechat/package.json | 2 +- packages/log-format/package.json | 2 +- packages/logger/package.json | 2 +- packages/message-parser/package.json | 2 +- packages/mock-providers/package.json | 2 +- packages/model-typings/package.json | 2 +- packages/models/package.json | 2 +- packages/node-poplib/package.json | 2 +- packages/password-policies/package.json | 2 +- packages/peggy-loader/package.json | 2 +- packages/random/package.json | 2 +- packages/release-action/package.json | 2 +- packages/release-changelog/package.json | 2 +- packages/rest-typings/package.json | 2 +- .../server-cloud-communication/package.json | 2 +- packages/server-fetch/package.json | 2 +- packages/sha256/package.json | 2 +- packages/tools/package.json | 2 +- packages/ui-avatar/package.json | 2 +- packages/ui-client/package.json | 2 +- packages/ui-composer/package.json | 2 +- packages/ui-contexts/package.json | 2 +- packages/ui-kit/package.json | 2 +- packages/ui-video-conf/package.json | 2 +- packages/uikit-playground/package.json | 2 +- packages/web-ui-registration/package.json | 2 +- yarn.lock | 146 ++++++++---------- 58 files changed, 124 insertions(+), 139 deletions(-) create mode 100644 .changeset/silly-socks-divide.md diff --git a/.changeset/silly-socks-divide.md b/.changeset/silly-socks-divide.md new file mode 100644 index 000000000000..aeaa5619a8b2 --- /dev/null +++ b/.changeset/silly-socks-divide.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/eslint-config": patch +--- + +Fixed react eslint config diff --git a/apps/meteor/ee/server/services/package.json b/apps/meteor/ee/server/services/package.json index 00a425ac60ef..576e354d68cb 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.3.2" + "typescript": "~5.3.3" }, "volta": { "extends": "../../../package.json" diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 0d9d0ecd3b70..13a749652ce6 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -210,7 +210,7 @@ "supports-color": "~7.2.0", "template-file": "^6.0.1", "ts-node": "^10.9.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "dependencies": { "@babel/runtime": "~7.22.15", diff --git a/ee/apps/account-service/package.json b/ee/apps/account-service/package.json index fc8009c40ea9..f02242b0661e 100644 --- a/ee/apps/account-service/package.json +++ b/ee/apps/account-service/package.json @@ -44,7 +44,7 @@ "@types/polka": "^0.5.6", "eslint": "~8.45.0", "ts-node": "^10.9.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "main": "./dist/ee/apps/account-service/src/service.js", "files": [ diff --git a/ee/apps/authorization-service/package.json b/ee/apps/authorization-service/package.json index 5a72761f597b..18caff67ffbc 100644 --- a/ee/apps/authorization-service/package.json +++ b/ee/apps/authorization-service/package.json @@ -41,7 +41,7 @@ "@types/polka": "^0.5.6", "eslint": "~8.45.0", "ts-node": "^10.9.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "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 65340aa2887e..327683a83869 100644 --- a/ee/apps/ddp-streamer/package.json +++ b/ee/apps/ddp-streamer/package.json @@ -57,7 +57,7 @@ "eslint": "~8.45.0", "pino-pretty": "^7.6.1", "ts-node": "^10.9.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "main": "./dist/service.js", "files": [ diff --git a/ee/apps/omnichannel-transcript/package.json b/ee/apps/omnichannel-transcript/package.json index f04bc3af8b50..3e7a6b1f44e9 100644 --- a/ee/apps/omnichannel-transcript/package.json +++ b/ee/apps/omnichannel-transcript/package.json @@ -48,7 +48,7 @@ "@types/polka": "^0.5.6", "eslint": "~8.45.0", "ts-node": "^10.9.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "main": "./dist/ee/apps/omnichannel-transcript/src/service.js", "files": [ diff --git a/ee/apps/presence-service/package.json b/ee/apps/presence-service/package.json index 955fe30be8c1..e682630ee594 100644 --- a/ee/apps/presence-service/package.json +++ b/ee/apps/presence-service/package.json @@ -41,7 +41,7 @@ "@types/polka": "^0.5.6", "eslint": "~8.45.0", "ts-node": "^10.9.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "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 1d6c7b938abe..80d7b604f964 100644 --- a/ee/apps/queue-worker/package.json +++ b/ee/apps/queue-worker/package.json @@ -45,7 +45,7 @@ "@types/polka": "^0.5.6", "eslint": "~8.45.0", "ts-node": "^10.9.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "main": "./dist/ee/apps/queue-worker/src/service.js", "files": [ diff --git a/ee/apps/stream-hub-service/package.json b/ee/apps/stream-hub-service/package.json index d51768be9d7b..ba0cefb140dd 100644 --- a/ee/apps/stream-hub-service/package.json +++ b/ee/apps/stream-hub-service/package.json @@ -43,7 +43,7 @@ "@types/polka": "^0.5.6", "eslint": "~8.45.0", "ts-node": "^10.9.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "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 edbb3fa686cb..93a60df954d5 100644 --- a/ee/packages/api-client/package.json +++ b/ee/packages/api-client/package.json @@ -11,7 +11,7 @@ "jest": "~29.6.4", "jest-fetch-mock": "^3.0.3", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "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 8ae25f9b443b..c4b632b4bd7f 100644 --- a/ee/packages/ddp-client/package.json +++ b/ee/packages/ddp-client/package.json @@ -12,7 +12,7 @@ "jest-environment-jsdom": "~29.6.4", "jest-websocket-mock": "^2.4.0", "ts-jest": "^29.1.2", - "typescript": "^5.3.2", + "typescript": "~5.3.3", "ws": "^8.13.0" }, "peerDependencies": { diff --git a/ee/packages/license/package.json b/ee/packages/license/package.json index 3f65c903816e..f5f9f9dbe8ec 100644 --- a/ee/packages/license/package.json +++ b/ee/packages/license/package.json @@ -13,7 +13,7 @@ "jest-environment-jsdom": "~29.6.4", "jest-websocket-mock": "^2.4.0", "ts-jest": "~29.1.1", - "typescript": "^5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/ee/packages/omnichannel-services/package.json b/ee/packages/omnichannel-services/package.json index 340a008293de..253f44999d97 100644 --- a/ee/packages/omnichannel-services/package.json +++ b/ee/packages/omnichannel-services/package.json @@ -8,7 +8,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "dependencies": { "@rocket.chat/core-services": "workspace:^", diff --git a/ee/packages/pdf-worker/package.json b/ee/packages/pdf-worker/package.json index 8c51bd06fe20..df82a24cbf86 100644 --- a/ee/packages/pdf-worker/package.json +++ b/ee/packages/pdf-worker/package.json @@ -16,7 +16,7 @@ "jest-environment-jsdom": "~29.6.4", "react-dom": "^18.2.0", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/ee/packages/presence/package.json b/ee/packages/presence/package.json index 321906d4f76a..1ed3007fff89 100644 --- a/ee/packages/presence/package.json +++ b/ee/packages/presence/package.json @@ -13,7 +13,7 @@ "babel-jest": "^29.0.3", "eslint": "~8.45.0", "jest": "~29.6.4", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint src", diff --git a/ee/packages/ui-theming/package.json b/ee/packages/ui-theming/package.json index 63732ae16db5..a8945368b45a 100644 --- a/ee/packages/ui-theming/package.json +++ b/ee/packages/ui-theming/package.json @@ -29,7 +29,7 @@ "react": "~17.0.2", "react-docgen-typescript-plugin": "~1.0.5", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/account-utils/package.json b/packages/account-utils/package.json index 4530fa86c3ba..d6c8161f264c 100644 --- a/packages/account-utils/package.json +++ b/packages/account-utils/package.json @@ -7,7 +7,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/agenda/package.json b/packages/agenda/package.json index 8880aa126da8..7351f373c861 100644 --- a/packages/agenda/package.json +++ b/packages/agenda/package.json @@ -17,7 +17,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/base64/package.json b/packages/base64/package.json index 99f9effe2ab0..a7fd818270f1 100644 --- a/packages/base64/package.json +++ b/packages/base64/package.json @@ -21,7 +21,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "volta": { "extends": "../../package.json" diff --git a/packages/cas-validate/package.json b/packages/cas-validate/package.json index 79cd4a03d644..8459c72ab4ea 100644 --- a/packages/cas-validate/package.json +++ b/packages/cas-validate/package.json @@ -8,7 +8,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/core-services/package.json b/packages/core-services/package.json index 868775a355f3..4bcbd2d7d196 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -15,7 +15,7 @@ "jest": "~29.6.4", "mongodb": "^4.17.2", "prettier": "~2.8.8", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/core-typings/package.json b/packages/core-typings/package.json index 930fbc77717b..5889cbfa1dcd 100644 --- a/packages/core-typings/package.json +++ b/packages/core-typings/package.json @@ -8,7 +8,7 @@ "eslint": "~8.45.0", "mongodb": "^4.17.2", "prettier": "~2.8.8", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/cron/package.json b/packages/cron/package.json index 9d9b2fb936f4..eed5bc3cab35 100644 --- a/packages/cron/package.json +++ b/packages/cron/package.json @@ -7,7 +7,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index ebfde8c5612b..252986724eae 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -32,6 +32,6 @@ "/node", "/style", "/variables", - "/react" + "react.js" ] } diff --git a/packages/favicon/package.json b/packages/favicon/package.json index 0166a5e6263d..b61eaee02a68 100644 --- a/packages/favicon/package.json +++ b/packages/favicon/package.json @@ -4,7 +4,7 @@ "private": true, "devDependencies": { "eslint": "~8.45.0", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/fuselage-ui-kit/package.json b/packages/fuselage-ui-kit/package.json index 69a1f1525edd..f4a8fb062e81 100644 --- a/packages/fuselage-ui-kit/package.json +++ b/packages/fuselage-ui-kit/package.json @@ -97,7 +97,7 @@ "rimraf": "^3.0.2", "storybook-dark-mode": "~3.0.1", "tslib": "^2.5.3", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "dependencies": { "@rocket.chat/gazzodown": "workspace:^", diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index da2d978b46c5..73b164905f93 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -46,7 +46,7 @@ "react-docgen-typescript-plugin": "~1.0.5", "react-dom": "~17.0.2", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 8e41a6593ff8..0b65daefcaad 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -10,7 +10,7 @@ "jest": "~29.6.4", "ts-jest": "~29.1.1", "tsup": "^6.7.0", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "build": "node ./src/index.mjs", diff --git a/packages/instance-status/package.json b/packages/instance-status/package.json index 362818b7e809..59136688e313 100644 --- a/packages/instance-status/package.json +++ b/packages/instance-status/package.json @@ -7,7 +7,7 @@ "eslint": "~8.45.0", "mongodb": "^4.17.2", "prettier": "~2.8.8", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/jwt/package.json b/packages/jwt/package.json index 7731e66ab13a..d93ec0dc4228 100644 --- a/packages/jwt/package.json +++ b/packages/jwt/package.json @@ -7,7 +7,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "^29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/livechat/package.json b/packages/livechat/package.json index c5ce385f0345..c5bbb001cd1d 100644 --- a/packages/livechat/package.json +++ b/packages/livechat/package.json @@ -82,7 +82,7 @@ "stylelint-order": "^5.0.0", "svg-loader": "^0.0.2", "terser-webpack-plugin": "~4.2.3", - "typescript": "~5.3.2", + "typescript": "~5.3.3", "url-loader": "^4.1.1", "webpack": "^5.89.0", "webpack-bundle-analyzer": "^4.9.1", diff --git a/packages/log-format/package.json b/packages/log-format/package.json index 43b2ee55738b..33eeacd1272f 100644 --- a/packages/log-format/package.json +++ b/packages/log-format/package.json @@ -9,7 +9,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/logger/package.json b/packages/logger/package.json index fca694e53d57..efbe5aa6e12f 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -7,7 +7,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/message-parser/package.json b/packages/message-parser/package.json index 5a4ff3ba8ba2..4dea09d7fca4 100644 --- a/packages/message-parser/package.json +++ b/packages/message-parser/package.json @@ -68,7 +68,7 @@ "ts-jest": "~29.1.0", "ts-loader": "~9.4.2", "typedoc": "~0.24.1", - "typescript": "~5.0.4", + "typescript": "~5.3.3", "webpack": "~5.78.0", "webpack-cli": "~5.0.1" }, diff --git a/packages/mock-providers/package.json b/packages/mock-providers/package.json index 93bbd7a01968..88571e402435 100644 --- a/packages/mock-providers/package.json +++ b/packages/mock-providers/package.json @@ -16,7 +16,7 @@ "jest": "~29.6.4", "react": "~17.0.2", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "peerDependencies": { "@tanstack/react-query": "*", diff --git a/packages/model-typings/package.json b/packages/model-typings/package.json index 2ab3abd2d1d1..6bc0059d5bb4 100644 --- a/packages/model-typings/package.json +++ b/packages/model-typings/package.json @@ -9,7 +9,7 @@ "jest": "~29.6.4", "mongodb": "^4.17.2", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/models/package.json b/packages/models/package.json index 336ad812ced7..c1e168b2e417 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -7,7 +7,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "dependencies": { "@rocket.chat/model-typings": "workspace:^" diff --git a/packages/node-poplib/package.json b/packages/node-poplib/package.json index 1db06d479638..9e9a112945e6 100644 --- a/packages/node-poplib/package.json +++ b/packages/node-poplib/package.json @@ -7,7 +7,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "test": "jest" diff --git a/packages/password-policies/package.json b/packages/password-policies/package.json index 2040c217805f..920554bad203 100644 --- a/packages/password-policies/package.json +++ b/packages/password-policies/package.json @@ -9,7 +9,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/peggy-loader/package.json b/packages/peggy-loader/package.json index eaa775d7c995..2b1983bfa62a 100644 --- a/packages/peggy-loader/package.json +++ b/packages/peggy-loader/package.json @@ -51,7 +51,7 @@ "prettier": "~2.8.7", "rimraf": "^3.0.2", "ts-jest": "~29.1.0", - "typescript": "~5.0.4", + "typescript": "~5.3.3", "webpack": "~5.78.0" } } diff --git a/packages/random/package.json b/packages/random/package.json index 08624a05424a..343ce2344ab1 100644 --- a/packages/random/package.json +++ b/packages/random/package.json @@ -23,7 +23,7 @@ "jest": "~29.6.4", "jest-environment-jsdom": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "volta": { "extends": "../../package.json" diff --git a/packages/release-action/package.json b/packages/release-action/package.json index c5aac2e9fb64..77ae0587e79a 100644 --- a/packages/release-action/package.json +++ b/packages/release-action/package.json @@ -11,7 +11,7 @@ "packageManager": "yarn@3.5.1", "devDependencies": { "@types/node": "^16.18.60", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "dependencies": { "@actions/core": "^1.10.1", diff --git a/packages/release-changelog/package.json b/packages/release-changelog/package.json index e3c1db973736..9e48d015b321 100644 --- a/packages/release-changelog/package.json +++ b/packages/release-changelog/package.json @@ -12,7 +12,7 @@ "@rocket.chat/eslint-config": "workspace:^", "@types/node": "^14.18.63", "eslint": "~8.45.0", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "dependencies": { "dataloader": "^1.4.0", diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index 34a2a79086a0..853b72e64c55 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -10,7 +10,7 @@ "jest-environment-jsdom": "~29.6.4", "mongodb": "^4.17.2", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/server-cloud-communication/package.json b/packages/server-cloud-communication/package.json index ce3bb5050865..878a0565306b 100644 --- a/packages/server-cloud-communication/package.json +++ b/packages/server-cloud-communication/package.json @@ -8,7 +8,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "volta": { "extends": "../../package.json" diff --git a/packages/server-fetch/package.json b/packages/server-fetch/package.json index b0fb30ecb6fd..e1527e928703 100644 --- a/packages/server-fetch/package.json +++ b/packages/server-fetch/package.json @@ -7,7 +7,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/sha256/package.json b/packages/sha256/package.json index ce0d51c41f7e..1b2f1fa6b523 100644 --- a/packages/sha256/package.json +++ b/packages/sha256/package.json @@ -21,7 +21,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "volta": { "extends": "../../package.json" diff --git a/packages/tools/package.json b/packages/tools/package.json index f8bba3185d8a..e6bbae9b5be6 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -7,7 +7,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/ui-avatar/package.json b/packages/ui-avatar/package.json index e968d4567d9c..f64c5a2383ae 100644 --- a/packages/ui-avatar/package.json +++ b/packages/ui-avatar/package.json @@ -15,7 +15,7 @@ "eslint-plugin-storybook": "~0.6.15", "eslint-plugin-testing-library": "~5.11.1", "react": "^17.0.2", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index 64b39555e3a2..360e7ef8c02e 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -40,7 +40,7 @@ "react-dom": "^17.0.2", "react-hook-form": "~7.45.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json index f3ac9ea3d555..468c45b1c39c 100644 --- a/packages/ui-composer/package.json +++ b/packages/ui-composer/package.json @@ -24,7 +24,7 @@ "jest": "~29.6.4", "react-docgen-typescript-plugin": "~1.0.5", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "peerDependencies": { "@react-aria/toolbar": "*", diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index 223de3de48de..88cf94911973 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -18,7 +18,7 @@ "mongodb": "^4.17.2", "react": "~17.0.2", "ts-jest": "~29.1.1", - "typescript": "~5.3.2", + "typescript": "~5.3.3", "use-sync-external-store": "^1.2.0" }, "peerDependencies": { diff --git a/packages/ui-kit/package.json b/packages/ui-kit/package.json index b2ee9668680d..23cd40877dfe 100644 --- a/packages/ui-kit/package.json +++ b/packages/ui-kit/package.json @@ -50,7 +50,7 @@ "ts-loader": "~9.4.2", "ts-node": "~10.9.1", "ts-patch": "~3.0.2", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "dependencies": { "typia": "~5.3.3" diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index 156d5594fabe..5fa7370d0d21 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -28,7 +28,7 @@ "jest": "~29.6.4", "react-docgen-typescript-plugin": "~1.0.5", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "peerDependencies": { "@rocket.chat/css-in-js": "*", diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index 6028c769f189..f9aa9987a658 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -50,7 +50,7 @@ "eslint": "~8.45.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.4", - "typescript": "~5.3.2", + "typescript": "~5.3.3", "vite": "^4.3.9" }, "volta": { diff --git a/packages/web-ui-registration/package.json b/packages/web-ui-registration/package.json index 3024d92e689f..eb286933d3bb 100644 --- a/packages/web-ui-registration/package.json +++ b/packages/web-ui-registration/package.json @@ -46,7 +46,7 @@ "react-i18next": "~13.2.2", "storybook-dark-mode": "~3.0.1", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "peerDependencies": { "@rocket.chat/layout": "*", diff --git a/yarn.lock b/yarn.lock index 8b8833493494..c0ec55ab90f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8273,7 +8273,7 @@ __metadata: pino: ^8.15.0 polka: ^0.5.2 ts-node: ^10.9.1 - typescript: ~5.3.2 + typescript: ~5.3.3 uuid: ^9.0.1 languageName: unknown linkType: soft @@ -8286,7 +8286,7 @@ __metadata: eslint: ~8.45.0 jest: ~29.6.4 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -8305,7 +8305,7 @@ __metadata: moment-timezone: ~0.5.43 mongodb: ^4.17.2 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -8327,7 +8327,7 @@ __metadata: split-on-first: ^3.0.0 strict-uri-encode: ^2.0.0 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -8377,7 +8377,7 @@ __metadata: pino: ^8.15.0 polka: ^0.5.2 ts-node: ^10.9.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -8393,7 +8393,7 @@ __metadata: eslint: ~8.45.0 jest: ~29.6.4 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -8406,7 +8406,7 @@ __metadata: eslint: ~8.45.0 jest: ~29.6.4 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -8435,7 +8435,7 @@ __metadata: jest: ~29.6.4 mongodb: ^4.17.2 prettier: ~2.8.8 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -8451,7 +8451,7 @@ __metadata: eslint: ~8.45.0 mongodb: ^4.17.2 prettier: ~2.8.8 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -8468,7 +8468,7 @@ __metadata: jest: ~29.6.4 mongodb: ^4.17.2 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -8509,7 +8509,7 @@ __metadata: jest-environment-jsdom: ~29.6.4 jest-websocket-mock: ^2.4.0 ts-jest: ^29.1.2 - typescript: ^5.3.2 + typescript: ~5.3.3 ws: ^8.13.0 peerDependencies: "@rocket.chat/emitter": "*" @@ -8557,7 +8557,7 @@ __metadata: polka: ^0.5.2 sharp: ^0.32.6 ts-node: ^10.9.1 - typescript: ~5.3.2 + typescript: ~5.3.3 underscore: ^1.13.6 uuid: ^7.0.3 ws: ^8.8.1 @@ -8596,7 +8596,7 @@ __metadata: resolution: "@rocket.chat/favicon@workspace:packages/favicon" dependencies: eslint: ~8.45.0 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -8752,7 +8752,7 @@ __metadata: rimraf: ^3.0.2 storybook-dark-mode: ~3.0.1 tslib: ^2.5.3 - typescript: ~5.3.2 + typescript: ~5.3.3 peerDependencies: "@rocket.chat/apps-engine": "*" "@rocket.chat/eslint-config": 0.6.1 @@ -8845,7 +8845,7 @@ __metadata: react-dom: ~17.0.2 react-error-boundary: ^3.1.4 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 peerDependencies: "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/css-in-js": "*" @@ -8871,7 +8871,7 @@ __metadata: jest: ~29.6.4 ts-jest: ~29.1.1 tsup: ^6.7.0 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -8891,7 +8891,7 @@ __metadata: eslint: ~8.45.0 mongodb: ^4.17.2 prettier: ~2.8.8 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -8904,7 +8904,7 @@ __metadata: jest: ~29.6.4 jose: ^4.14.4 ts-jest: ^29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -8938,7 +8938,7 @@ __metadata: jest-environment-jsdom: ~29.6.4 jest-websocket-mock: ^2.4.0 ts-jest: ~29.1.1 - typescript: ^5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9022,7 +9022,7 @@ __metadata: stylelint-order: ^5.0.0 svg-loader: ^0.0.2 terser-webpack-plugin: ~4.2.3 - typescript: ~5.3.2 + typescript: ~5.3.3 url-loader: ^4.1.1 webpack: ^5.89.0 webpack-bundle-analyzer: ^4.9.1 @@ -9047,7 +9047,7 @@ __metadata: eslint: ~8.45.0 jest: ~29.6.4 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9061,7 +9061,7 @@ __metadata: jest: ~29.6.4 pino: ^8.15.0 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9110,7 +9110,7 @@ __metadata: ts-jest: ~29.1.0 ts-loader: ~9.4.2 typedoc: ~0.24.1 - typescript: ~5.0.4 + typescript: ~5.3.3 webpack: ~5.78.0 webpack-cli: ~5.0.1 languageName: unknown @@ -9481,7 +9481,7 @@ __metadata: turndown: ^7.1.2 twilio: ^3.76.1 twit: ^2.2.11 - typescript: ~5.3.2 + typescript: ~5.3.3 typia: ^5.3.3 ua-parser-js: ^1.0.37 underscore: ^1.13.6 @@ -9514,7 +9514,7 @@ __metadata: react: ~17.0.2 react-i18next: ~13.2.2 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 peerDependencies: "@tanstack/react-query": "*" react: "*" @@ -9532,7 +9532,7 @@ __metadata: jest: ~29.6.4 mongodb: ^4.17.2 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9545,7 +9545,7 @@ __metadata: eslint: ~8.45.0 jest: ~29.6.4 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9587,7 +9587,7 @@ __metadata: mongodb: ^4.17.2 pino: ^8.15.0 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9626,7 +9626,7 @@ __metadata: pino: ^8.15.0 polka: ^0.5.2 ts-node: ^10.9.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9661,7 +9661,7 @@ __metadata: eslint: ~8.45.0 jest: ~29.6.4 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9691,7 +9691,7 @@ __metadata: react: ^18.2.0 react-dom: ^18.2.0 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9708,7 +9708,7 @@ __metadata: prettier: ~2.8.7 rimraf: ^3.0.2 ts-jest: ~29.1.0 - typescript: ~5.0.4 + typescript: ~5.3.3 webpack: ~5.78.0 peerDependencies: peggy: "*" @@ -9724,7 +9724,7 @@ __metadata: eslint: ~8.45.0 jest: ~29.6.4 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9756,7 +9756,7 @@ __metadata: pino: ^8.15.0 polka: ^0.5.2 ts-node: ^10.9.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9778,7 +9778,7 @@ __metadata: eslint: ~8.45.0 jest: ~29.6.4 mongodb: ^4.17.2 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9823,7 +9823,7 @@ __metadata: pino: ^8.15.0 polka: ^0.5.2 ts-node: ^10.9.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9840,7 +9840,7 @@ __metadata: jest: ~29.6.4 jest-environment-jsdom: ~29.6.4 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9859,7 +9859,7 @@ __metadata: remark-parse: 9.0.0 remark-stringify: 9.0.1 semver: ^7.5.2 - typescript: ~5.3.2 + typescript: ~5.3.3 unified: 9.2.2 languageName: unknown linkType: soft @@ -9874,7 +9874,7 @@ __metadata: dataloader: ^1.4.0 eslint: ~8.45.0 node-fetch: ^2 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9895,7 +9895,7 @@ __metadata: jest-environment-jsdom: ~29.6.4 mongodb: ^4.17.2 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9921,7 +9921,7 @@ __metadata: eslint: ~8.45.0 jest: ~29.6.4 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9938,7 +9938,7 @@ __metadata: node-fetch: 2.3.0 proxy-from-env: ^1.1.0 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9954,7 +9954,7 @@ __metadata: eslint: ~8.45.0 jest: ~29.6.4 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -9988,7 +9988,7 @@ __metadata: pino: ^8.15.0 polka: ^0.5.2 ts-node: ^10.9.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -10028,7 +10028,7 @@ __metadata: jest: ~29.6.4 moment-timezone: ^0.5.43 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft @@ -10048,7 +10048,7 @@ __metadata: eslint-plugin-storybook: ~0.6.15 eslint-plugin-testing-library: ~5.11.1 react: ^17.0.2 - typescript: ~5.3.2 + typescript: ~5.3.3 peerDependencies: "@rocket.chat/fuselage": "*" "@rocket.chat/ui-contexts": "*" @@ -10097,7 +10097,7 @@ __metadata: react-dom: ^17.0.2 react-hook-form: ~7.45.4 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 peerDependencies: "@react-aria/toolbar": "*" "@rocket.chat/css-in-js": "*" @@ -10134,7 +10134,7 @@ __metadata: jest: ~29.6.4 react-docgen-typescript-plugin: ~1.0.5 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 peerDependencies: "@react-aria/toolbar": "*" "@rocket.chat/fuselage": "*" @@ -10164,7 +10164,7 @@ __metadata: mongodb: ^4.17.2 react: ~17.0.2 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 use-sync-external-store: ^1.2.0 peerDependencies: "@rocket.chat/core-typings": "workspace:^" @@ -10198,7 +10198,7 @@ __metadata: ts-loader: ~9.4.2 ts-node: ~10.9.1 ts-patch: ~3.0.2 - typescript: ~5.3.2 + typescript: ~5.3.3 typia: ~5.3.3 languageName: unknown linkType: soft @@ -10233,7 +10233,7 @@ __metadata: react: ~17.0.2 react-docgen-typescript-plugin: ~1.0.5 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 peerDependencies: "@rocket.chat/css-in-js": "*" "@rocket.chat/fuselage": "*" @@ -10273,7 +10273,7 @@ __metadata: jest: ~29.6.4 react-docgen-typescript-plugin: ~1.0.5 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 peerDependencies: "@rocket.chat/css-in-js": "*" "@rocket.chat/fuselage": "*" @@ -10328,7 +10328,7 @@ __metadata: react-split-pane: ^0.1.92 react-virtuoso: ^4.3.10 reactflow: ^11.7.2 - typescript: ~5.3.2 + typescript: ~5.3.3 use-subscription: ^1.8.0 vite: ^4.3.9 languageName: unknown @@ -10367,7 +10367,7 @@ __metadata: react-i18next: ~13.2.2 storybook-dark-mode: ~3.0.1 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 peerDependencies: "@rocket.chat/layout": "*" "@rocket.chat/tools": 0.2.1 @@ -36221,7 +36221,7 @@ __metadata: sodium-native: ^3.3.0 sodium-plus: ^0.9.0 ts-node: ^10.9.1 - typescript: ~5.3.2 + typescript: ~5.3.3 uuid: ^8.3.2 ws: ^8.8.1 languageName: unknown @@ -40007,43 +40007,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.3.2, typescript@npm:~5.3.2": - version: 5.3.2 - resolution: "typescript@npm:5.3.2" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: d92534dda639eb825db013203404c1fabca8ac630564283c9e7dc9e64fd9c9346c2de95ecebdf3e6e8c1c32941bca1cfe0da37877611feb9daf8feeaea58d230 - languageName: node - linkType: hard - -"typescript@npm:~5.0.4": - 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.3.2#~builtin, typescript@patch:typescript@~5.3.2#~builtin": - version: 5.3.2 - resolution: "typescript@patch:typescript@npm%3A5.3.2#~builtin::version=5.3.2&hash=85af82" +"typescript@npm:~5.3.3": + version: 5.3.3 + resolution: "typescript@npm:5.3.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: c034461079fbfde3cb584ddee52afccb15b6e32a0ce186d0b2719968786f7ca73e1b07f71fac4163088790b16811c6ccf79680de190664ef66ff0ba9c1fe4a23 + checksum: 2007ccb6e51bbbf6fde0a78099efe04dc1c3dfbdff04ca3b6a8bc717991862b39fd6126c0c3ebf2d2d98ac5e960bcaa873826bb2bb241f14277034148f41f6a2 languageName: node linkType: hard -"typescript@patch:typescript@~5.0.4#~builtin": - version: 5.0.4 - resolution: "typescript@patch:typescript@npm%3A5.0.4#~builtin::version=5.0.4&hash=85af82" +"typescript@patch:typescript@~5.3.3#~builtin": + version: 5.3.3 + resolution: "typescript@patch:typescript@npm%3A5.3.3#~builtin::version=5.3.3&hash=85af82" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: bb309d320c59a26565fb3793dba550576ab861018ff3fd1b7fccabbe46ae4a35546bc45f342c0a0b6f265c801ccdf64ffd68f548f117ceb7f0eac4b805cd52a9 + checksum: f61375590b3162599f0f0d5b8737877ac0a7bc52761dbb585d67e7b8753a3a4c42d9a554c4cc929f591ffcf3a2b0602f65ae3ce74714fd5652623a816862b610 languageName: node linkType: hard From 6fe9387b258783120563d179d79d5deca968fac2 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Wed, 28 Feb 2024 16:44:29 -0300 Subject: [PATCH 072/207] chore: Sidebar toolbar keyboard navigation (#31846) --- apps/meteor/client/sidebar/header/Header.tsx | 7 ++--- .../client/sidebar/header/HeaderUnstable.tsx | 5 ++-- .../sidebar/header/SidebarHeaderToolbar.tsx | 13 ++++++++++ .../sidebar/sections/OmnichannelSection.tsx | 9 ++++--- apps/meteor/package.json | 2 +- ee/packages/ui-theming/package.json | 2 +- packages/fuselage-ui-kit/package.json | 2 +- packages/gazzodown/package.json | 2 +- packages/i18n/src/locales/en.i18n.json | 2 ++ packages/ui-avatar/package.json | 2 +- packages/ui-client/package.json | 2 +- packages/ui-composer/package.json | 2 +- packages/ui-video-conf/package.json | 2 +- packages/uikit-playground/package.json | 2 +- yarn.lock | 26 +++++++++---------- 15 files changed, 49 insertions(+), 31 deletions(-) create mode 100644 apps/meteor/client/sidebar/header/SidebarHeaderToolbar.tsx diff --git a/apps/meteor/client/sidebar/header/Header.tsx b/apps/meteor/client/sidebar/header/Header.tsx index 8b1c838f8d62..b7f00af460ae 100644 --- a/apps/meteor/client/sidebar/header/Header.tsx +++ b/apps/meteor/client/sidebar/header/Header.tsx @@ -1,8 +1,9 @@ import { Sidebar } from '@rocket.chat/fuselage'; -import { useUser, useTranslation } from '@rocket.chat/ui-contexts'; +import { useTranslation, useUser } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { memo } from 'react'; +import SidebarHeaderToolbar from './SidebarHeaderToolbar'; import UserAvatarWithStatus from './UserAvatarWithStatus'; import UserMenu from './UserMenu'; import Administration from './actions/Administration'; @@ -26,7 +27,7 @@ const Header = (): ReactElement => { return ( {user ? : } - + {user && ( @@ -38,7 +39,7 @@ const Header = (): ReactElement => { )} {!user && } - + ); }; diff --git a/apps/meteor/client/sidebar/header/HeaderUnstable.tsx b/apps/meteor/client/sidebar/header/HeaderUnstable.tsx index c732b646ccc6..ac6929bf2fb3 100644 --- a/apps/meteor/client/sidebar/header/HeaderUnstable.tsx +++ b/apps/meteor/client/sidebar/header/HeaderUnstable.tsx @@ -3,6 +3,7 @@ import { useUserId, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { memo } from 'react'; +import SidebarHeaderToolbar from './SidebarHeaderToolbar'; import CreateRoom from './actions/CreateRoom'; import Directory from './actions/Directory'; import Login from './actions/Login'; @@ -15,7 +16,7 @@ const HeaderUnstable = (): ReactElement => { return ( - + {uid && ( <> @@ -25,7 +26,7 @@ const HeaderUnstable = (): ReactElement => { )} {!uid && } - + ); }; diff --git a/apps/meteor/client/sidebar/header/SidebarHeaderToolbar.tsx b/apps/meteor/client/sidebar/header/SidebarHeaderToolbar.tsx new file mode 100644 index 000000000000..d897c352072d --- /dev/null +++ b/apps/meteor/client/sidebar/header/SidebarHeaderToolbar.tsx @@ -0,0 +1,13 @@ +import { useToolbar } from '@react-aria/toolbar'; +import { TopBarActions } from '@rocket.chat/fuselage'; +import type { ComponentProps } from 'react'; +import React, { useRef } from 'react'; + +const SidebarHeaderToolbar = (props: ComponentProps) => { + const ref = useRef(null); + const { toolbarProps } = useToolbar(props, ref); + + return ; +}; + +export default SidebarHeaderToolbar; diff --git a/apps/meteor/client/sidebar/sections/OmnichannelSection.tsx b/apps/meteor/client/sidebar/sections/OmnichannelSection.tsx index c4538166bba0..6dd63e6a36d4 100644 --- a/apps/meteor/client/sidebar/sections/OmnichannelSection.tsx +++ b/apps/meteor/client/sidebar/sections/OmnichannelSection.tsx @@ -6,6 +6,7 @@ import React, { memo } from 'react'; import { useIsCallEnabled, useIsCallReady } from '../../contexts/CallContext'; import { useIsOverMacLimit } from '../../hooks/omnichannel/useIsOverMacLimit'; import { useOmnichannelShowQueueLink } from '../../hooks/omnichannel/useOmnichannelShowQueueLink'; +import SidebarHeaderToolbar from '../header/SidebarHeaderToolbar'; import { OverMacLimitSection } from './OverMacLimitSection'; import { OmniChannelCallDialPad, OmnichannelCallToggle, OmnichannelLivechatToggle } from './actions'; @@ -38,9 +39,9 @@ const OmnichannelSection = () => { <> {isWorkspaceOverMacLimit && } - + {t('Omnichannel')} - + {showOmnichannelQueueLink && ( handleRoute('queue')} /> )} @@ -55,8 +56,8 @@ const OmnichannelSection = () => { /> )} {isCallReady && } - - + + ); }; diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 13a749652ce6..113b9979f492 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -240,7 +240,7 @@ "@rocket.chat/favicon": "workspace:^", "@rocket.chat/forked-matrix-appservice-bridge": "^4.0.2", "@rocket.chat/forked-matrix-bot-sdk": "^0.6.0-beta.3", - "@rocket.chat/fuselage": "^0.47.1", + "@rocket.chat/fuselage": "^0.48.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/fuselage-toastbar": "~0.31.25", diff --git a/ee/packages/ui-theming/package.json b/ee/packages/ui-theming/package.json index a8945368b45a..8243c3a9d664 100644 --- a/ee/packages/ui-theming/package.json +++ b/ee/packages/ui-theming/package.json @@ -4,7 +4,7 @@ "private": true, "devDependencies": { "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.47.1", + "@rocket.chat/fuselage": "^0.48.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/ui-contexts": "workspace:~", diff --git a/packages/fuselage-ui-kit/package.json b/packages/fuselage-ui-kit/package.json index f4a8fb062e81..068e559f101a 100644 --- a/packages/fuselage-ui-kit/package.json +++ b/packages/fuselage-ui-kit/package.json @@ -63,7 +63,7 @@ "@babel/preset-typescript": "~7.22.15", "@rocket.chat/apps-engine": "1.41.0", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.47.1", + "@rocket.chat/fuselage": "^0.48.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/icons": "^0.33.0", diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index 73b164905f93..b95a374fe96c 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.47.1", + "@rocket.chat/fuselage": "^0.48.0", "@rocket.chat/fuselage-tokens": "~0.32.0", "@rocket.chat/message-parser": "workspace:^", "@rocket.chat/styled": "~0.31.25", diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 9088b80f4672..271907a68caa 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -3162,6 +3162,7 @@ "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_actions": "Omnichannel actions", "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}}", @@ -4816,6 +4817,7 @@ "Show_or_hide_the_user_roles_of_message_authors": "Show or hide the user roles of message authors.", "Show_or_hide_the_username_of_message_authors": "Show or hide the username of message authors.", "Sidebar": "Sidebar", + "Sidebar_actions": "Sidebar actions", "Sidebar_list_mode": "Sidebar Channel List Mode", "Sign_in_to_start_talking": "Sign in to start talking", "Sign_in_with__provider__": "Sign in with {{provider}}", diff --git a/packages/ui-avatar/package.json b/packages/ui-avatar/package.json index f64c5a2383ae..7bf05ddaf253 100644 --- a/packages/ui-avatar/package.json +++ b/packages/ui-avatar/package.json @@ -4,7 +4,7 @@ "private": true, "devDependencies": { "@babel/core": "~7.22.20", - "@rocket.chat/fuselage": "^0.47.1", + "@rocket.chat/fuselage": "^0.48.0", "@rocket.chat/ui-contexts": "workspace:^", "@types/babel__core": "~7.20.3", "@types/react": "~17.0.69", diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index 360e7ef8c02e..29518b2febe9 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.47.1", + "@rocket.chat/fuselage": "^0.48.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/mock-providers": "workspace:^", diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json index 468c45b1c39c..bc63d18be2b3 100644 --- a/packages/ui-composer/package.json +++ b/packages/ui-composer/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.47.1", + "@rocket.chat/fuselage": "^0.48.0", "@rocket.chat/icons": "^0.33.0", "@storybook/addon-actions": "~6.5.16", "@storybook/addon-docs": "~6.5.16", diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index 5fa7370d0d21..2bdc366985b0 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.47.1", + "@rocket.chat/fuselage": "^0.48.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/styled": "~0.31.25", diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index f9aa9987a658..f46a2a6f634c 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -15,7 +15,7 @@ "@codemirror/tooltip": "^0.19.16", "@lezer/highlight": "^1.1.6", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.47.1", + "@rocket.chat/fuselage": "^0.48.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/fuselage-toastbar": "^0.31.25", diff --git a/yarn.lock b/yarn.lock index c0ec55ab90f5..42180b9cb174 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8717,7 +8717,7 @@ __metadata: "@babel/preset-typescript": ~7.22.15 "@rocket.chat/apps-engine": 1.41.0 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.47.1 + "@rocket.chat/fuselage": ^0.48.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/gazzodown": "workspace:^" @@ -8772,9 +8772,9 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/fuselage@npm:^0.47.1": - version: 0.47.1 - resolution: "@rocket.chat/fuselage@npm:0.47.1" +"@rocket.chat/fuselage@npm:^0.48.0": + version: 0.48.0 + resolution: "@rocket.chat/fuselage@npm:0.48.0" dependencies: "@rocket.chat/css-in-js": ^0.31.25 "@rocket.chat/css-supports": ^0.31.25 @@ -8792,7 +8792,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: d812e4ed52b53372268b4f5d396fb8d6aeb3f46a4f7e276642edf287ca2038f88e2a6d8bc0264f95fee1772aadc849419f7695e35cc6a278111efcbc18e1472d + checksum: d82121b6e6d0fa6ea204d8cbd2cc32460c4ca3585c05a1a1d908250c9150ed52edfd0acc75520cb29c6bd81a8671dc7d91f427913bcc4364a14b4eacd4bd2bf4 languageName: node linkType: hard @@ -8803,7 +8803,7 @@ __metadata: "@babel/core": ~7.22.20 "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.47.1 + "@rocket.chat/fuselage": ^0.48.0 "@rocket.chat/fuselage-tokens": ~0.32.0 "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/styled": ~0.31.25 @@ -9158,7 +9158,7 @@ __metadata: "@rocket.chat/favicon": "workspace:^" "@rocket.chat/forked-matrix-appservice-bridge": ^4.0.2 "@rocket.chat/forked-matrix-bot-sdk": ^0.6.0-beta.3 - "@rocket.chat/fuselage": ^0.47.1 + "@rocket.chat/fuselage": ^0.48.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/fuselage-toastbar": ~0.31.25 @@ -10037,7 +10037,7 @@ __metadata: resolution: "@rocket.chat/ui-avatar@workspace:packages/ui-avatar" dependencies: "@babel/core": ~7.22.20 - "@rocket.chat/fuselage": ^0.47.1 + "@rocket.chat/fuselage": ^0.48.0 "@rocket.chat/ui-contexts": "workspace:^" "@types/babel__core": ~7.20.3 "@types/react": ~17.0.69 @@ -10063,7 +10063,7 @@ __metadata: "@babel/core": ~7.22.20 "@react-aria/toolbar": ^3.0.0-beta.1 "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.47.1 + "@rocket.chat/fuselage": ^0.48.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/mock-providers": "workspace:^" @@ -10116,7 +10116,7 @@ __metadata: "@babel/core": ~7.22.20 "@react-aria/toolbar": ^3.0.0-beta.1 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.47.1 + "@rocket.chat/fuselage": ^0.48.0 "@rocket.chat/icons": ^0.33.0 "@storybook/addon-actions": ~6.5.16 "@storybook/addon-docs": ~6.5.16 @@ -10208,7 +10208,7 @@ __metadata: resolution: "@rocket.chat/ui-theming@workspace:ee/packages/ui-theming" dependencies: "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.47.1 + "@rocket.chat/fuselage": ^0.48.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/ui-contexts": "workspace:~" @@ -10251,7 +10251,7 @@ __metadata: "@rocket.chat/css-in-js": ~0.31.25 "@rocket.chat/emitter": ~0.31.25 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.47.1 + "@rocket.chat/fuselage": ^0.48.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/styled": ~0.31.25 @@ -10296,7 +10296,7 @@ __metadata: "@codemirror/tooltip": ^0.19.16 "@lezer/highlight": ^1.1.6 "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.47.1 + "@rocket.chat/fuselage": ^0.48.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/fuselage-toastbar": ^0.31.25 From 59bac17148a039dde1e74be86ea398d46ec7fde9 Mon Sep 17 00:00:00 2001 From: "lingohub[bot]" <69908207+lingohub[bot]@users.noreply.github.com> Date: Wed, 28 Feb 2024 21:58:23 -0500 Subject: [PATCH 073/207] =?UTF-8?q?i18n:=20Rocket.Chat=20language=20update?= =?UTF-8?q?=20from=20LingoHub=20=F0=9F=A4=96=20on=202024-02-28Z=20(#31856)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/i18n/src/locales/af.i18n.json | 11 ++ packages/i18n/src/locales/ar.i18n.json | 25 ++- packages/i18n/src/locales/az.i18n.json | 11 ++ packages/i18n/src/locales/be-BY.i18n.json | 11 ++ packages/i18n/src/locales/bg.i18n.json | 12 ++ packages/i18n/src/locales/bs.i18n.json | 12 ++ packages/i18n/src/locales/ca.i18n.json | 10 + packages/i18n/src/locales/cs.i18n.json | 25 ++- packages/i18n/src/locales/cy.i18n.json | 12 ++ packages/i18n/src/locales/da.i18n.json | 21 +++ packages/i18n/src/locales/de-AT.i18n.json | 11 ++ packages/i18n/src/locales/de.i18n.json | 6 + packages/i18n/src/locales/el.i18n.json | 9 + packages/i18n/src/locales/eo.i18n.json | 11 ++ packages/i18n/src/locales/es.i18n.json | 25 ++- packages/i18n/src/locales/et.i18n.json | 1 + packages/i18n/src/locales/eu.i18n.json | 2 + packages/i18n/src/locales/fa.i18n.json | 10 + packages/i18n/src/locales/fi.i18n.json | 6 + packages/i18n/src/locales/fr.i18n.json | 13 +- packages/i18n/src/locales/gl.i18n.json | 8 +- packages/i18n/src/locales/he.i18n.json | 14 ++ packages/i18n/src/locales/hi-IN.i18n.json | 217 ++++++++++++++++++++++ packages/i18n/src/locales/hi.i18n.json | 2 + packages/i18n/src/locales/hr.i18n.json | 9 + packages/i18n/src/locales/hu.i18n.json | 12 ++ packages/i18n/src/locales/id.i18n.json | 10 + packages/i18n/src/locales/it.i18n.json | 25 ++- packages/i18n/src/locales/ja.i18n.json | 6 + packages/i18n/src/locales/ka-GE.i18n.json | 15 ++ packages/i18n/src/locales/km.i18n.json | 13 ++ packages/i18n/src/locales/ko.i18n.json | 17 ++ packages/i18n/src/locales/ku.i18n.json | 10 + packages/i18n/src/locales/lo.i18n.json | 10 + packages/i18n/src/locales/lt.i18n.json | 11 ++ packages/i18n/src/locales/lv.i18n.json | 11 ++ packages/i18n/src/locales/mn.i18n.json | 11 ++ packages/i18n/src/locales/ms-MY.i18n.json | 10 + packages/i18n/src/locales/nl.i18n.json | 7 + packages/i18n/src/locales/no.i18n.json | 24 +++ packages/i18n/src/locales/pl.i18n.json | 19 +- packages/i18n/src/locales/pt-BR.i18n.json | 17 +- packages/i18n/src/locales/pt.i18n.json | 11 ++ packages/i18n/src/locales/ro.i18n.json | 10 + packages/i18n/src/locales/ru.i18n.json | 31 ++-- packages/i18n/src/locales/sk-SK.i18n.json | 12 ++ packages/i18n/src/locales/sl-SI.i18n.json | 12 ++ packages/i18n/src/locales/sq.i18n.json | 10 + packages/i18n/src/locales/sr.i18n.json | 13 ++ packages/i18n/src/locales/sv.i18n.json | 6 + packages/i18n/src/locales/ta-IN.i18n.json | 9 + packages/i18n/src/locales/th-TH.i18n.json | 12 ++ packages/i18n/src/locales/tr.i18n.json | 14 ++ packages/i18n/src/locales/ug.i18n.json | 6 + packages/i18n/src/locales/uk.i18n.json | 15 +- packages/i18n/src/locales/vi-VN.i18n.json | 13 ++ packages/i18n/src/locales/zh-HK.i18n.json | 12 ++ packages/i18n/src/locales/zh-TW.i18n.json | 7 + packages/i18n/src/locales/zh.i18n.json | 16 ++ 59 files changed, 875 insertions(+), 56 deletions(-) create mode 100644 packages/i18n/src/locales/hi-IN.i18n.json diff --git a/packages/i18n/src/locales/af.i18n.json b/packages/i18n/src/locales/af.i18n.json index 386ab9826ece..05926a54689a 100644 --- a/packages/i18n/src/locales/af.i18n.json +++ b/packages/i18n/src/locales/af.i18n.json @@ -320,6 +320,7 @@ "Apply": "aansoek doen", "Apply_and_refresh_all_clients": "Dien en verfris alle kliënte", "Apps": "programme", + "Apps_context_premium": "onderneming", "Apps_Settings": "Program se instellings", "Apps_WhatIsIt": "Programme: Wat is hulle?", "Apps_WhatIsIt_paragraph1": "'N Nuwe ikoon in die administrasie area! Wat beteken dit en wat is Apps?", @@ -1099,6 +1100,7 @@ "FEDERATION_Domain": "domein", "FEDERATION_Status": "status", "Retry_Count": "Probeer weer", + "Federation_Matrix_enabled": "enabled", "Field": "veld", "Field_removed": "Veld verwyder", "Field_required": "Veld benodig", @@ -1458,6 +1460,10 @@ "Layout_Terms_of_Service": "Diensbepalings", "LDAP": "LDAP", "LDAP_Description": "LDAP is 'n hiërargiese databasis wat baie maatskappye gebruik om 'n enkele teken aan te bied - 'n fasiliteit om een ​​wagwoord tussen verskeie webwerwe en dienste te deel. Vir gevorderde konfigurasie inligting en voorbeelde, raadpleeg asseblief ons wiki: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "enkripsie", + "LDAP_DataSync_BackgroundSync": "Agtergrondsinkronisering", + "LDAP_Server_Type": "Servertipe", + "LDAP_Server_Type_Other": "ander", "LDAP_Authentication": "in staat te stel", "LDAP_Authentication_Password": "wagwoord", "LDAP_Authentication_UserDN": "Gebruiker DN", @@ -2488,6 +2494,7 @@ "Unarchive": "Onargiveer", "unarchive-room": "Unarchive Room", "unarchive-room_description": "Toestemming om kanale te unarchive", + "unauthorized": "Nie gemagtig nie", "Unblock_User": "Ontblok gebruiker", "Unignore": "negeer lys verwyder", "Uninstall": "verwyder", @@ -2664,6 +2671,8 @@ "Visitor_Navigation": "Besoeker Navigasie", "Visitor_page_URL": "URL van besoekersbladsy", "Visitor_time_on_site": "Besoekers tyd op die webwerf", + "VoIP_Management_Server_Username": "Gebruikersnaam", + "VoIP_Management_Server_Password": "wagwoord", "Wait_activation_warning": "Voordat u kan inteken, moet u rekening handmatig deur 'n administrateur geaktiveer word.", "Warnings": "waarskuwings", "We_are_offline_Sorry_for_the_inconvenience": "Ons is vanlyn. Jammer vir die ongerief.", @@ -2738,6 +2747,7 @@ "Your_push_was_sent_to_s_devices": "Jou druk is gestuur na%s toestelle", "Your_server_link": "Jou bediener skakel", "Your_workspace_is_ready": "Jou werkruimte is gereed om 🎉 te gebruik", + "registration.page.login.errors.wrongCredentials": "Gebruiker nie gevind nie of verkeerde wagwoord", "registration.page.registration.waitActivationWarning": "Voordat u kan inteken, moet u rekening handmatig deur 'n administrateur geaktiveer word.", "registration.page.login.forgot": "Het jy jou wagwoord vergeet", "registration.page.resetPassword.sent": "As hierdie e-pos geregistreer is, stuur ons instruksies oor hoe om u wagwoord terug te stel. As jy nie binnekort 'n e-pos ontvang nie, kom asseblief terug en probeer weer.", @@ -2747,6 +2757,7 @@ "registration.component.form.emailOrUsername": "E-pos of gebruikersnaam", "registration.component.form.username": "Gebruikersnaam", "registration.component.form.name": "naam", + "registration.component.form.userAlreadyExist": "Gebruikersnaam bestaan ​​reeds. Probeer asseblief 'n ander gebruikersnaam.", "registration.component.form.emailAlreadyExists": "E-pos bestaan ​​reeds", "registration.component.form.usernameAlreadyExists": "Gebruikersnaam bestaan ​​reeds. Probeer asseblief 'n ander gebruikersnaam.", "registration.component.form.invalidEmail": "Die ingevoerde e-pos is ongeldig", diff --git a/packages/i18n/src/locales/ar.i18n.json b/packages/i18n/src/locales/ar.i18n.json index 5fd02c33c1fe..214a1940a388 100644 --- a/packages/i18n/src/locales/ar.i18n.json +++ b/packages/i18n/src/locales/ar.i18n.json @@ -409,6 +409,7 @@ "API_Shield_Types_Description": "أنواع الدروع المحددة للتمكين كقائمة مفصولة بفواصل، اختر من بين \"على الإنترنت\" أو \"قناة\" أو \"*\" للكل", "Apps_Framework_Development_Mode": "تمكين وضع التطوير", "API_Shield_user_require_auth": "تلزم المصادقة لدروع المستخدمين", + "Calls_in_queue_many": "{{count}} من المكالمات الانتظار", "API_Token": "الرمز المميز لواجهة برمجة التطبيقات", "Apps_Framework_Development_Mode_Description": "يسمح وضع التطوير بتثبيت التطبيقات غير الواردة من سوق Rocket.Chat.", "API_Tokenpass_URL": "عنوان URL لخادم Tokenpass", @@ -441,6 +442,7 @@ "App_support_url": "عنوان url للدعم", "App_Url_to_Install_From": "تثبيت من عنوان URL", "App_Url_to_Install_From_File": "تثبيت من ملف", + "Calls_in_queue_few": "{{count}} من المكالمات الانتظار", "App_user_not_allowed_to_login": "لا يُسمح لمستخدمي التطبيق بتسجيل الدخول مباشرة.", "Appearance": "مظهر", "Application_added": "تمت إضافة التطبيق", @@ -451,6 +453,7 @@ "Apply_and_refresh_all_clients": "التطبيق وتحديث جميع العملاء", "Apps": "التطبيقات", "Apps_context_installed": "تم التثبيت", + "Apps_context_premium": "مؤسسة", "Apps_Engine_Version": "إصدار محرك التطبيقات", "Apps_Essential_Alert": "هذا التطبيق ضروري للأحداث التالية:", "Apps_Essential_Disclaimer": "سيتم تعطيل الأحداث المذكورة أعلاه إذا تم تعطيل هذا التطبيق. إذا كنت تريد أن يعمل Rocket.Chat بدون وظيفة هذا التطبيق، فسيلزمك إلغاء تثبيته", @@ -487,7 +490,6 @@ "Apps_License_Message_appId": "لم يتم إصدار الترخيص لهذا التطبيق", "Apps_License_Message_bundle": "ترخيص صادر لحزمة لا تحتوي على التطبيق", "Apps_License_Message_expire": "لم يعد الترخيص صالحًا ويحتاج إلى تجديد", - "Calls_in_queue_many": "{{count}} من المكالمات الانتظار", "Apps_License_Message_maxSeats": "لا يستوعب الترخيص العدد الحالي من المستخدمين النشطين. تُرجى زيادة عدد المقاعد", "Apps_License_Message_publicKey": "حدث خطأ أثناء محاولة فك تشفير الترخيص. تُرجى مزامنة مساحة العمل الخاصة بك في خدمات الاتصال وإعادة المحاولة.", "Apps_License_Message_renewal": "انتهت صلاحية الترخيص ويلزم تجديده", @@ -527,7 +529,6 @@ "Apps_Permissions_livechat-message_read": "الوصول إلى معلومات رسالة Livechat ", "Apps_Permissions_livechat-message_write": "تعديل معلومات رسالة Livechat ", "Apps_Permissions_livechat-room_read": "الوصول إلى معلومات غرفة Livechat", - "Calls_in_queue_few": "{{count}} من المكالمات الانتظار", "Apps_Permissions_livechat-room_write": "تعديل معلومات غرفة Livechat", "Apps_Permissions_livechat-department_read": "الوصول إلى معلومات قسم Livechat", "Apps_Permissions_livechat-department_multiple": "الوصول إلى معلومات أقسام Livechat المتعددة", @@ -1375,6 +1376,7 @@ "Desktop_Notifications_Not_Enabled": "إشعارات سطح المكتب غير ممكّنة", "Details": "التفاصيل", "line": "السطر", + "Device_Management_IP": " عنوان IP", "Different_Style_For_User_Mentions": "نمط مختلف لإشارات المستخدم", "Livechat_Facebook_API_Key": "مفتاح واجهة برمجة تطبيقات Channel متعددة الاتجاهات", "Direct": "مباشر", @@ -1627,6 +1629,7 @@ "Enter_Normal": "الوضع العادي (إرسال باستخدام إنتر)", "Enter_to": "إدخال إلى", "Enter_your_E2E_password": "أدخل كلمة مرور التشفير بين الطرفيات", + "message_counter_many": "{{count}} رسائل", "Entertainment": "الترفيه", "Error": "خطأ", "Error_something_went_wrong": "أُووبس! هناك خطأ ما. يرجى إعادة تحميل الصفحة أو الاتصال بالمسؤول.", @@ -1674,6 +1677,7 @@ "error-forwarding-department-target-not-allowed": "غير مسموح بإعادة التوجيه إلى القسم المستهدف.", "error-guests-cant-have-other-roles": "لا يمكن أن يكون للمستخدمين الضيوف أي دور آخر.", "error-import-file-extract-error": "فشل استخراج ملف الاستيراد.", + "meteor_status_reconnect_in_many": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "error-import-file-is-empty": "يبدو أن الملف المستورد فارغ.", "error-import-file-missing": "لم يتم العثور على الملف المراد استيراده على المسار المحدد.", "error-importer-not-defined": "لم يتم تحديد المستورِد بطريقة صحيحة، ففئة الاستيراد غير محددة به.", @@ -1769,6 +1773,7 @@ "error-this-is-not-a-livechat-room": "هذه ليست غرفة قناة متعددة الاتجاهات", "error-token-already-exists": "سبق وجود رمز مميز بهذا الاسم", "error-token-does-not-exists": "لا يوجد رمز مميز", + "message_counter_few": "{{count}} رسائل", "error-too-many-requests": "خطأ، تلقينا الكثير من الطلبات. من فضلك أبطئ الوتيرة، يلزمك الانتظار مدة {{seconds}} من الثواني قبل المحاولة مرة أخرى.", "error-transcript-already-requested": "سبق طلب نسخة مكتوبة", "error-unpinning-message": "تعذر إلغاء تثبيت الرسالة", @@ -1821,6 +1826,7 @@ "Extension_Number": "رقم الامتداد", "Extension_Status": "حالة الامتداد", "External": "خارجي", + "meteor_status_reconnect_in_few": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "External_Domains": "النطاقات الخارجية", "External_Queue_Service_URL": "عنوان URL لخدمة قائمة الانتظار الخارجية", "External_Service": "الخدمة الخارجية", @@ -1966,7 +1972,6 @@ "Finish": "إنهاء", "Finish_Registration": "إنهاء التسجيل", "First_Channel_After_Login": "أول Channel بعد تسجيل الدخول", - "message_counter_many": "{{count}} رسائل", "First_response_time": "وقت للاستجابة الأولى", "Flags": "أعلام", "Follow_message": "رسالة المتابعة", @@ -2026,7 +2031,6 @@ "get-password-policy-mustContainAtLeastOneSpecialCharacter": "يجب أن تحتوي كلمة المرور تحتوي على رمز خاص واحد على الأقل", "get-password-policy-mustContainAtLeastOneUppercase": "يجب أن تحتوي كلمة المرور على حرف كبير واحد على الأقل", "get-server-info": "الحصول على معلومات الخادم", - "meteor_status_reconnect_in_many": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "github_no_public_email": "ليس لديك أي بريد إلكتروني عام في حسابك على Github", "github_HEAD": "العنوان", "Give_a_unique_name_for_the_custom_oauth": "امنح اسمًا فريدًا مخصصًا لـ oauth", @@ -2122,7 +2126,6 @@ "Ignored": "تم التجاهل", "Images": "الصور", "IMAP_intercepter_already_running": "أداة اعتراض IMAP قيد التشغيل الآن", - "message_counter_few": "{{count}} رسائل", "IMAP_intercepter_Not_running": "أداة اعتراض IMAP لا تعمل", "Impersonate_next_agent_from_queue": "انتحال شخصية الوكيل التالي من قائمة الانتظار", "Impersonate_user": "انتحال شخصية المستخدم", @@ -2195,7 +2198,6 @@ "Install": "تثبيت", "Install_Extension": "تثبيت الامتداد", "Install_FxOs": "تثبيت Rocket.Chat على Firefox لديك", - "meteor_status_reconnect_in_few": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "Install_FxOs_done": "رائع! يمكنك الآن استخدام Rocket.Chat عبر الأيقونة التي على الشاشة الرئيسية لديك. انعم بالمرح مع Rocket.Chat!", "Install_FxOs_error": "عذرًا، هذا لم يعمل على النحو المنشود! ظهر الخطأ التالي:", "Install_FxOs_follow_instructions": "يُرجى التأكد من تثبيت التطبيق على جهازك (اضغط على \"تثبيت\" عند التوجيه بذلك).", @@ -2381,7 +2383,9 @@ "Language_Hungarian": "الهنغارية", "Language_Italian": "الإيطالية", "Language_Japanese": "اليابانية", + "message_counter_two": "{{count}} رسائل", "Language_Latvian": "اللاتفية", + "message_counter_zero": "{{count}} رسائل", "Language_Lithuanian": "اللتوانية", "Language_Not_set": "غير محددة", "Language_Polish": "البولندية", @@ -2453,7 +2457,9 @@ "LDAP_Avatar_Field": "حقل الصورة الرمزية للمستخدم", "You_have_to_set_an_API_token_first_in_order_to_use_the_integration": "يجب عليك أولاً تعيين الرمز المميز لواجهة برمجة التطبيقات من أجل استخدام التكامل.", "LDAP_Avatar_Field_Description": "الحقل الذي سيتم استخدامه كـ *صورة رمزية* للمستخدمين. اتركه فارغًا لاستخدام \"thumbnailPhoto\" أولاً و\"jpegPhoto\" كبديل.", + "meteor_status_reconnect_in_two": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "LDAP_Background_Sync": "مزامنة الخلفية", + "meteor_status_reconnect_in_zero": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "LDAP_Background_Sync_Avatars": "مزامنة خلفية الصورة الرمزية", "LDAP_Background_Sync_Avatars_Description": "تمكين إجراء خلفية منفصلة لمزامنة الصور الرمزية للمستخدم.", "LDAP_Background_Sync_Avatars_Interval": "الفاصل الزمني لمزامنة خلفية الصورة الرمزية", @@ -2856,8 +2862,6 @@ "Message_Code_highlight_Description": "قائمة اللغات المفصولة بفواصل (كل اللغات المدعومة على [highlight.js](https://github.com/highlightjs/highlight.js/tree/11.6.0#supported-languages)) التي سيتم استخدامها لتمييز الكتل البرمجية للتعليمات البرمجية", "message_counter_one": "{{count}} رسالة", "message_counter_other": "{{count}} رسائل", - "message_counter_two": "{{count}} رسائل", - "message_counter_zero": "{{count}} رسائل", "Message_DateFormat": "تنسيق التاريخ", "Message_DateFormat_Description": "انظر أيضًا: [Moment.js](http://momentjs.com/docs/#/displaying/format/)", "Message_deleting_blocked": "لا يمكن حذف هذه الرسالة بعد الآن", @@ -2952,8 +2956,6 @@ "meteor_status_offline": "وضع عدم الاتصال.", "meteor_status_reconnect_in_one": "المحاولة مرة أخرى خلال ثانية واحدة...", "meteor_status_reconnect_in_other": "المحاولة مرة أخرى خلال {{count}} من الثواني...", - "meteor_status_reconnect_in_two": "المحاولة مرة أخرى خلال {{count}} من الثواني...", - "meteor_status_reconnect_in_zero": "المحاولة مرة أخرى خلال {{count}} من الثواني...", "meteor_status_try_now_offline": "الاتصال مرة أخرى", "meteor_status_try_now_waiting": "المحاولة الآن", "meteor_status_waiting": "في انتظار اتصال الخادم،", @@ -3354,6 +3356,7 @@ "Privacy": "الخصوصية", "Privacy_Policy": "سياسة الخصوصية", "Private": "خاص", + "Private_channels": "Channels خاصة", "Private_Channel": "Channel خاصة", "Private_Channels": "Channels خاصة", "Private_Chats": "دردشات خاصة", @@ -3594,6 +3597,7 @@ "Unsafe_Url": "عنوان URL غير آمن", "Rocket_Chat_Alert": "تنبيه Rocket.Chat", "Role": "الدور", + "Roles": "الأدوار", "Role_Editing": "تحرير الدور", "Role_Mapping": "تعيين الدور", "Role_removed": "تمت إزالة الدور", @@ -4801,6 +4805,7 @@ "registration.component.form.emailOrUsername": "البريد الإلكتروني أو اسم المستخدم", "registration.component.form.username": "اسم المستخدم", "registration.component.form.name": "الاسم", + "registration.component.form.userAlreadyExist": "سبق وجود اسم المستخدم. تُرجى تجربة اسم مستخدم آخر.", "registration.component.form.emailAlreadyExists": "سبق وجود البريد الإلكتروني", "registration.component.form.usernameAlreadyExists": "سبق وجود اسم المستخدم. تُرجى تجربة اسم مستخدم آخر.", "registration.component.form.invalidEmail": "البريد الإلكتروني الذي تم إدخاله غير صالح", diff --git a/packages/i18n/src/locales/az.i18n.json b/packages/i18n/src/locales/az.i18n.json index a4410953ab14..513bd976f2f6 100644 --- a/packages/i18n/src/locales/az.i18n.json +++ b/packages/i18n/src/locales/az.i18n.json @@ -320,6 +320,7 @@ "Apply": "Tətbiq edin", "Apply_and_refresh_all_clients": "Bütün müştərilərə müraciət edin və yeniləyin", "Apps": "Proqramlar", + "Apps_context_premium": "Müəssisə", "Apps_Settings": "App Ayarları", "Apps_WhatIsIt": "Proqramlar: Onlar nədir?", "Apps_WhatIsIt_paragraph1": "İdarəetmə sahəsində yeni bir simge! Bu nə deməkdir və nədir?", @@ -1099,6 +1100,7 @@ "FEDERATION_Domain": "Domain", "FEDERATION_Status": "Status", "Retry_Count": "Yenidən cəhd edin", + "Federation_Matrix_enabled": "Etkin", "Field": "Alan", "Field_removed": "Alan çıxarıldı", "Field_required": "Alan tələb olunur", @@ -1458,6 +1460,10 @@ "Layout_Terms_of_Service": "Xidmət Şərtləri", "LDAP": "LDAP", "LDAP_Description": "LDAP, bir çox şirkət və birdən çox sayt arasında bir parol paylaşma imkanı olan bir çox şirkətin vahid işarəsi təmin etmək üçün istifadə etdiyi hiyerarşik bir verilənlər bazasıdır. Ətraflı konfiqurasiya məlumatları və nümunələr üçün, bizim wiki müraciət edin: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "Şifrələmə", + "LDAP_DataSync_BackgroundSync": "Background Sync", + "LDAP_Server_Type": "Server Tipi", + "LDAP_Server_Type_Other": "Digər", "LDAP_Authentication": "Enable", "LDAP_Authentication_Password": "Şifrə", "LDAP_Authentication_UserDN": "İstifadəçi DN", @@ -2488,6 +2494,7 @@ "Unarchive": "Arxivsiz", "unarchive-room": "Arxivsiz Otaq", "unarchive-room_description": "Arxivsiz kanallara icazə", + "unauthorized": "Səlahiyyətli deyil", "Unblock_User": "İstifadəçini blokdan çıxarın", "Unignore": "Unignore", "Uninstall": "Silin", @@ -2664,6 +2671,8 @@ "Visitor_Navigation": "Ziyaretçi Naviqasiyası", "Visitor_page_URL": "Ziyaretçi səhifəsinin URL", "Visitor_time_on_site": "Saytda baxış zamanı", + "VoIP_Management_Server_Username": "İstifadəçi adı", + "VoIP_Management_Server_Password": "Şifrə", "Wait_activation_warning": "Giriş yapabilmeniz üçün hesabınız bir administrator tərəfindən əl ilə aktivləşdirilməlidir.", "Warnings": "Uyarılar", "We_are_offline_Sorry_for_the_inconvenience": "Biz offline. Narahatçılığa görə üzr istəyirik.", @@ -2738,6 +2747,7 @@ "Your_push_was_sent_to_s_devices": "Sizin itəniz%s cihazlarına göndərildi", "Your_server_link": "Sizin server bağlantınız", "Your_workspace_is_ready": "İş yeriniz 🎉 istifadə etməyə hazırdır", + "registration.page.login.errors.wrongCredentials": "İstifadəçi tapılmadı ya da yanlış parol", "registration.page.registration.waitActivationWarning": "Giriş yapabilmeniz üçün hesabınız bir administrator tərəfindən əl ilə aktivləşdirilməlidir.", "registration.page.login.forgot": "Şifrəni unutmusan", "registration.page.resetPassword.sent": "Bu e-poçt qeydə alındıqda, parolunuzu necə sıfırlayacağına dair təlimatlar göndərəcəyik. Qısa müddətdə bir e-poçt almazsan, geri qayıdın və yenidən cəhd edin.", @@ -2747,6 +2757,7 @@ "registration.component.form.emailOrUsername": "E-poçt və ya istifadəçi adı", "registration.component.form.username": "İstifadəçi adı", "registration.component.form.name": "Adı", + "registration.component.form.userAlreadyExist": "İstifadəçi adı artıq mövcuddur. Başqa bir istifadəçi adı cəhd edin.", "registration.component.form.emailAlreadyExists": "Elektron poçt ünvanı artıq mövcuddur", "registration.component.form.usernameAlreadyExists": "İstifadəçi adı artıq mövcuddur. Başqa bir istifadəçi adı cəhd edin.", "registration.component.form.invalidEmail": "Girilən e-poçt etibarsızdır", diff --git a/packages/i18n/src/locales/be-BY.i18n.json b/packages/i18n/src/locales/be-BY.i18n.json index 7814ebf6efa5..5c9fb33cc0d8 100644 --- a/packages/i18n/src/locales/be-BY.i18n.json +++ b/packages/i18n/src/locales/be-BY.i18n.json @@ -333,6 +333,7 @@ "Apply": "Прымяніць", "Apply_and_refresh_all_clients": "Прымяніць і абнавіць ўсіх кліентаў", "Apps": "прыкладання", + "Apps_context_premium": "прадпрыемства", "Apps_Settings": "налады прыкладання", "Apps_WhatIsIt": "Праграмы: Якія яны?", "Apps_WhatIsIt_paragraph1": "Новы значок у галіне кіравання! Што гэта значыць і якія прыкладанні?", @@ -1115,6 +1116,7 @@ "FEDERATION_Domain": "дамен", "FEDERATION_Status": "статус", "Retry_Count": "Retry Count", + "Federation_Matrix_enabled": "Уключана", "Field": "поле", "Field_removed": "поле выдаленае", "Field_required": "абавязковыя для запаўнення палі", @@ -1474,6 +1476,10 @@ "Layout_Terms_of_Service": "Ўмовы абслугоўвання", "LDAP": "LDAP", "LDAP_Description": "LDAP ўяўляе сабой іерархічную базу дадзеных, што многія кампаніі выкарыстоўваюць для забеспячэння адзінага ўваходу - аб'ект для абмену адзін пароля паміж некалькімі сайтамі і паслугамі. Для папярэдняй інфармацыі аб канфігурацыі і прыклады, калі ласка, звярніцеся ў наш вікі: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "шыфраванне", + "LDAP_DataSync_BackgroundSync": "фонавая сінхранізацыя", + "LDAP_Server_Type": "тып сервера", + "LDAP_Server_Type_Other": "іншае", "LDAP_Authentication": "Ўключыць", "LDAP_Authentication_Password": "пароль", "LDAP_Authentication_UserDN": "DN карыстальніка", @@ -2506,6 +2512,7 @@ "Unarchive": "разархіваваць", "unarchive-room": "разархіваваць нумар", "unarchive-room_description": "Дазвол на разархіваваць каналы", + "unauthorized": "забаронена", "Unblock_User": "разблакаваць карыстальніка", "Unignore": "ігнараваць", "Uninstall": "выдаліць", @@ -2682,6 +2689,8 @@ "Visitor_Navigation": "наведвальнік навігацыі", "Visitor_page_URL": "старонка наведвальнікаў URL", "Visitor_time_on_site": "Час для наведвальнікаў на сайце", + "VoIP_Management_Server_Username": "імя карыстальніка", + "VoIP_Management_Server_Password": "пароль", "Wait_activation_warning": "Перад тым, як увайсці, ваш уліковы запіс павінна быць актываваная ўручную адміністратарам.", "Warnings": "папярэджання", "We_are_offline_Sorry_for_the_inconvenience": "Мы не ў сеткі. Прабачце за дастаўленыя нязручнасці.", @@ -2756,6 +2765,7 @@ "Your_push_was_sent_to_s_devices": "Ваш штуршок быў адпраўлены ў%s прылад", "Your_server_link": "Ваша спасылка сервера", "Your_workspace_is_ready": "Ваша працоўная вобласць гатова да выкарыстання 🎉", + "registration.page.login.errors.wrongCredentials": "Карыстальнік не знойдзены або няправільны пароль", "registration.page.registration.waitActivationWarning": "Перад тым, як увайсці, ваш уліковы запіс павінна быць актываваная ўручную адміністратарам.", "registration.page.login.forgot": "Забыліся свой пароль", "registration.page.resetPassword.sent": "Калі гэты ліст зарэгістравана, мы адправім інструкцыі па аднаўленні пароля. Калі вы не атрымаеце па электроннай пошце ў бліжэйшы час, калі ласка, вярніцеся і паспрабуйце ізноў.", @@ -2765,6 +2775,7 @@ "registration.component.form.emailOrUsername": "Email або імя карыстальніка", "registration.component.form.username": "імя карыстальніка", "registration.component.form.name": "імя", + "registration.component.form.userAlreadyExist": "Імя карыстальніка ўжо існуе. Калі ласка, паспрабуйце іншую назву.", "registration.component.form.emailAlreadyExists": "E-mail ўжо існуе", "registration.component.form.usernameAlreadyExists": "Імя карыстальніка ўжо існуе. Калі ласка, паспрабуйце іншую назву.", "registration.component.form.invalidEmail": "Адрас электроннай пошты ня дзейнічае", diff --git a/packages/i18n/src/locales/bg.i18n.json b/packages/i18n/src/locales/bg.i18n.json index 162895dfe48a..171f8c32ce4f 100644 --- a/packages/i18n/src/locales/bg.i18n.json +++ b/packages/i18n/src/locales/bg.i18n.json @@ -320,6 +320,7 @@ "Apply": "Приложи", "Apply_and_refresh_all_clients": "Приложете и опреснете всички клиенти", "Apps": "Apps", + "Apps_context_premium": "начинание", "Apps_Settings": "Настройките на приложението", "Apps_WhatIsIt": "Приложения: Какви са те?", "Apps_WhatIsIt_paragraph1": "Нова икона в административната област! Какво означава това и какви са приложенията?", @@ -1099,6 +1100,7 @@ "FEDERATION_Domain": "Домейн", "FEDERATION_Status": "Статус", "Retry_Count": "Повторен опит", + "Federation_Matrix_enabled": "Enabled", "Field": "Поле", "Field_removed": "Полето е премахнато", "Field_required": "Полето е задължително", @@ -1455,6 +1457,10 @@ "Layout_Sidenav_Footer_Dark_description": "Размерът на долния колонтитул е 260 x 70px", "Layout_Terms_of_Service": "Условия за ползване", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "Encryption", + "LDAP_DataSync_BackgroundSync": "Синхронизиране на фона", + "LDAP_Server_Type": "Тип сървър", + "LDAP_Server_Type_Other": "Друг", "LDAP_Authentication": "Активиране", "LDAP_Authentication_Password": "Парола", "LDAP_Authentication_UserDN": "Потребителски DN", @@ -1984,6 +1990,7 @@ "Reactions": "реакции", "Read_by": "Прочетете от", "Read_only": "Само за четене", + "This_room_is_read_only": "Тази стая е само за четене", "Read_only_changed_successfully": "Четенето се промени само успешно", "Read_only_channel": "Само канал за четене", "Read_only_group": "Групи само за четене", @@ -2484,6 +2491,7 @@ "Unarchive": "Разархивирайте", "unarchive-room": "Разделна стая", "unarchive-room_description": "Разрешение за премахване на архивите", + "unauthorized": "Неоторизиран", "Unblock_User": "Деблокиране на потребителя", "Unignore": "Отмени пренебрегването", "Uninstall": "Uninstall", @@ -2659,6 +2667,8 @@ "Visitor_Navigation": "Навигационна навигация", "Visitor_page_URL": "URL адрес на страницата на посетителя", "Visitor_time_on_site": "Време за посещение на място", + "VoIP_Management_Server_Username": "Потребителско име", + "VoIP_Management_Server_Password": "Парола", "Wait_activation_warning": "Преди да можете да влезете, вашият акаунт трябва да бъде активиран ръчно от администратор.", "Warnings": "Предопреждения", "We_are_offline_Sorry_for_the_inconvenience": "Ние сме офлайн. Съжалявам за неудобството.", @@ -2732,6 +2742,7 @@ "Your_push_was_sent_to_s_devices": "Натискането ви бе изпратено на %s устройства", "Your_server_link": "Вашата сървърна връзка", "Your_workspace_is_ready": "Работното ви пространство е готово за използване 🎉", + "registration.page.login.errors.wrongCredentials": "Потребителят не е намерен или неправилна парола", "registration.page.registration.waitActivationWarning": "Преди да можете да влезете, вашият акаунт трябва да бъде активиран ръчно от администратор.", "registration.page.resetPassword.sent": "Ако този имейл е регистриран, ще изпратим инструкции как да зададете нова парола. Ако скоро не получите имейл, моля, върнете се и опитайте отново.", "registration.component.login": "Влез", @@ -2739,6 +2750,7 @@ "registration.component.resetPassword": "Нулиране на паролата", "registration.component.form.username": "Потребителско име", "registration.component.form.name": "Име", + "registration.component.form.userAlreadyExist": "Потребителското име вече съществува. Моля, опитайте с друго потребителско име.", "registration.component.form.emailAlreadyExists": "Електроната поща вече съсществува", "registration.component.form.usernameAlreadyExists": "Потребителското име вече съществува. Моля, опитайте с друго потребителско име.", "registration.component.form.invalidEmail": "Въведеният имейл адрес е невалиден", diff --git a/packages/i18n/src/locales/bs.i18n.json b/packages/i18n/src/locales/bs.i18n.json index 6c8f9528afa1..4fe15d1e24ae 100644 --- a/packages/i18n/src/locales/bs.i18n.json +++ b/packages/i18n/src/locales/bs.i18n.json @@ -320,6 +320,7 @@ "Apply": "primijeniti", "Apply_and_refresh_all_clients": "Primjeni i osvježi sve klijente", "Apps": "Apps", + "Apps_context_premium": "Poduzeće", "Apps_Settings": "Postavke aplikacije", "Apps_WhatIsIt": "Aplikacije: Koje su to?", "Apps_WhatIsIt_paragraph1": "Nova ikona na području administracije! Što to znači i koje su aplikacije?", @@ -1094,6 +1095,7 @@ "FEDERATION_Domain": "Domena", "FEDERATION_Status": "Status", "Retry_Count": "Ponovite račun", + "Federation_Matrix_enabled": "Omogućeno", "Field": "Polje", "Field_removed": "Polje je uklonjeno", "Field_required": "Polje je obavezno", @@ -1452,6 +1454,10 @@ "Layout_Sidenav_Footer_Dark_description": "Veličina podnožja je 260 x 70px", "Layout_Terms_of_Service": "Uvjeti pružanja usluge", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "Šifriranje", + "LDAP_DataSync_BackgroundSync": "Sinkronizacija pozadine", + "LDAP_Server_Type": "Vrsta poslužitelja", + "LDAP_Server_Type_Other": "Ostalo", "LDAP_Authentication": "Omogućiti", "LDAP_Authentication_Password": "Lozinka", "LDAP_Authentication_UserDN": "Korisnički DN", @@ -1980,6 +1986,7 @@ "Reactions": "Reakcije", "Read_by": "Pročitajte", "Read_only": "Samo za čitanje", + "This_room_is_read_only": "Ova soba je samo za čitanje", "Read_only_changed_successfully": "Postavka Samo za čitanje uspješno promjenjena", "Read_only_channel": "Samo za čitanje Soba", "Read_only_group": "Samo za čitanje Grupa", @@ -2480,6 +2487,7 @@ "Unarchive": "Dearhiviraj", "unarchive-room": "Soba za arhiviranje", "unarchive-room_description": "Dopuštenje za uklanjanje kanala", + "unauthorized": "Niste ovlašteni", "Unblock_User": "Odblokiraj Korisnika", "Unignore": "Ponovno prihvati", "Uninstall": "Uninstall", @@ -2655,6 +2663,8 @@ "Visitor_Navigation": "Navigacija Posjetitelja", "Visitor_page_URL": "URL stranice posjetitelja", "Visitor_time_on_site": "Vrijeme posjetitelja na stranici", + "VoIP_Management_Server_Username": "Korisničko ime", + "VoIP_Management_Server_Password": "Lozinka", "Wait_activation_warning": "Prije nego što se prijavite, vaš račun mora ručno aktivirati administrator.", "Warnings": "Upozorenja", "We_are_offline_Sorry_for_the_inconvenience": "Mi smo izvan mreže. Oprostite na neugodnosti.", @@ -2729,6 +2739,7 @@ "Your_push_was_sent_to_s_devices": "Push obavijest je poslana %s uređaje", "Your_server_link": "Veza poslužitelja", "Your_workspace_is_ready": "Radni je prostor spreman za upotrebu 🎉", + "registration.page.login.errors.wrongCredentials": "Korisnik nije pronađen ili pogrešna lozinka", "registration.page.registration.waitActivationWarning": "Prije nego što se prijavite, vaš račun mora ručno aktivirati administrator.", "registration.page.resetPassword.sent": "Ako je ova e-poruka registrirana, poslat ćemo vam upute o tome kako poništiti zaporku. Ako uskoro ne primite e-poruku, vratite se i pokušajte ponovo.", "registration.component.login": "Prijava", @@ -2736,6 +2747,7 @@ "registration.component.resetPassword": "Resetiraj lozinku", "registration.component.form.username": "Korisničko ime", "registration.component.form.name": "Ime", + "registration.component.form.userAlreadyExist": "Korisničko ime već postoji. Pokušajte s drugim korisničkim imenom.", "registration.component.form.emailAlreadyExists": "Email već postoji", "registration.component.form.usernameAlreadyExists": "Korisničko ime već postoji. Pokušajte s drugim korisničkim imenom.", "registration.component.form.invalidEmail": "Uneseni e-mail nije valjan", diff --git a/packages/i18n/src/locales/ca.i18n.json b/packages/i18n/src/locales/ca.i18n.json index 376d37de9175..56b5713d155d 100644 --- a/packages/i18n/src/locales/ca.i18n.json +++ b/packages/i18n/src/locales/ca.i18n.json @@ -451,6 +451,7 @@ "Apply_and_refresh_all_clients": "Aplicar i refrescar tots els clients", "Apps": "Aplicacions", "Apps_context_installed": "Instal·lat", + "Apps_context_premium": "Empresa", "Apps_Engine_Version": "Versió del Motor d'Aplicacions", "Apps_Essential_Alert": "Aquesta aplicació és essencial per als següents esdeveniments:", "Apps_Essential_Disclaimer": "Els esdeveniments enumerats dalt seran interromputs si aquesta aplicació està desactivada. Si vols que Rocket.Chat funcioni sense la funcionalitat d'aquesta aplicació, l'has de desinstal·lar", @@ -1368,6 +1369,7 @@ "Desktop_Notifications_Not_Enabled": "Les notificacions d'escriptori no estan habilitades", "Details": "Detalls", "line": "línia", + "Device_Management_IP": "IP", "Different_Style_For_User_Mentions": "Estil diferent per a les mencions de lusuari", "Livechat_Facebook_API_Key": "Clave API de LiveChat ", "Livechat_Facebook_API_Secret": "API Secret LiveChat ", @@ -1852,6 +1854,7 @@ "FEDERATION_Test_Setup_Error": "No s'ha pogut trobar el vostre servidor usant la vostra configuració, reviseu-ne la configuració.", "FEDERATION_Test_Setup_Success": "La configuració de la seva federació està funcionant i altres servidors poden trobar-se!", "Retry_Count": "Comptador de reintents", + "Federation_Matrix_enabled": "Activa", "Field": "Camp", "Field_removed": "Camp eliminat", "Field_required": "Camp obligatori", @@ -2226,6 +2229,7 @@ "InternalHubot": "Hubot intern", "InternalHubot_EnableForChannels": "Activa per als canals públics", "InternalHubot_EnableForDirectMessages": "Activa per als missatges directes", + "InternalHubot_EnableForPrivateGroups": "Activa per a canals privats", "InternalHubot_PathToLoadCustomScripts": "Carpeta des d'on carregar els scripts", "InternalHubot_reload": "Recarrega els scripts", "InternalHubot_ScriptsToLoad": "Seqüències d'ordres (scripts) per carregar", @@ -3301,6 +3305,7 @@ "Privacy": "Privacitat", "Privacy_Policy": "Política de privacitat", "Private": "Privat", + "Private_channels": "Channels Privats", "Private_Channel": "Canal privat", "Private_Channels": "Channels Privats", "Private_Chats": "Xats privats", @@ -3376,6 +3381,7 @@ "Reactions": "Reaccions", "Read_by": "Llegit per", "Read_only": "Només lectura", + "This_room_is_read_only": "Aquesta sala és de només lectura", "Read_only_changed_successfully": "Només lectura canviat correctament", "Read_only_channel": "Channel Només lectura", "Read_only_group": "Grup de només lectura", @@ -3526,6 +3532,7 @@ "Unsafe_Url": "URL insegura", "Rocket_Chat_Alert": "Alerta Rocket.Chat", "Role": "Rol", + "Roles": "Rols", "Role_Editing": "Edició de rols", "Role_Mapping": "Mapeig de rols", "Role_removed": "Rol eliminat", @@ -4532,6 +4539,8 @@ "Visitor_Navigation": "Navegació del visitant", "Visitor_page_URL": "URL de la pàgina del visitant", "Visitor_time_on_site": "Temps de visita", + "VoIP_Management_Server_Username": "Nom d'usuari", + "VoIP_Management_Server_Password": "Contrasenya", "Chat_opened_by_visitor": "Xat obert pel visitant.", "Wait_activation_warning": "Abans de poder iniciar sessió, els comptes han de ser activats manualment per un administrador.", "Waiting_queue": "Cua d’espera", @@ -4670,6 +4679,7 @@ "registration.component.resetPassword": "Reinicialitza la contrasenya", "registration.component.form.username": "Nom d'usuari", "registration.component.form.name": "Nom", + "registration.component.form.userAlreadyExist": "Nom d'usuari ja existeix. Proveu amb un altre nom d'usuari.", "registration.component.form.emailAlreadyExists": "L'adreça de correu electrònic ja existeix", "registration.component.form.usernameAlreadyExists": "Nom d'usuari ja existeix. Proveu amb un altre nom d'usuari.", "registration.component.form.invalidEmail": "L'adreça de correu-e és invàlida", diff --git a/packages/i18n/src/locales/cs.i18n.json b/packages/i18n/src/locales/cs.i18n.json index bc23ea19b98f..32a127396509 100644 --- a/packages/i18n/src/locales/cs.i18n.json +++ b/packages/i18n/src/locales/cs.i18n.json @@ -414,6 +414,7 @@ "Apply_and_refresh_all_clients": "Použít a aktualizovat všechny klienty", "Apps": "Aplikace", "Apps_context_installed": "Instalované", + "Apps_context_premium": "Korporace", "Apps_Engine_Version": "Verze Engine aplikace", "Apps_Essential_Alert": "Tato aplikace je důležité pro následující události:", "Apps_Essential_Disclaimer": "Výše uvedené události budou ovlivněny odebráním této aplikace. Pokud ji i přesto chcete odebrat odinstalujte ji.", @@ -751,6 +752,7 @@ "Confirm_password": "Potvrďte heslo", "Confirm_your_password": "Potvrďte heslo", "Connect": "Připojit", + "Connected": "Připojeno", "Connection_Closed": "Připojení bylo uzavřeno", "Connection_Reset": "Obnovit připojení", "Connectivity_Services": "Služby připojení", @@ -1371,6 +1373,7 @@ "Enter_Normal": "Normální mód (odeslání po stisku klávesy enter)", "Enter_to": "Enter", "Enter_your_E2E_password": "Zadejte heslo E2E", + "message_counter_many": "{{count}} zpráv(y)", "Entertainment": "Zábava", "Error": "Chyba", "Error_404": "Chyba: 404 nenalezeno", @@ -1412,6 +1415,7 @@ "error-forwarding-department-target-not-allowed": "Přesměrování do tohoto oddělení není povoleno", "error-guests-cant-have-other-roles": "Hosté nemůžou mít žádné další role.", "error-import-file-extract-error": "Nepodařilo se rozbalit importovaný soubor.", + "meteor_status_reconnect_in_many": "zkusím znovu za {{count}} sekund...", "error-import-file-is-empty": "Importovaný soubor se zdá být prázdný.", "error-import-file-missing": "Importovaný soubor nebyl na zadané cestě nalezen.", "error-importer-not-defined": "Nástroj pro import špatně nadefinován, chybí třída Importu.", @@ -1493,6 +1497,7 @@ "error-this-is-not-a-livechat-room": "Toto není Omnichannel místnost", "error-token-already-exists": "Token s tímto názvem již existuje", "error-token-does-not-exists": "Token neexistuje", + "message_counter_few": "{{count}} zpráv(y)", "error-too-many-requests": "Chyba, příliš mnoho požadavků. Prosím zpomalte. Vyčkejte {{seconds}} sekund před dalším pokusem.", "error-transcript-already-requested": "Přepis již byl vyžádán", "error-unpinning-message": "Zprávu nelze odepnout", @@ -1536,6 +1541,7 @@ "Export_My_Data": "Exportovat moje data (JSON)", "expression": "Výraz", "Extended": "Rozšířené", + "meteor_status_reconnect_in_few": "zkusím znovu za {{count}} sekund...", "External_Domains": "Externí domény", "External_Queue_Service_URL": "URL Externí služby front", "External_Service": "Externí služba", @@ -1571,6 +1577,7 @@ "FEDERATION_Test_Setup_Error": "Pomocí vašeho nastavení se nepodařilo najít váš server. Zkontrolujte prosím nastavení.", "FEDERATION_Test_Setup_Success": "Vaše nastavení Federace funguje a ostatní servery vás mohou najít!", "Retry_Count": "Počet opakování", + "Federation_Matrix_enabled": "Povoleno", "Field": "Pole", "Field_removed": "Pole odebráno", "Field_required": "Pole vyžadováno", @@ -1655,7 +1662,6 @@ "Filters": "Filtry", "Financial_Services": "Finanční služby", "First_Channel_After_Login": "Výchozí místnost po přihlášení", - "message_counter_many": "{{count}} zpráv(y)", "First_response_time": "První doba odezvy", "Flags": "Vlajky", "Follow_message": "Sledovat zprávu", @@ -1710,7 +1716,6 @@ "get-password-policy-mustContainAtLeastOneNumber": "Heslo musí obsahovat alespoň jednu číslici", "get-password-policy-mustContainAtLeastOneSpecialCharacter": "Heslo musí obsahovat alespoň jeden speciální znak", "get-password-policy-mustContainAtLeastOneUppercase": "Heslo musí obsahovat alespoň jedno velké písmeno", - "meteor_status_reconnect_in_many": "zkusím znovu za {{count}} sekund...", "github_no_public_email": "Ve svém účtu GitHub nemáte žádný e-mail jako veřejný", "Give_a_unique_name_for_the_custom_oauth": "Zadejte jedinečný název pro vlastní OAuth", "strike": "přeškrtnuté", @@ -1797,7 +1802,6 @@ "Ignored": "Ignorováno", "Images": "Obrázky", "IMAP_intercepter_already_running": "Zachytávání IMAP již běží", - "message_counter_few": "{{count}} zpráv(y)", "IMAP_intercepter_Not_running": "Zachytávání IMAP neběží", "Impersonate_next_agent_from_queue": "Vydávat se za dalšího operátora ve frontě", "Impersonate_user": "Vydávat se za uživatele", @@ -1866,7 +1870,6 @@ "Install": "Instalace", "Install_Extension": "Nainstalovat rozšíření", "Install_FxOs": "Nainstalovat Rocket.Chat do Vašeho Firefoxu", - "meteor_status_reconnect_in_few": "zkusím znovu za {{count}} sekund...", "Install_FxOs_done": "Super! Nyní můžete používat Rocket.Chat přes ikonu na úvodní obrazovce. Užijte si Rocket.Chat!", "Install_FxOs_error": "Jejda, tohle se stát nemělo! Objevila se následující chyba:", "Install_FxOs_follow_instructions": "Prosím potvrďte instalaci aplikace na Vašem přístroji (stiskněte tlačítko \"Install\" po výzvě).", @@ -2059,7 +2062,15 @@ "Layout_Sidenav_Footer_Dark_description": "Velikost zápatí je 260 x 70 pixelů", "Layout_Terms_of_Service": "Podmínky služby", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "Šifrování", "LDAP_Connection_successful": "LDAP připojení úspěšné", + "LDAP_Connection_Timeouts": "Časové limity", + "LDAP_UserSearch": "Hledat uživatele", + "LDAP_DataSync_DataMap": "Mapování", + "LDAP_DataSync_Advanced": "Pokročilá synchronizace", + "LDAP_DataSync_BackgroundSync": "Synchronizace na pozadí", + "LDAP_Server_Type": "Typ serveru", + "LDAP_Server_Type_Other": "Jiné", "LDAP_Advanced_Sync": "Pokročilá synchronizace", "LDAP_Authentication": "Povolit", "LDAP_Authentication_Password": "Heslo", @@ -2801,6 +2812,7 @@ "Privacy": "Soukromí", "Privacy_Policy": "Zásady ochrany osobních údajů", "Private": "Privátní", + "Private_channels": "Soukromé místnosti", "Private_Channel": "Privátní místnost", "Private_Channels": "Soukromé místnosti", "Private_Chats": "Soukromé konverzace", @@ -2870,6 +2882,7 @@ "Reactions": "Reakce", "Read_by": "Přečteno", "Read_only": "Pouze pro čtení", + "This_room_is_read_only": "Tato místnost je pouze pro čtení", "Read_only_changed_successfully": "Pouze pro čtení změněno", "Read_only_channel": "Místnost pouze pro čtení", "Read_only_group": "Skupina pouze pro čtení", @@ -2994,6 +3007,7 @@ "Robot_Instructions_File_Content": "Obsah souboru robots.txt", "Rocket_Chat_Alert": "Upozornění Rocket.Chat", "Role": "Role", + "Roles": "Role", "Role_Editing": "Editace Role", "Role_Mapping": "Mapování rolí", "Role_removed": "Role odstraněna", @@ -3830,6 +3844,8 @@ "Visitor_Navigation": "Navigace návštěvníka", "Visitor_page_URL": "URL Stránky pro návštěvníky", "Visitor_time_on_site": "Doba návštěvníka na stránce", + "VoIP_Management_Server_Username": "Uživatelské jméno", + "VoIP_Management_Server_Password": "Heslo", "Wait_activation_warning": "Předtím, než se můžete přihlásit, Váš účet musí být ručně aktivován správcem.", "Waiting_queue": "Čekací fronta", "Waiting_queue_message": "Zpráva v čekací frontě", @@ -3951,6 +3967,7 @@ "registration.component.resetPassword": "Obnovit heslo", "registration.component.form.username": "Uživatelské jméno", "registration.component.form.name": "Jméno", + "registration.component.form.userAlreadyExist": "Uživatelské jméno již existuje. Použijte prosím jiné.", "registration.component.form.emailAlreadyExists": "Email již existuje", "registration.component.form.usernameAlreadyExists": "Uživatelské jméno již existuje. Použijte prosím jiné.", "registration.component.form.invalidEmail": "Zadaný e-mail je neplatný", diff --git a/packages/i18n/src/locales/cy.i18n.json b/packages/i18n/src/locales/cy.i18n.json index 8d2b105ee23f..38cb42a46eb3 100644 --- a/packages/i18n/src/locales/cy.i18n.json +++ b/packages/i18n/src/locales/cy.i18n.json @@ -320,6 +320,7 @@ "Apply": "Gwneud cais", "Apply_and_refresh_all_clients": "Gwneud cais a adnewyddu pob cleient", "Apps": "Apps", + "Apps_context_premium": "Menter", "Apps_Settings": "Gosodiadau'r App", "Apps_WhatIsIt": "Apps: Beth Ydyn nhw?", "Apps_WhatIsIt_paragraph1": "Eicon newydd yn yr ardal weinyddol! Beth mae hyn yn ei olygu a beth yw Apps?", @@ -1095,6 +1096,7 @@ "FEDERATION_Domain": "Parth", "FEDERATION_Status": "Statws", "Retry_Count": "Ailadroddwch y Cyfrif", + "Federation_Matrix_enabled": "Wedi'i alluogi", "Field": "Maes", "Field_removed": "Cae wedi'i dynnu", "Field_required": "Maes gofynnol", @@ -1453,6 +1455,10 @@ "Layout_Sidenav_Footer_Dark_description": "Maint footer yw 260 x 70px", "Layout_Terms_of_Service": "Telerau'r Gwasanaeth", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "Amgryptio", + "LDAP_DataSync_BackgroundSync": "Sync Cefndir", + "LDAP_Server_Type": "Math o Weinyddwr", + "LDAP_Server_Type_Other": "Arall", "LDAP_Authentication": "Galluogi", "LDAP_Authentication_Password": "Cyfrinair", "LDAP_Authentication_UserDN": "DN Defnyddiwr", @@ -1982,6 +1988,7 @@ "Reactions": "Ymatebion", "Read_by": "Darllenwch gan", "Read_only": "Darllen yn unig", + "This_room_is_read_only": "Mae'r ystafell hon yn cael ei ddarllen yn unig", "Read_only_changed_successfully": "Dim ond yn llwyddiannus y mae darllen yn newid", "Read_only_channel": "Sianel Darllen yn Unig", "Read_only_group": "Grŵp Darllen yn Unig", @@ -2482,6 +2489,7 @@ "Unarchive": "Annisgwyl", "unarchive-room": "Ystafell Ddieithr", "unarchive-room_description": "Caniatâd i sianeli anarchifol", + "unauthorized": "Heb ei awdurdodi", "Unblock_User": "Dadlwytho Defnyddiwr", "Unignore": "Unignore", "Uninstall": "Dadstystio", @@ -2657,6 +2665,8 @@ "Visitor_Navigation": "Ymwelwyr Ymwelwyr", "Visitor_page_URL": "URL ymwelwyr tudalen", "Visitor_time_on_site": "Amser ymwelwyr ar y safle", + "VoIP_Management_Server_Username": "Enw Defnyddiwr", + "VoIP_Management_Server_Password": "Cyfrinair", "Wait_activation_warning": "Cyn i chi allu mewngofnodi, rhaid i'ch gweinydd gael ei weithredu gan weinyddwr.", "Warnings": "Rhybuddion", "We_are_offline_Sorry_for_the_inconvenience": "Rydym ni allan. Mae'n ddrwg gennym am yr anghyfleustra.", @@ -2731,6 +2741,7 @@ "Your_push_was_sent_to_s_devices": "Anfonwyd eich gwthio i ddyfeisiau %s", "Your_server_link": "Dolen eich gweinydd", "Your_workspace_is_ready": "Mae'ch gweithle yn barod i ddefnyddio 🎉", + "registration.page.login.errors.wrongCredentials": "Ni chanfuwyd defnyddiwr na chyfrinair anghywir", "registration.page.registration.waitActivationWarning": "Cyn i chi allu mewngofnodi, rhaid i'ch gweinydd gael ei weithredu gan weinyddwr.", "registration.page.resetPassword.sent": "Os yw'r e-bost hwn wedi'i gofrestru, byddwn yn anfon cyfarwyddiadau ar sut i ailosod eich cyfrinair. Os na chewch e-bost yn fuan, dewch yn ôl a cheisiwch eto.", "registration.component.login": "Mewngofnodi", @@ -2738,6 +2749,7 @@ "registration.component.resetPassword": "Ailosod cyfrinair", "registration.component.form.username": "Enw Defnyddiwr", "registration.component.form.name": "Enw", + "registration.component.form.userAlreadyExist": "Mae enw defnyddiwr eisoes yn bodoli. Rhowch gynnig ar enw defnyddiwr arall.", "registration.component.form.emailAlreadyExists": "Ebost eisoes yn bodoli", "registration.component.form.usernameAlreadyExists": "Mae enw defnyddiwr eisoes yn bodoli. Rhowch gynnig ar enw defnyddiwr arall.", "registration.component.form.invalidEmail": "Mae'r e-bost a roddwyd yn annilys", diff --git a/packages/i18n/src/locales/da.i18n.json b/packages/i18n/src/locales/da.i18n.json index 58cec2856880..748e6e3f1fc1 100644 --- a/packages/i18n/src/locales/da.i18n.json +++ b/packages/i18n/src/locales/da.i18n.json @@ -481,6 +481,7 @@ "Apply_and_refresh_all_clients": "Anvend og opdatér alle klienter", "Apps": "Apps", "Apps_context_installed": "Installeret", + "Apps_context_premium": "Firma", "Apps_Engine_Version": "Apps Engine Version", "Apps_Essential_Alert": "Denne app er vigtig for følgende begivenheder:", "Apps_Essential_Disclaimer": "Begivenheder anført ovenfor vil blive afbrudt hvis denne app bliver deaktiveret. Hvis du vil have Rocket.Chat til at fungere uden denne apps funktionalitet, skal du afinstallere det", @@ -832,6 +833,7 @@ "Confirm_password": "Bekræft dit kodeord", "Confirm_your_password": "Bekræft dit kodeord", "Connect": "Forbind", + "Connected": "Forbundet", "Connection_Closed": "Forbindelse lukket", "Connection_Reset": "Nulstilning af forbindelse", "Connectivity_Services": "Connectivity Services", @@ -1664,6 +1666,7 @@ "FEDERATION_Test_Setup_Error": "Kunne ikke finde din server ved hjælp af din opsætning. Gennemgå venligst dine indstillinger.", "FEDERATION_Test_Setup_Success": "Din føderationsopsætning fungerer og andre servere kan finde dig!", "Retry_Count": "Prøv igen", + "Federation_Matrix_enabled": "Aktiveret", "Field": "Felt", "Field_removed": "Felt fjernet", "Field_required": "Felt påkrævet", @@ -2151,7 +2154,15 @@ "Layout_Sidenav_Footer_Dark_description": "Footer-størrelse er 260 x 70px", "Layout_Terms_of_Service": "Servicevilkår", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "Kryptering", "LDAP_Connection_successful": "LDAP-forbindelse lykkedes", + "LDAP_Connection_Timeouts": "Timeout", + "LDAP_UserSearch": "Brugersøgning", + "LDAP_DataSync_DataMap": "Mapping", + "LDAP_DataSync_Advanced": "Avanceret synkronisering", + "LDAP_DataSync_BackgroundSync": "Baggrundssynkronisering", + "LDAP_Server_Type": "Server Type", + "LDAP_Server_Type_Other": "Andet", "LDAP_Advanced_Sync": "Avanceret synkronisering", "LDAP_Authentication": "Aktivér", "LDAP_Authentication_Password": "Adgangskode", @@ -2898,6 +2909,7 @@ "Privacy": "Privatliv", "Privacy_Policy": "Fortrolighedspolitik", "Private": "Privat", + "Private_channels": "Private kanaler", "Private_Channel": "Privat kanal", "Private_Channels": "Private kanaler", "Private_Chats": "Private chats", @@ -2967,6 +2979,7 @@ "Reactions": "Reaktioner", "Read_by": "Læs af", "Read_only": "Læs kun", + "This_room_is_read_only": "Dette rum er skrivebeskyttet", "Read_only_changed_successfully": "Læsning er kun ændret", "Read_only_channel": "Læs kun kanal", "Read_only_group": "Læs kun gruppe", @@ -3094,6 +3107,7 @@ "Robot_Instructions_File_Content": "Indhold af robots.txt-fil", "Rocket_Chat_Alert": "Rocket.Chat Alert", "Role": "Rolle", + "Roles": "Roller", "Role_Editing": "Rollredigering", "Role_Mapping": "Rolletilknytning", "Role_removed": "Rolle fjernet", @@ -3703,6 +3717,7 @@ "Unarchive": "Annuller arkivering", "unarchive-room": "Unarchive Room", "unarchive-room_description": "Tilladelse til at ophæve kanaler", + "unauthorized": "Ikke godkendt", "Unavailable": "Utilgængelig", "Unblock_User": "Fjern blokering af bruger", "Uncheck_All": "Fjern alle markeringer", @@ -3938,6 +3953,8 @@ "Visitor_Navigation": "Visitor Navigation", "Visitor_page_URL": "URL for besøgende side", "Visitor_time_on_site": "Besøgende tid på stedet", + "VoIP_Management_Server_Username": "Brugernavn", + "VoIP_Management_Server_Password": "Adgangskode", "Wait_activation_warning": "Før du kan logge ind, skal din konto manuelt aktiveres af en administrator.", "Waiting_queue": "Ventende kø", "Waiting_queue_message": "Meddelelse for ventende kø", @@ -4050,6 +4067,7 @@ "Your_server_link": "Din server link", "Your_temporary_password_is_password": "Din midlertidige adgangskode er [password].", "Your_workspace_is_ready": "Dit arbejdsområde er klar til brug 🎉", + "registration.page.login.errors.wrongCredentials": "Bruger ikke fundet eller forkert adgangskode", "registration.page.login.errors.loginBlockedForIp": "Login er blevet midlertidigt blokeret til denne IP", "registration.page.login.errors.loginBlockedForUser": "Login er blevet midlertidigt blokeret for denne bruger", "registration.page.login.errors.AppUserNotAllowedToLogin": "App-brugere må ikke logge direkte på.", @@ -4061,6 +4079,7 @@ "registration.component.form.username": "Brugernavn", "registration.component.form.name": "Navn", "registration.component.form.createAnAccount": "Opret en konto", + "registration.component.form.userAlreadyExist": "Brugernavnet eksisterer allerede. Prøv venligst et andet brugernavn.", "registration.component.form.emailAlreadyExists": "E-mailen eksisterer allerede", "registration.component.form.usernameAlreadyExists": "Brugernavnet eksisterer allerede. Prøv venligst et andet brugernavn.", "registration.component.form.invalidEmail": "Den indtastede e-mail er ugyldig", @@ -4074,7 +4093,9 @@ "registration.component.form.sendConfirmationEmail": "Send bekræftelses-email", "onboarding.component.form.action.pasteHere": "Indsæt her ...", "onboarding.form.registerOfflineForm.title": "Registrer offline", + "Create_an_account": "Opret en konto", "New_custom_status": "Ny brugerdefineret status", + "RegisterWorkspace_Features_ThirdPartyLogin_Title": "Login fra tredjepart", "Enterprise": "Firma", "UpgradeToGetMore_engagement-dashboard_Title": "Analyse", "UpgradeToGetMore_auditing_Title": "Meddelelsesovervågning" diff --git a/packages/i18n/src/locales/de-AT.i18n.json b/packages/i18n/src/locales/de-AT.i18n.json index 8981a77afade..b4073551357c 100644 --- a/packages/i18n/src/locales/de-AT.i18n.json +++ b/packages/i18n/src/locales/de-AT.i18n.json @@ -320,6 +320,7 @@ "Apply": "Sich bewerben", "Apply_and_refresh_all_clients": "Alle Clients anwenden und aktualisieren", "Apps": "Apps", + "Apps_context_premium": "Unternehmen", "Apps_Settings": "App-Einstellungen", "Apps_WhatIsIt": "Apps: Was sind sie?", "Apps_WhatIsIt_paragraph1": "Ein neues Icon im Administrationsbereich! Was bedeutet das und was sind Apps?", @@ -1102,6 +1103,7 @@ "FEDERATION_Domain": "Domain", "FEDERATION_Status": "Status", "Retry_Count": "Wiederholungsanzahl", + "Federation_Matrix_enabled": "Aktiviert", "Field": "Feld", "Field_removed": "Feld entfernt", "Field_required": "Feld erforderlich", @@ -1459,6 +1461,10 @@ "Layout_Sidenav_Footer_Dark_description": "Die Größe der Fußzeile beträgt 260 x 70 Pixel.", "Layout_Terms_of_Service": "Nutzungsbedingungen", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "Verschlüsselung", + "LDAP_DataSync_BackgroundSync": "Hintergrundsynchronisierung", + "LDAP_Server_Type": "Server Typ", + "LDAP_Server_Type_Other": "Andere", "LDAP_Authentication": "Aktivieren", "LDAP_Authentication_Password": "Passwort", "LDAP_Authentication_UserDN": "Benutzer-DN", @@ -1990,6 +1996,7 @@ "Reactions": "Reaktionen", "Read_by": "Gelesen von", "Read_only": "Schreibgeschützt", + "This_room_is_read_only": "Dieser Raum ist nur lesbar", "Read_only_changed_successfully": "Nur Lesen erfolgreich geändert", "Read_only_channel": "Schreibgeschützter Kanal", "Read_only_group": "Schreibgeschützte Gruppe", @@ -2666,6 +2673,8 @@ "Visitor_Navigation": "Besuchernavigation", "Visitor_page_URL": "URL der Besucherseite", "Visitor_time_on_site": "Besucherzeit auf der Seite", + "VoIP_Management_Server_Username": "Nutzername", + "VoIP_Management_Server_Password": "Passwort", "Wait_activation_warning": "Bevor Sie sich anmelden können, muss das Konto von einem Administrator manuell aktiviert werden.", "Warnings": "Warnungen", "We_are_offline_Sorry_for_the_inconvenience": "Wir sind offline. Entschuldigen Sie die Unannehmlichkeiten.", @@ -2747,6 +2756,8 @@ "registration.component.login.userNotFound": "Der/die Benutzer/in konnte nicht gefunden werden.", "registration.component.resetPassword": "Passwort zurücksetzen", "registration.component.form.username": "Nutzername", + "registration.component.form.name": "Name", + "registration.component.form.userAlreadyExist": "Benutzername existiert bereits. Bitte versuchen Sie es mit einem anderen Benutzernamen.", "registration.component.form.emailAlreadyExists": "Die E-Mail-Adresse existiert bereits.", "registration.component.form.usernameAlreadyExists": "Benutzername existiert bereits. Bitte versuchen Sie es mit einem anderen Benutzernamen.", "registration.component.form.invalidEmail": "Die eingegebene E-Mail-Adresse ist ungültig.", diff --git a/packages/i18n/src/locales/de.i18n.json b/packages/i18n/src/locales/de.i18n.json index a21fd248ecfb..2ce24e2aa0e4 100644 --- a/packages/i18n/src/locales/de.i18n.json +++ b/packages/i18n/src/locales/de.i18n.json @@ -498,6 +498,7 @@ "Apps_context_explore": "Erkunden", "Apps_context_installed": "Installiert", "Apps_context_requested": "Angefordert", + "Apps_context_premium": "Unternehmen", "Apps_Engine_Version": "Version der Anwendungs-Engine", "Apps_Essential_Alert": "Diese App ist für die folgenden Ereignisse unerlässlich:", "Apps_Essential_Disclaimer": "Die oben aufgeführten Ereignisse werden gestört, wenn diese App deaktiviert wird. Wenn Sie Rocket.Chat ohne die Funktionalität dieser App ausführen möchten, müssen Sie sie deinstallieren", @@ -3763,6 +3764,7 @@ "Privacy_summary": "Zusammenfassung zum Datenschutz", "Private": "Privat", "private": "privat", + "Private_channels": "Private Kanäle", "Private_Channel": "Privater Channel", "Private_Channels": "Private Kanäle", "Private_Chats": "Private Chats", @@ -5380,6 +5382,7 @@ "Your_workspace_is_ready": "Ihr Arbeitsbereich ist einsatzbereit 🎉", "Zapier": "Zapier", "registration.page.login.errors.wrongCredentials": "Entweder konnte der Benutzer nicht gefunden werden oder Sie haben ein falsches Passwort angegeben.", + "registration.page.login.errors.invalidEmail": "Ungültige E-Mail", "registration.page.login.errors.loginBlockedForIp": "Die Anmeldung wurde für diese IP vorübergehend gesperrt", "registration.page.login.errors.loginBlockedForUser": "Die Anmeldung wurde für diesen Benutzer vorübergehend gesperrt", "registration.page.login.errors.licenseUserLimitReached": "Die maximale Anzahl Benutzer wurde erreicht.", @@ -5400,6 +5403,8 @@ "registration.component.form.emailOrUsername": "E-Mail-Adresse oder Nutzername", "registration.component.form.username": "Benutzername", "registration.component.form.name": "Name", + "registration.component.form.createAnAccount": "Ein Konto erstellen", + "registration.component.form.userAlreadyExist": "Benutzername existiert bereits. Bitte versuchen Sie es mit einem anderen Benutzernamen.", "registration.component.form.emailAlreadyExists": "Die E-Mail-Adresse existiert bereits.", "registration.component.form.usernameAlreadyExists": "Benutzername existiert bereits. Bitte versuchen Sie es mit einem anderen Benutzernamen.", "registration.component.form.invalidEmail": "Die eingegebene E-Mail-Adresse ist ungültig.", @@ -5461,6 +5466,7 @@ "onboarding.form.adminInfoForm.fields.password.placeholder": "Passwort erstellen", "onboarding.form.adminInfoForm.fields.keepPosted.label": "Halten Sie mich über Rocket.Chat-Updates auf dem Laufenden", "onboarding.form.awaitConfirmationForm.title": "Warten auf Bestätigung", + "onboarding.form.awaitConfirmationForm.content.securityCode": "Sicherheitscode", "onboarding.form.organizationInfoForm.title": "Organisations-Info", "onboarding.form.organizationInfoForm.subtitle": "Bitte, haben Sie Verständnis. Diese Informationen helfen uns, Ihren Arbeitsbereich zu personalisieren.", "onboarding.form.organizationInfoForm.fields.organizationName.label": "Name der Organisation", diff --git a/packages/i18n/src/locales/el.i18n.json b/packages/i18n/src/locales/el.i18n.json index e9ffacd76117..0c52b2df3259 100644 --- a/packages/i18n/src/locales/el.i18n.json +++ b/packages/i18n/src/locales/el.i18n.json @@ -326,6 +326,7 @@ "Apply": "Εφαρμόζεται", "Apply_and_refresh_all_clients": "Εφαρμόστε και ανανεώσετε όλους τους πελάτες", "Apps": "Εφαρμογές", + "Apps_context_premium": "Επιχείρηση", "Apps_Settings": "Ρυθμίσεις της εφαρμογής", "Apps_WhatIsIt": "Εφαρμογές: Τι είναι αυτοί;", "Apps_WhatIsIt_paragraph1": "Ένα νέο εικονίδιο στην περιοχή διαχείρισης! Τι σημαίνει αυτό και ποιες είναι οι εφαρμογές;", @@ -1106,6 +1107,7 @@ "FEDERATION_Domain": "Τομέας", "FEDERATION_Status": "Κατάσταση", "Retry_Count": "Επανάληψη μέτρησης", + "Federation_Matrix_enabled": "Ενεργοποιήθηκε", "Field": "Πεδίο", "Field_removed": "Το πεδίο αφαιρεθεί", "Field_required": "Απαιτείται πεδίο", @@ -1465,6 +1467,10 @@ "Layout_Terms_of_Service": "Όροι χρήσης", "LDAP": "LDAP", "LDAP_Description": "LDAP είναι μια ιεραρχική βάση δεδομένων που πολλές εταιρείες χρησιμοποιούν για να παρέχουν Single Sign On - μια εγκατάσταση για την κοινή χρήση ενός κωδικού πρόσβασης από πολλαπλές τοποθεσίες και υπηρεσίες. Για προχωρημένους πληροφορίες διαμόρφωσης και παραδείγματα, συμβουλευτείτε wiki μας: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "κρυπτογράφηση", + "LDAP_DataSync_BackgroundSync": "Συγχρονισμός φόντου", + "LDAP_Server_Type": "Τύπος διακομιστή", + "LDAP_Server_Type_Other": "Άλλα", "LDAP_Authentication": "Ενεργοποίηση", "LDAP_Authentication_Password": "Σύνθημα", "LDAP_Authentication_UserDN": "DN χρήστη", @@ -2672,6 +2678,8 @@ "Visitor_Navigation": "επισκέπτης πλοήγησης", "Visitor_page_URL": "Διεύθυνση URL της σελίδας επισκέπτης", "Visitor_time_on_site": "επισκέπτη φορά στην ιστοσελίδα", + "VoIP_Management_Server_Username": "Όνομα Χρήστη", + "VoIP_Management_Server_Password": "Κωδικός", "Wait_activation_warning": "Για να μπορέσετε να συνδεθείτε, ο λογαριασμός σας θα πρέπει να ενεργοποιηθεί χειροκίνητα από κάποιον διαχειριστή.", "Warnings": "Προειδοποιήσεις", "We_are_offline_Sorry_for_the_inconvenience": "Είμαστε εκτός σύνδεσης. Συγγνώμη για την ταλαιπωρία.", @@ -2756,6 +2764,7 @@ "registration.component.form.emailOrUsername": "Email ή όνομα χρήστη", "registration.component.form.username": "Όνομα Χρήστη", "registration.component.form.name": "Όνομα", + "registration.component.form.userAlreadyExist": "Το όνομα χρήστη υπάρχει ήδη. Δοκιμάστε ένα άλλο όνομα χρήστη.", "registration.component.form.emailAlreadyExists": "Το email υπάρχει ήδη", "registration.component.form.usernameAlreadyExists": "Το όνομα χρήστη υπάρχει ήδη. Δοκιμάστε ένα άλλο όνομα χρήστη.", "registration.component.form.invalidEmail": "Το e-mail που δώσατε δεν είναι έγκυρο", diff --git a/packages/i18n/src/locales/eo.i18n.json b/packages/i18n/src/locales/eo.i18n.json index 69cc5595f560..f20d82eb9682 100644 --- a/packages/i18n/src/locales/eo.i18n.json +++ b/packages/i18n/src/locales/eo.i18n.json @@ -320,6 +320,7 @@ "Apply": "Apliki", "Apply_and_refresh_all_clients": "Apliki kaj refreŝigi ĉiujn klientojn", "Apps": "Apps", + "Apps_context_premium": "Entrepreno", "Apps_Settings": "Agordoj de la App", "Apps_WhatIsIt": "Aplikaĵoj: Kio Estas Ili?", "Apps_WhatIsIt_paragraph1": "Nova ikono en la administra areo! Kion tio signifas kaj kio estas Apps?", @@ -1099,6 +1100,7 @@ "FEDERATION_Domain": "Domajno", "FEDERATION_Status": "Statuso", "Retry_Count": "Retry Count", + "Federation_Matrix_enabled": "Enabled", "Field": "Kampo", "Field_removed": "Kampo forigita", "Field_required": "Kampo postulita", @@ -1458,6 +1460,10 @@ "Layout_Terms_of_Service": "Reguloj de servado", "LDAP": "LDAP", "LDAP_Description": "LDAP estas hierarkia datumbazo, kiun multaj kompanioj uzas por doni unu signon al ĝi - facilecon por dividi unu pasvorton inter pluraj retejoj kaj servoj. Por altnivelaj agordoj kaj ekzemploj, bonvolu konsulti nian vikion: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "Ĉifrado", + "LDAP_DataSync_BackgroundSync": "Fona Sinkro", + "LDAP_Server_Type": "Servilo Tipo", + "LDAP_Server_Type_Other": "Alia", "LDAP_Authentication": "Ebligu", "LDAP_Authentication_Password": "Pasvorto", "LDAP_Authentication_UserDN": "Uzanto DN", @@ -2488,6 +2494,7 @@ "Unarchive": "Senarkora", "unarchive-room": "Senĉambra Ĉambro", "unarchive-room_description": "Permeso al kanaloj unárquicos", + "unauthorized": "Ne rajtigita", "Unblock_User": "Malŝalti Uzanton", "Unignore": "Senigi", "Uninstall": "Malinstali", @@ -2664,6 +2671,8 @@ "Visitor_Navigation": "Vizitanto Navigado", "Visitor_page_URL": "Vizitanto paĝo URL", "Visitor_time_on_site": "Vizitanto tempo sur retejo", + "VoIP_Management_Server_Username": "Uzulnomo", + "VoIP_Management_Server_Password": "Pasvorto", "Wait_activation_warning": "Antaŭ ol vi povas ensaluti, via konto devas esti permane aktivigita de administranto.", "Warnings": "Avertoj", "We_are_offline_Sorry_for_the_inconvenience": "Ni estas eksterrete. Pardonu la malkomforton.", @@ -2739,6 +2748,7 @@ "Your_server_link": "Via servilo-ligilo", "Your_temporary_password_is_password": "Via provizora pasvorto estas: [pasvorto]", "Your_workspace_is_ready": "Via labora spaco pretas uzi 🎉", + "registration.page.login.errors.wrongCredentials": "Uzanto ne trovita aŭ malĝusta pasvorto", "registration.page.registration.waitActivationWarning": "Antaŭ ol vi povas ensaluti, via konto devas esti permane aktivigita de administranto.", "registration.page.login.forgot": "Ĉu vi forgesis vian pasvorton", "registration.page.resetPassword.sent": "Se ĉi tiu retpoŝto estas registrita, ni sendos instrukciojn pri kiel restarigi vian pasvorton. Se vi ne ricevos retpoŝton baldaŭ, bonvolu reveni kaj provu denove.", @@ -2748,6 +2758,7 @@ "registration.component.form.emailOrUsername": "Retpoŝtadreso aŭ uzantnomo", "registration.component.form.username": "Uzulnomo", "registration.component.form.name": "Nomo", + "registration.component.form.userAlreadyExist": "Uzantnomo jam ekzistas. Bonvolu provi alian uzantnomon.", "registration.component.form.emailAlreadyExists": "Retpoŝto jam ekzistas", "registration.component.form.usernameAlreadyExists": "Uzantnomo jam ekzistas. Bonvolu provi alian uzantnomon.", "registration.component.form.invalidEmail": "La retpoŝta eniro estas nevalida", diff --git a/packages/i18n/src/locales/es.i18n.json b/packages/i18n/src/locales/es.i18n.json index d49ad1bd10c6..8eca2a2aec15 100644 --- a/packages/i18n/src/locales/es.i18n.json +++ b/packages/i18n/src/locales/es.i18n.json @@ -13,8 +13,8 @@ "__usersCount__member_joined_other": "{{count}} miembros se han unido", "__usersCount__people_will_be_invited": "{{usersCount}} miembros sern invitados", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} ya no es {{role}} (por {{user_by}})", - "__usersCount__member_joined_many": "{{count}} miembros se han unido", "__username__was_set__role__by__user_by_": "{{username}} se ha establecido como {{role}} por {{user_by}}", + "__usersCount__member_joined_many": "{{count}} miembros se han unido", "__count__without__department__": "{{count}} sin departamentos", "__count__without__tags__": "{{count}} sin etiquetas", "__count__without__assignee__": "{{count}} sin un agente asignado", @@ -468,6 +468,7 @@ "Apply_and_refresh_all_clients": "Aplicar y actualizar todos los clientes", "Apps": "Aplicaciones", "Apps_context_installed": "Instalada", + "Apps_context_premium": "Premium", "Apps_Engine_Version": "Versión de motor de aplicaciones", "Apps_Essential_Alert": "Esta aplicación es esencial para los siguientes eventos:", "Apps_Essential_Disclaimer": "Los eventos enumerados arriba se interrumpirán si esta aplicación se deshabilita. Si quieres que Rocket.Chat funcione sin esta aplicación, tienes que desinstalarla.", @@ -1383,6 +1384,7 @@ "Desktop_Notifications_Not_Enabled": "Las notificaciones de escritorio no están habilitadas", "Details": "Detalles", "line": "línea", + "Device_Management_IP": "IP", "Different_Style_For_User_Mentions": "Estilo diferente para menciones de usuario", "Livechat_Facebook_API_Key": "Clave API de OmniChannel", "Livechat_Facebook_API_Secret": "Secreto de API de OmniChannel", @@ -1632,6 +1634,7 @@ "Enter_Normal": "Modo normal (enviar con Intro)", "Enter_to": "Intro para", "Enter_your_E2E_password": "Introduzca su contraseña E2E", + "message_counter_many": "{{count}} mensajes", "Entertainment": "Entretenimiento", "Error": "Error", "Error_404": "Error 404", @@ -1678,6 +1681,7 @@ "error-forwarding-department-target-not-allowed": "No se permite el reenvío al departamento de destino.", "error-guests-cant-have-other-roles": "Los usuarios invitados no pueden tener ningún otro rol.", "error-import-file-extract-error": "No se ha podido extraer el archivo de importación.", + "meteor_status_reconnect_in_many": "intentando de nuevo dentro de {{count}} segundos...", "error-import-file-is-empty": "El archivo importado parece estar vacío.", "error-import-file-missing": "El archivo para importar no se ha encontrado en la ruta especificada.", "error-importer-not-defined": "El importador no se ha definido correctamente: falta la clase de importación.", @@ -1869,6 +1873,7 @@ "FEDERATION_Test_Setup_Error": "No se ha podido encontrar tu servidor usando tu configuración, así que debes revisarla.", "FEDERATION_Test_Setup_Success": "La configuración de tu federación funciona y otros servidores pueden encontrarte", "Retry_Count": "Contador de reintentos", + "Federation_Matrix_enabled": "Habilitada", "Field": "Campo", "Field_removed": "Campo eliminado", "Field_required": "Campo obligatorio", @@ -1960,7 +1965,6 @@ "Finish": "Finalizar", "Finish_Registration": "Finalizar registro", "First_Channel_After_Login": "Primer Channel tras iniciar sesión", - "message_counter_many": "{{count}} mensajes", "First_response_time": "Tiempo de primera respuesta", "Flags": "Indicadores", "Follow_message": "Mensaje para seguirnos", @@ -2018,7 +2022,6 @@ "get-password-policy-mustContainAtLeastOneNumber": "La contraseña debe contener al menos un número", "get-password-policy-mustContainAtLeastOneSpecialCharacter": "La contraseña debe contener al menos un carácter especial", "get-password-policy-mustContainAtLeastOneUppercase": "La contraseña debe contener al menos una letra en mayúscula", - "meteor_status_reconnect_in_many": "intentando de nuevo dentro de {{count}} segundos...", "github_no_public_email": "No tienes ningún correo electrónico como correo público en tu cuenta de GitHub", "github_HEAD": "HEAD", "Give_a_unique_name_for_the_custom_oauth": "Indicar un nombre único para el OAuth personalizado", @@ -2681,11 +2684,14 @@ "Local_Time_time": "Hora local: {{time}}", "Localization": "Localización", "Location": "Ubicación", + "mentions_counter_many": "{{count}} menciones", "Log_Exceptions_to_Channel": "Registrar excepciones en el Channel", "Log_Exceptions_to_Channel_Description": "Canal que recibirá todas las excepciones capturadas. Dejar vacío para ignorar las excepciones.", "Log_File": "Mostrar archivo y línea", + "threads_counter_many": "{{count}} mensajes en hilo sin leer", "Log_Level": "Nivel de registro", "Log_Package": "Mostrar paquete", + "unread_messages_counter_many": "{{count}} mensajes sin leer", "Log_Trace_Methods": "Seguimiento de llamadas de método", "Log_Trace_Methods_Filter": "Seguimiento de filtro de métodos", "Log_Trace_Methods_Filter_Description": "El texto de aquí se evaluará como regex (\"new RegExp('text')\"). Dejar vacío para mostrar el rastro de cada llamada.", @@ -3190,7 +3196,9 @@ "Only_invited_users_can_acess_this_channel": "Solo los usuarios invitados pueden acceder a este Channel", "Oops_page_not_found": "Vaya, página no encontrada", "Oops!": "¡Vaya!", + "subscription.callout.description.limitsExceeded_many": "Su espacio de trabajo ha superado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "Open": "Abiertas", + "subscription.callout.description.limitsReached_many": "Su espacio de trabajo ha alcanzado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "Open_channel_user_search": "\"%s\" - Abrir búsqueda de usuario/Channel", "Open_conversations": "Conversaciones abiertas", "Open_Days": "Días abiertos", @@ -3245,12 +3253,9 @@ "Password_Changed_Email_Subject": "[Site_Name] - Contraseña cambiada", "Password_changed_section": "Contraseña cambiada", "Password_changed_successfully": "Contraseña cambiada correctamente", - "mentions_counter_many": "{{count}} menciones", "Password_History": "Historial de contraseñas", "Password_History_Amount": "Longitud del historial de contraseñas", - "threads_counter_many": "{{count}} mensajes en hilo sin leer", "Password_History_Amount_Description": "Cantidad de contraseñas usadas más recientemente para evitar que los usuarios las reutilicen.", - "unread_messages_counter_many": "{{count}} mensajes sin leer", "Password_Policy": "Política de contraseñas", "Password_to_access": "Contraseña para acceder", "Passwords_do_not_match": "Las contraseñas no coinciden", @@ -3343,6 +3348,7 @@ "Privacy": "Privacidad", "Privacy_Policy": "Política de privacidad", "Private": "Privado", + "Private_channels": "Channels privados", "Private_Channel": "Channel privado", "Private_Channels": "Channels privados", "Private_Chats": "Chats privados", @@ -3419,6 +3425,7 @@ "Reactions": "Reacciones", "Read_by": "Leído por", "Read_only": "Solo lectura", + "This_room_is_read_only": "Esta sala es de solo lectura", "Read_only_changed_successfully": "Solo lectura cambiado correctamente", "Read_only_channel": "Channel de solo lectura", "Read_only_group": "Grupo de solo lectura", @@ -3577,6 +3584,7 @@ "Unsafe_Url": "URL no segura", "Rocket_Chat_Alert": "Alerta de Rocket.Chat", "Role": "Rol", + "Roles": "Roles", "Role_Editing": "Edición de roles", "Role_Mapping": "Asegnación de roles", "Role_removed": "Rol eliminado", @@ -3938,11 +3946,9 @@ "Sound": "Sonido", "Sound_File_mp3": "Archivo de sonido (mp3)", "Source": "Fuente", - "subscription.callout.description.limitsExceeded_many": "Su espacio de trabajo ha superado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "SSL": "SSL", "Star": "Destacar", "Star_Message": "Destacar mensaje", - "subscription.callout.description.limitsReached_many": "Su espacio de trabajo ha alcanzado los límites <1> {{val, list}} . <3> Administre su suscripción para incrementar los límites.", "Starred_Messages": "Mensajes destacados", "Start": "Iniciar", "Start_audio_call": "Iniciar llamada de audio", @@ -4325,6 +4331,7 @@ "unarchive-room": "Room desarchivada", "unarchive-room_description": "Permiso para desarchivar canales", "Unassigned": "Sin asignar", + "unauthorized": "No autorizado", "Unavailable": "No disponible", "Unblock_User": "Desbloquear usuario ", "Uncheck_All": "Desmarcar todo", @@ -4822,8 +4829,10 @@ "registration.component.form.requiredField": "Este campo es obligatorio", "registration.component.form.joinYourTeam": "Únete a tu equipo", "registration.component.form.reasonToJoin": "Motivo para unirse", + "registration.component.form.invalidConfirmPass": "La confirmación de la contraseña no coincide con la contraseña", "registration.component.form.confirmPassword": "Confirma tu contraseña", "registration.component.form.confirmation": "Confirmación", + "registration.component.form.sendConfirmationEmail": "Enviar correo electrónico de confirmación", "registration.component.form.register": "Registrar", "onboarding.component.form.requiredField": "Este campo es obligatorio", "onboarding.component.form.steps": "Paso {{currentStep}} de {{stepCount}}", diff --git a/packages/i18n/src/locales/et.i18n.json b/packages/i18n/src/locales/et.i18n.json index 52ea2c08027c..e86303430c09 100644 --- a/packages/i18n/src/locales/et.i18n.json +++ b/packages/i18n/src/locales/et.i18n.json @@ -43,6 +43,7 @@ "Cancel": "Tühista", "Cancel_message_input": "Tühista", "Close_menu": "Sulge menüü", + "Connected": "Ühendatud", "Edit_Status": "Redigeeri olekut", "How_friendly_was_the_chat_agent": "Kui sõbralik oli sinuga vestelnud agent?", "How_knowledgeable_was_the_chat_agent": "Kui teadlik oli sinuga vestelnud agent? ", diff --git a/packages/i18n/src/locales/eu.i18n.json b/packages/i18n/src/locales/eu.i18n.json index 7585a29458f2..c6e37c60e2c7 100644 --- a/packages/i18n/src/locales/eu.i18n.json +++ b/packages/i18n/src/locales/eu.i18n.json @@ -87,6 +87,7 @@ "Discussions": "Eztabaidak", "Edit_Status": "Editatu egoera", "Enabled": "Gaituta", + "Federation_Matrix_enabled": "Gaituta", "Group_discussions": "Talde eztabaidak", "How_satisfied_were_you_with_this_chat": "Zein pozik zeuden txat honekin?", "Installation": "Instalazioa", @@ -106,6 +107,7 @@ "Send": "Bidali", "Skip": "Saltatu", "Start_Chat": "Hasi txata", + "Stats_Total_Channels": "Kanalak", "Survey": "Inkesta", "Thank_you_for_your_feedback": "Eskerrik asko zure iritziagatik", "Total_Discussions": "Eztabaida guztiak", diff --git a/packages/i18n/src/locales/fa.i18n.json b/packages/i18n/src/locales/fa.i18n.json index a04ca62af4b1..cfeb59e37ddd 100644 --- a/packages/i18n/src/locales/fa.i18n.json +++ b/packages/i18n/src/locales/fa.i18n.json @@ -382,6 +382,7 @@ "Apply": "درخواست دادن", "Apply_and_refresh_all_clients": "اعمال کن و تمامی کاربران را رفرش کن", "Apps": "برنامه ها", + "Apps_context_premium": "شرکت، پروژه", "Apps_Engine_Version": "ورژن موتور برنامه ها", "Apps_Marketplace_Deactivate_App_Prompt": "آیا واقعاً می خواهید این برنامه را غیرفعال کنید؟", "Apps_Marketplace_Login_Required_Description": "خرید برنامه از Rocket.Chat Marketplace نیاز به ثبت فضای کاری شما و ورود به سیستم دارد.", @@ -1356,6 +1357,7 @@ "FEDERATION_Domain": "دامنه", "FEDERATION_Status": "وضعیت", "Retry_Count": "تعداد تلاش مجدد", + "Federation_Matrix_enabled": "فعال شد", "Field": "فیلد", "Field_removed": "فیلد حذف شد", "Field_required": "فیلد مورد نیاز است", @@ -1722,6 +1724,11 @@ "Layout_Terms_of_Service": "شرایط استفاده از خدمات", "LDAP": "LDAP", "LDAP_Description": "یک مرکز برای به اشتراک گذاری یک رمز عبور بین سایت و خدمات مختلف - LDAP یک پایگاه داده سلسله مراتبی است که بسیاری از شرکت ها برای ارائه یکبار ورود به سیستم است. https://rocket.chat/docs/administrator-guides/authentication/ldap/: برای کسب اطلاعات پیکربندی پیشرفته و نمونه، لطفا ویکی ما مشورت کنید.", + "LDAP_Connection_Encryption": "رمزگذاری", + "LDAP_DataSync_Advanced": "همگام سازی پیشرفته", + "LDAP_DataSync_BackgroundSync": "همگام سازی پس زمینه", + "LDAP_Server_Type": "نوع سرور", + "LDAP_Server_Type_Other": "دیگر", "LDAP_Advanced_Sync": "همگام سازی پیشرفته", "LDAP_Authentication": "فعال کردن", "LDAP_Authentication_Password": "رمز عبور", @@ -3008,6 +3015,8 @@ "Visitor_Navigation": "کنترل های بازدید کننده", "Visitor_page_URL": "URL صفحه های بازدید کننده", "Visitor_time_on_site": "زمان بازدید کنندگان در سایت", + "VoIP_Management_Server_Username": "نام کاربری", + "VoIP_Management_Server_Password": "رمز عبور", "Wait_activation_warning": "قبل از اینکه شما می توانید وارد شوید، حساب خود را باید به صورت دستی توسط مدیر فعال می شود.", "Warnings": "اخطارها", "We_are_offline_Sorry_for_the_inconvenience": "متاسفانه ما برخط نیستیم.", @@ -3093,6 +3102,7 @@ "registration.component.form.emailOrUsername": "ایمیل یا نام کاربری", "registration.component.form.username": "نام کاربری", "registration.component.form.name": "نام", + "registration.component.form.userAlreadyExist": "نام کاربری از قبل وجود دارد. لطفا نام کاربری دیگری را امتحان کنید", "registration.component.form.emailAlreadyExists": "ایمیل از قبل وجود دارد", "registration.component.form.usernameAlreadyExists": "نام کاربری از قبل وجود دارد. لطفا نام کاربری دیگری را امتحان کنید", "registration.component.form.invalidEmail": "ایمیل وارد شده نامعتبر است", diff --git a/packages/i18n/src/locales/fi.i18n.json b/packages/i18n/src/locales/fi.i18n.json index 6783b74a9ea9..7cc44651e568 100644 --- a/packages/i18n/src/locales/fi.i18n.json +++ b/packages/i18n/src/locales/fi.i18n.json @@ -11,6 +11,7 @@ "__username__was_set__role__by__user_by_": "{{user_by}} muutti käyttäjän {{username}} rooliksi {{role}} ", "This_room_encryption_has_been_enabled_by__username_": "Tämän huoneen salauksen otti käyttöön {{username}} ", "This_room_encryption_has_been_disabled_by__username_": "Tämän huoneen salauksen poisti käytöstä {{username}}", + "Third_party_login": "Kolmannen osapuolen kirjautuminen", "Enabled_E2E_Encryption_for_this_room": "otti E2E-salauksen käyttöön tässä huoneessa", "disabled": "poissa käytöstä", "Disabled_E2E_Encryption_for_this_room": "poisti E2E-salauksen käytöstä tässä huoneessa", @@ -507,6 +508,7 @@ "Apps_context_installed": "Asennettu", "Apps_context_requested": "Pyydetty", "Apps_context_private": "Yksityiset sovellukset", + "Apps_context_premium": "Yritys", "Apps_Count_Enabled_one": "{{count}} sovellus käytössä", "Apps_Count_Enabled_other": "{{count}} sovellusta käytössä", "Private_Apps_Count_Enabled_one": "{{count}} yksityistä sovellusta käytössä", @@ -3831,6 +3833,7 @@ "Privacy_summary": "Tietosuojakäytännön yhteenveto", "Private": "Yksityinen", "private": "yksityinen", + "Private_channels": "Yksityiset kanavat Channel", "Private_Apps": "Yksityiset sovellukset", "Private_Channel": "Yksityinen kanava Channel", "Private_Channels": "Yksityiset kanavat Channel", @@ -5569,6 +5572,7 @@ "onboarding.component.form.action.next": "Seuraava", "onboarding.component.form.action.skip": "Ohita tämä vaihe", "onboarding.component.form.action.register": "Rekisteröi", + "onboarding.component.form.action.registerWorkspace": "Rekisteröi työtila", "onboarding.component.form.action.confirm": "Vahvista", "onboarding.component.form.termsAndConditions": "Hyväksyn käyttöehdot <1>ja <3>tietosuojaselosteen", "onboarding.component.emailCodeFallback": "Etkö saanut sähköpostia? <1>Lähetä uudelleen tai <3>muuta sähköpostia", @@ -5609,6 +5613,7 @@ "onboarding.form.adminInfoForm.fields.password.placeholder": "Luo salasana", "onboarding.form.adminInfoForm.fields.keepPosted.label": "Pidä minut ajan tasalla chatsovelluksen päivityksistä", "onboarding.form.awaitConfirmationForm.title": "Odotamme vahvistusta", + "onboarding.form.awaitConfirmationForm.content.securityCode": "Turvakoodi", "onboarding.form.organizationInfoForm.title": "Organisaation tiedot", "onboarding.form.organizationInfoForm.subtitle": "Pyydämme kärsivällisyyttä, nämä tiedot auttavat meitä muokkaamaan työtilasi yksilölliseksi", "onboarding.form.organizationInfoForm.fields.organizationName.label": "Organisaation nimi", @@ -5621,6 +5626,7 @@ "onboarding.form.organizationInfoForm.fields.organizationSize.placeholder": "Valitse", "onboarding.form.organizationInfoForm.fields.country.label": "Maa", "onboarding.form.organizationInfoForm.fields.country.placeholder": "Valitse", + "onboarding.form.registerOfflineForm.title": "Rekisteröidy offline-tilassa", "onboarding.form.registeredServerForm.title": "Rekisteröi palvelimesi", "onboarding.form.registeredServerForm.included.push": "Mobiili push-ilmoitukset", "onboarding.form.registeredServerForm.included.externalProviders": "Integrointi ulkoisten palveluntarjoajien kanssa (WhatsApp, Facebook, Telegram, Twitter)", diff --git a/packages/i18n/src/locales/fr.i18n.json b/packages/i18n/src/locales/fr.i18n.json index ed73b52dccbd..b8c6ee5b6414 100644 --- a/packages/i18n/src/locales/fr.i18n.json +++ b/packages/i18n/src/locales/fr.i18n.json @@ -409,6 +409,7 @@ "API_Shield_Types_Description": "Types de bouclier à activer sous forme de liste séparée par des virgules, choisissez parmi `en ligne`,` canal` ou `*` pour tout", "Apps_Framework_Development_Mode": "Activer le mode de développement", "API_Shield_user_require_auth": "Exiger une authentification pour les boucliers des utilisateurs", + "Calls_in_queue_many": "{{count}} appels en file d'attente", "API_Token": "Jeton d'API", "Apps_Framework_Development_Mode_Description": "Le mode de développement permet l'installation d'applications qui ne proviennent pas du marketplace Rocket.Chat.", "API_Tokenpass_URL": "URL du serveur Tokenpass", @@ -452,6 +453,7 @@ "Apply_and_refresh_all_clients": "Appliquer et actualiser tous les clients", "Apps": "Applications", "Apps_context_installed": "Installé", + "Apps_context_premium": "Entreprise", "Apps_Engine_Version": "Version d'Apps Engine", "Apps_Essential_Alert": "Cette application est indispensable pour les événements suivants :", "Apps_Essential_Disclaimer": "Les événements ci-dessus seront perturbés si cette application est désactivée. Si vous voulez que Rocket.Chat fonctionne sans les fonctionnalités de cette application, vous devez la désinstaller", @@ -488,7 +490,6 @@ "Apps_License_Message_appId": "La licence n'a pas été émise pour cette application", "Apps_License_Message_bundle": "Licence émise pour un bundle qui ne contient pas l'application", "Apps_License_Message_expire": "La licence n'est plus valide et doit être renouvelée", - "Calls_in_queue_many": "{{count}} appels en file d'attente", "Apps_License_Message_maxSeats": "La licence n'est pas adaptée au nombre actuel d'utilisateurs actifs. Augmentez le nombre de sièges", "Apps_License_Message_publicKey": "Une erreur s'est produite lors du déchiffrement de la licence. Synchronisez votre espace de travail dans les services de connectivité et réessayez", "Apps_License_Message_renewal": "La licence a expiré et doit être renouvelée", @@ -1380,6 +1381,7 @@ "Desktop_Notifications_Not_Enabled": "Les notifications de bureau ne sont pas activées", "Details": "Détails", "line": "ligne", + "Device_Management_IP": "IP", "Different_Style_For_User_Mentions": "Style différent pour les mentions d'utilisateur", "Livechat_Facebook_API_Key": "Clé API d'omnicanal", "Livechat_Facebook_API_Secret": "Clé secrète d'API d'omnicanal", @@ -1630,6 +1632,7 @@ "Enter_Normal": "Mode normal (envoyé avec Entrée)", "Enter_to": "Entrée pour", "Enter_your_E2E_password": "Entrez votre mot de passe E2E", + "message_counter_many": "{{count}} messages", "Entertainment": "Divertissement", "Error": "Erreur", "Error_404": "Erreur 404", @@ -1676,6 +1679,7 @@ "error-forwarding-department-target-not-allowed": "Le transfert vers le département cible n'est pas autorisé.", "error-guests-cant-have-other-roles": "Les utilisateurs invités ne peuvent avoir aucun autre rôle.", "error-import-file-extract-error": "Échec de l'extraction du fichier d'importation.", + "meteor_status_reconnect_in_many": "nouvelle tentative dans {{count}} secondes...", "error-import-file-is-empty": "Le fichier importé semble être vide.", "error-import-file-missing": "Le fichier à importer est introuvable dans l'emplacement spécifié.", "error-importer-not-defined": "L'importateur n'a pas été défini correctement, il manque la classe d'importation.", @@ -1866,6 +1870,7 @@ "FEDERATION_Test_Setup_Error": "Impossible de trouver votre serveur en utilisant votre configuration, vérifiez vos paramètres.", "FEDERATION_Test_Setup_Success": "Votre configuration de fédération fonctionne et d'autres serveurs peuvent vous trouver !", "Retry_Count": "Nombre de tentatives", + "Federation_Matrix_enabled": "Activé", "Field": "Champ", "Field_removed": "Champ supprimé", "Field_required": "Champ requis", @@ -1957,7 +1962,6 @@ "Finish": "Terminer", "Finish_Registration": "Terminer l'inscription", "First_Channel_After_Login": "Premier canal après la connexion", - "message_counter_many": "{{count}} messages", "First_response_time": "Temps de première réponse", "Flags": "Indicateurs", "Follow_message": "Suivre le message", @@ -2016,7 +2020,6 @@ "get-password-policy-mustContainAtLeastOneSpecialCharacter": "Le mot de passe doit contenir au moins un caractère spécial", "get-password-policy-mustContainAtLeastOneUppercase": "Le mot de passe doit contenir au moins une lettre majuscule", "get-server-info": "Obtenir des informations sur le serveur", - "meteor_status_reconnect_in_many": "nouvelle tentative dans {{count}} secondes...", "github_no_public_email": "Vous n'avez pas d'adresse e-mail publique associée à votre compte GitHub", "github_HEAD": "EN-TÊTE", "Give_a_unique_name_for_the_custom_oauth": "Indiquez un nom unique pour OAuth personnalisé", @@ -3343,6 +3346,7 @@ "Privacy": "Confidentialité", "Privacy_Policy": "Politique de confidentialité", "Private": "Privé", + "Private_channels": "Canaux privés", "Private_Channel": "Canal privé", "Private_Channels": "Canaux privés", "Private_Chats": "Chats privés", @@ -3419,6 +3423,7 @@ "Reactions": "Réactions", "Read_by": "Lu par", "Read_only": "Lecture seule", + "This_room_is_read_only": "Le salon est en lecture seule", "Read_only_changed_successfully": "Modification du mode lecture seule réussie", "Read_only_channel": "Canal en lecture seule", "Read_only_group": "Groupe en lecture seule", @@ -3581,6 +3586,7 @@ "Unsafe_Url": "URL non sécurisées", "Rocket_Chat_Alert": "Alerte Rocket.Chat", "Role": "Rôle", + "Roles": "Rôles", "Role_Editing": "Modification de rôle", "Role_Mapping": "Mappage de rôles", "Role_removed": "Rôle supprimé", @@ -4794,6 +4800,7 @@ "registration.component.resetPassword": "Réinitialiser le mot de passe", "registration.component.form.username": "Nom d'utilisateur", "registration.component.form.name": "Nom", + "registration.component.form.userAlreadyExist": "Ce nom d'utilisateur existe déjà. Essayez avec un autre nom d'utilisateur.", "registration.component.form.emailAlreadyExists": "L'adresse e-mail existe déjà", "registration.component.form.usernameAlreadyExists": "Ce nom d'utilisateur existe déjà. Essayez avec un autre nom d'utilisateur.", "registration.component.form.invalidEmail": "L'adresse e-mail saisie est invalide", diff --git a/packages/i18n/src/locales/gl.i18n.json b/packages/i18n/src/locales/gl.i18n.json index a17dba96f252..c616ab6c6b4e 100644 --- a/packages/i18n/src/locales/gl.i18n.json +++ b/packages/i18n/src/locales/gl.i18n.json @@ -167,6 +167,7 @@ "Favorite": "Favorito", "Favorite_Rooms": "Habilitar Room favoritas", "Favorites": "Favoritos", + "Federation_Matrix_enabled": "Activado", "File_name_Placeholder": "Procurar arquivos...", "files": "arquivos", "Files": "Lista de arquivos", @@ -262,6 +263,8 @@ "Started_a_video_call": "Iniciou unha videochamada", "Stats_Away_Users": "Usuarios ausentes", "Stats_Online_Users": "Usuarios en liña", + "Stats_Total_Channels": "Channel", + "Stats_Total_Messages": "Mensaxes", "StatusMessage": "Mensaxe de Estado", "Symbols": "Símbolos", "theme-color-status-away": "Cor do estado ausente", @@ -385,5 +388,8 @@ "onboarding.form.standaloneServerForm.title": "Confirmación do servidor autónomo", "onboarding.form.standaloneServerForm.servicesUnavailable": "Algúns dos servizos non estarán dispoñibles ou requirirán unha configuración manual", "onboarding.form.standaloneServerForm.publishOwnApp": "Para enviar notificacións push, debes compilar e publicar a túa propia aplicación en Google Play e App Store", - "onboarding.form.standaloneServerForm.manuallyIntegrate": "Necesita integrarse manualmente con servizos externos" + "onboarding.form.standaloneServerForm.manuallyIntegrate": "Necesita integrarse manualmente con servizos externos", + "Awaiting_confirmation": "Agardando confirmación", + "RegisterWorkspace_Features_MobileNotifications_Title": "Notificacións push móbiles", + "cloud.RegisterWorkspace_Setup_Terms_Privacy": "Acepto os <1>Termos e condicións e a <3>Política de privacidade" } \ No newline at end of file diff --git a/packages/i18n/src/locales/he.i18n.json b/packages/i18n/src/locales/he.i18n.json index b231a2c2ce9a..84da76d59421 100644 --- a/packages/i18n/src/locales/he.i18n.json +++ b/packages/i18n/src/locales/he.i18n.json @@ -58,6 +58,7 @@ "Accounts_OAuth_Custom_Identity_Path": "נתיב הזהות", "Accounts_OAuth_Custom_Login_Style": "סגנון כניסה", "Accounts_OAuth_Custom_Roles_Claim": "שם שדה / קבוצות", + "Accounts_OAuth_Custom_Scope": "תְחוּם", "Accounts_OAuth_Custom_Secret": "סוד", "Accounts_OAuth_Custom_Token_Path": "נתיב ה-Token ", "Accounts_OAuth_Custom_Token_Sent_Via": "Via אסימון שנשלח", @@ -179,6 +180,7 @@ "Announcement": "הודעת הכרזה", "API": "API", "API_Analytics": "סטטיסטיקה", + "API_Drupal_URL_Description": "דוגמה: `https://domain.com` (ללא \"/\" בסוף הכתובת)", "API_Embed": "הטמעה", "API_EmbedIgnoredHosts": "מארחי שבץ שהתעלמו מהן", "API_EmbedIgnoredHosts_Description": "רשימה מופרדת בפסיקים של מארחים או כתובות CIDR, למשל. localhost, 127.0.0.1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16", @@ -355,6 +357,7 @@ "Create_unique_rules_for_this_channel": "יצירת חוקים ייחודיים לערוץ זה", "Created_at": "נוצר ב־", "Created_at_s_by_s": "נוצר בתאריך %s על ידי %s", + "CROWD_Reject_Unauthorized": "דחית מורשה", "Current_Chats": "צ'אטים נוכחיים", "Current_Status": "סטטוס נוכחי", "Custom": "מותאם", @@ -401,6 +404,7 @@ "Direct_message_someone": "מישהו מסר ישיר", "Direct_Messages": "הודעות ישירות", "Direct_Reply": "הגב בפרטי", + "Direct_Reply_Username": "שם משתמש", "Directory": "קבוצות ציבוריות ומשתמשים", "Disable_Notifications": "השבת נוטיפיקציות", "Disabled": "לא משופעל", @@ -561,6 +565,7 @@ "Favorites": "מועדפים", "FEDERATION_Domain": "תחום", "FEDERATION_Status": "סטטוס", + "Federation_Matrix_enabled": "משופעל", "Field": "שדה", "Field_removed": "שדה הוסר", "Field_required": "שדה נדרש", @@ -691,6 +696,8 @@ "Integrations_Outgoing_Type_SendMessage": "הודעה נשלחה", "Integrations_Outgoing_Type_UserCreated": "המשתמש נוצר", "InternalHubot": "Hubot הפנימי", + "InternalHubot_EnableForChannels": "אפשר ערוצים ציבוריים", + "InternalHubot_EnableForPrivateGroups": "אפשר ערוצים פרטיים", "InternalHubot_ScriptsToLoad": "סקריפטים לטעון", "InternalHubot_ScriptsToLoad_Description": "הזן רשימה מופרדת בפסיקים של סקריפטים לטעון מן https://github.com/github/hubot-scripts/tree/master/src/scripts", "InternalHubot_Username_Description": "זה חייב להיות שם משתמש תקין של בוט רשום בשרת שלך.", @@ -766,6 +773,8 @@ "Layout_Sidenav_Footer_Dark_description": "גודל חלק תחתון הוא 260 על 70 פיקסלים", "Layout_Terms_of_Service": "תנאי השירות", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "הצף", + "LDAP_UserSearch": "חיפוש משתמשים", "LDAP_Authentication": "לאפשר", "LDAP_Authentication_UserDN_Description": "משתמש LDAP שמבצע חיפושי משתמש כדי לאמת משתמשים אחרים כאשר הם נכנסים. \n זה בדרך כלל חשבון שירות שנוצרו במיוחד עבור ואינטגרציות צד שלישי. השתמש בשם מלא, כגון `cn = מנהל, cn = Users, dc = דוגמא, dc = com`.", "LDAP_BaseDN_Description": "השם הייחודי מלא (DN) של עץ משנה LDAP אתה רוצה לחפש משתמשים וקבוצות. אתה יכול להוסיף כמה שאתה רוצה; עם זאת, כל קבוצה חייבת להיות מוגדרת באותו הבסיס מושלם בתור המשתמשים ששייכים אליו. אם תציין קבוצות משתמש מוגבלות, רק משתמשים השייכים לקבוצות אלו יהיו בהיקפה. אנו ממליצים לציין את הרמה העליונה של ספריית עץ LDAP שלך כבסיס התחום שלך ולהשתמש במסנן חיפוש כדי לשלוט בגישה.", @@ -890,6 +899,7 @@ "Message_removed": "ההודעה הוסרה", "Message_ShowDeletedStatus": "הצגת מצב מחיקה", "Message_starring": "סימון הודעות בכוכב", + "Message_TimeAndDateFormat_Description": "לעיון נוסף: [Moment.js](http://momentjs.com/docs/#/displaying/format/)", "Message_TimeFormat": "תבנית זמן", "Message_TimeFormat_Description": "ראו גם: [Moment.js](http://momentjs.com/docs/#/displaying/format/)", "Message_too_long": "ההודעה ארוכה מדי", @@ -1312,6 +1322,7 @@ "theme-color-primary-background-color": "צבע רקע ראשי", "theme-color-primary-font-color": "צבע פונט ראשי", "theme-color-rc-color-content": "תוכן", + "theme-color-rc-color-error": "שגיאה", "theme-color-secondary-background-color": "צבע רקע משני", "theme-color-secondary-font-color": "צבע פונט משני", "theme-color-status-away": "צבע סטטוס לא נמצא", @@ -1452,6 +1463,7 @@ "View_mode": "מצב תצוגה", "View_All": "הצגת הכול", "View_Logs": "יומנים", + "view-logs": "יומנים", "Viewing_room_administration": "ניהול הצפייה בחדר", "Visibility": "רְאוּת", "Visible": "נִרְאֶה", @@ -1460,6 +1472,8 @@ "Visitor_Navigation": "ניווט גולשים", "Visitor_page_URL": "כתובת אתר של דף הגולש", "Visitor_time_on_site": "זמן גולש באתר", + "VoIP_Management_Server_Username": "שם משתמש", + "VoIP_Management_Server_Password": "ססמה", "Wait_activation_warning": "לפני שתוכל להתחבר, החשבון שלך חייב לעבור אישור על ידי האדמין.", "We_are_offline_Sorry_for_the_inconvenience": "אנחנו במצב לא מקוון. מצטער על אי הנוחות.", "We_have_sent_password_email": "בדקות הקרובות אמורה להגיע אליך הודעה בדוא״ל עם הוראות לאיפוס הססמה. אם ההודעה לא מגיעה אליך, נא לנסות שוב.", diff --git a/packages/i18n/src/locales/hi-IN.i18n.json b/packages/i18n/src/locales/hi-IN.i18n.json new file mode 100644 index 000000000000..a2b3e208382f --- /dev/null +++ b/packages/i18n/src/locales/hi-IN.i18n.json @@ -0,0 +1,217 @@ +{ + "500": "आंतरिक सर्वर त्रुटि", + "__username__is_no_longer__role__defined_by__user_by_": "{{username}} is no longer {{role}} by {{user_by}}", + "__username__was_set__role__by__user_by_": "{{username}} was set {{role}} by {{user_by}}", + "@username": "@यूज़रनेम", + "@username_message": "@यूज़रनेम ", + "#channel": "#चैनल", + "%_of_conversations": "% बातचीत", + "0_Errors_Only": "0 - त्रुटियां केवल", + "1_Errors_and_Information": "1 - त्रुटियां और सूचना", + "2_Erros_Information_and_Debug": "2 - त्रुटियां, सूचना और डिबग", + "12_Hour": "12-घंटे की घड़ी", + "24_Hour": "24-घंटे की घड़ी", + "Accept": "स्वीकार करें", + "Accept_incoming_livechat_requests_even_if_there_are_no_online_agents": "यदि कोई ऑनलाइन एजेंट नहीं हैं, तो भी इनकमिंग लाइवचैट अनुरोध स्वीकार करें", + "Accept_with_no_online_agents": "कोई ऑनलाइन एजेंटों के साथ स्वीकार करें", + "Access_not_authorized": "प्रवेश अधिकृत नहीं है", + "Access_Token_URL": "एक्सेस टोकन URL", + "access-mailer": "मेलर स्क्रीन एक्सेस करें", + "access-mailer_description": "सभी उपयोगकर्ताओं को बड़े पैमाने पर ईमेल भेजने की अनुमति।", + "access-permissions": "अनुमतियाँ स्क्रीन एक्सेस करें", + "access-permissions_description": "विभिन्न भूमिकाओं के लिए अनुमतियों को संशोधित करें।", + "Accessing_permissions": "अक्सेस्सिंग की अनुमति", + "Account_SID": "खाता एसआईडी", + "Accounts": "खाता", + "Accounts_Admin_Email_Approval_Needed_Default": "

          The user [name] ([email]) has been registered.

          Please check \"Administration -> Users\" to activate or delete it.

          ", + "Accounts_Admin_Email_Approval_Needed_Subject_Default": "एक नया उपयोगकर्ता पंजीकृत है और उसे अनुमोदन की आवश्यकता है", + "Accounts_Admin_Email_Approval_Needed_With_Reason_Default": "

          The user [name] ([email]) has been registered.

          Reason: [reason]

          Please check \"Administration -> Users\" to activate or delete it.

          ", + "Accounts_AllowAnonymousRead": "अनाम पढ़ने की अनुमति दें", + "Accounts_AllowAnonymousWrite": "अनाम लिखने की अनुमति दें", + "Accounts_AllowDeleteOwnAccount": "उपयोगकर्ताओं को स्वयं का खाता हटाने की अनुमति दें", + "Accounts_AllowedDomainsList": "अनुमत डोमेन सूची", + "Accounts_AllowedDomainsList_Description": "अनुमत डोमेन की कोमा-पृथक सूची", + "Accounts_AllowEmailChange": "ईमेल परिवर्तन की अनुमति दें", + "Accounts_AllowPasswordChange": "पासवर्ड बदलने की अनुमति दें", + "Accounts_AllowRealNameChange": "नाम बदलने की अनुमति दें", + "Accounts_AllowUserAvatarChange": "उपयोगकर्ता अवतार परिवर्तन की अनुमति दें", + "Accounts_AllowUsernameChange": "उपयोगकर्ता नाम बदलने की अनुमति दें", + "Accounts_AllowUserProfileChange": "उपयोगकर्ता प्रोफ़ाइल बदलने की अनुमति दें", + "Accounts_AvatarBlockUnauthenticatedAccess": "अपुष्ट एक्सेस को अवतारों से ब्लॉक करें", + "Accounts_AvatarCacheTime": "अवतार कैश समय", + "Accounts_AvatarCacheTime_description": "HTTP प्रोटोकॉल को अवतार छवियों को कैश करने के लिए सेकंड की संख्या बताई गई है।", + "Accounts_AvatarExternalProviderUrl": "अवतार बाहरी प्रदाता URL", + "Accounts_AvatarExternalProviderUrl_Description": "उदाहरण: `https://acme.com/api/v1/ {उपयोगकर्ता नाम}`", + "Accounts_AvatarResize": "अवतार का आकार बदलें", + "Accounts_AvatarSize": "अवतार का आकार", + "Accounts_BlockedDomainsList": "अवरुद्ध डोमेन सूची", + "Accounts_BlockedDomainsList_Description": "अवरुद्ध डोमेन की कोमा से अलग सूची", + "Accounts_BlockedUsernameList": "अवरुद्ध उपयोगकर्ता नाम सूची", + "Accounts_BlockedUsernameList_Description": "कॉमा-अवरुद्ध उपयोगकर्ता नाम की अलग-अलग सूची (केस-असंवेदनशील)", + "Accounts_CustomFields_Description": "एक वैध JSON होना चाहिए जहां कुंजियाँ फ़ील्ड नाम हैं जिसमें फ़ील्ड सेटिंग्स का शब्दकोश है। उदाहरण: \n`{\"role\":{ \"type\": \"select\", \"defaultValue\": \"student\", \"options\": [\"teacher\", \"student\"], \"required\": true, \"modifyRecordField\": { \"array\": true, \"field\": \"roles\" } }, \"twitter\": { \"type\": \"text\", \"required\": true, \"minLength\": 2, \"maxLength\": 10 }}`", + "Accounts_CustomFieldsToShowInUserInfo": "कस्टम फ़ील्ड उपयोगकर्ता जानकारी में दिखाने के लिए", + "Accounts_Default_User_Preferences": "डिफ़ॉल्ट उपयोगकर्ता प्राथमिकताएं", + "Accounts_Default_User_Preferences_audioNotifications": "ऑडियो सूचनाएं डिफ़ॉल्ट चेतावनी", + "Accounts_Default_User_Preferences_desktopNotifications": "डेस्कटॉप सूचनाएं डिफ़ॉल्ट चेतावनी", + "Accounts_Default_User_Preferences_pushNotifications": "मोबाइल सूचनाएं डिफ़ॉल्ट चेतावनी", + "Accounts_Default_User_Preferences_not_available": "उपयोगकर्ता प्राथमिकताएँ प्राप्त करने में विफल, क्योंकि वे उपयोगकर्ता द्वारा अभी तक सेट नहीं किए गए हैं", + "Accounts_DefaultUsernamePrefixSuggestion": "डिफ़ॉल्ट उपयोगकर्ता नाम उपसर्ग सुझाव", + "Accounts_denyUnverifiedEmail": "अयोग्य ईमेल अस्वीकार करें", + "Accounts_Email_Activated": "[name]

          आपका खाता सक्रिय हो गया था।

          ", + "Accounts_Email_Activated_Subject": "खाता सक्रिय किया गया", + "Accounts_Email_Approved": "[name]

          आपका खाता स्वीकृत हो गया।

          ", + "Accounts_Email_Approved_Subject": "खाता स्वीकृत हुआ", + "Accounts_Email_Deactivated": "[name]

          आपका खाता निष्क्रिय कर दिया गया।

          ", + "Accounts_Email_Deactivated_Subject": "खाता निष्क्रिय किया गया", + "Accounts_EmailVerification": "ई - मेल सत्यापन", + "Accounts_EmailVerification_Description": "सुनिश्चित करें कि आपके पास इस सुविधा का उपयोग करने के लिए SMTP सेटिंग्स सही हैं", + "Accounts_Enrollment_Email": "नामांकन ईमेल", + "Accounts_Enrollment_Email_Default": "

          Welcome to [Site_Name]

          Go to [Site_URL] and try the best open source chat solution available today!

          ", + "Accounts_Enrollment_Email_Description": "आप निम्नलिखित प्लेसहोल्डर का उपयोग कर सकते हैं: \n - `[name]`, `[fname]`, `[lname]` उपयोगकर्ता के पूर्ण नाम के लिए, क्रमशः पहला नाम या अंतिम नाम। - । ईमेल] उपयोगकर्ता के ईमेल के लिए। \n - `[Site_Name]` और `[Site_URL]` क्रमशः अनुप्रयोग नाम और URL के लिए। ", + "Accounts_Enrollment_Email_Subject_Default": "[Site_Name] में आपका स्वागत है", + "Accounts_ForgetUserSessionOnWindowClose": "विंडो बंद होने पर उपयोगकर्ता सत्र को भूल जाएं", + "Accounts_Iframe_api_method": "API विधि", + "Accounts_Iframe_api_url": "API URL", + "Accounts_iframe_enabled": "सक्रिय", + "Accounts_iframe_url": "Iframe URL", + "Accounts_LoginExpiration": "दिन में प्रवेश की समाप्ति", + "Accounts_ManuallyApproveNewUsers": "नए उपयोगकर्ताओं को मैन्युअल रूप से अनुमोदित करें", + "Accounts_OAuth_Custom_Authorize_Path": "पथ अधिकृत करें", + "Accounts_OAuth_Custom_Button_Color": "बटन का रंग", + "Accounts_OAuth_Custom_Button_Label_Color": "बटन टेक्स्ट का रंग", + "Accounts_OAuth_Custom_Button_Label_Text": "बटन टेक्स्ट", + "Accounts_OAuth_Custom_Enable": "सक्षम करें", + "Accounts_OAuth_Custom_id": "Id", + "Accounts_OAuth_Custom_Identity_Path": "पहचान पथ", + "Accounts_OAuth_Custom_Identity_Token_Sent_Via": "पहचान टोकन भेजा गया", + "Accounts_OAuth_Custom_Login_Style": "लॉगिन शैली", + "Accounts_OAuth_Custom_Merge_Users": "उपयोगकर्ताओं को मर्ज करें", + "Accounts_OAuth_Custom_Scope": "क्षेत्र", + "Accounts_OAuth_Custom_Secret": "गुप्त", + "Accounts_OAuth_Custom_Token_Path": "टोकन पथ", + "Accounts_OAuth_Custom_Token_Sent_Via": "के जरिए टोकन भेजा गया", + "Accounts_OAuth_Custom_Username_Field": "उपयोगकर्ता नाम फ़ील्ड", + "Accounts_OAuth_Drupal": "Drupal लॉगिन सक्षम है", + "Accounts_OAuth_Drupal_callback_url": "Drupal oAuth2 रीडायरेक्ट URI", + "Accounts_OAuth_Drupal_id": "Drupal oAuth2 क्लाइंट आईडी", + "Accounts_OAuth_Drupal_secret": "Drupal oAuth2 क्लाइंट सीक्रेट", + "Accounts_OAuth_Facebook": "फेसबुक लोगिन", + "Accounts_OAuth_Facebook_callback_url": "फेसबुक कॉलबैक URL", + "Accounts_OAuth_Facebook_id": "फेसबुक ऐप आईडी", + "Accounts_OAuth_Facebook_secret": "फेसबुक सीक्रेट", + "Accounts_OAuth_Github": "OAuth सक्षम", + "Accounts_OAuth_Github_callback_url": "GitHub कॉलबैक यूआरएल", + "Accounts_OAuth_GitHub_Enterprise": "OAuth सक्षम", + "Accounts_OAuth_GitHub_Enterprise_callback_url": "GitHub एंटरप्राइज़ कॉलबैक URL", + "Accounts_OAuth_GitHub_Enterprise_id": "क्लाइंट ID", + "Accounts_OAuth_GitHub_Enterprise_secret": "क्लाइंट Secret", + "Accounts_OAuth_Github_id": "क्लाइंट Id", + "Accounts_OAuth_Github_secret": "क्लाइंट Secret", + "Accounts_OAuth_Gitlab": "OAuth सक्षम", + "Accounts_OAuth_Gitlab_callback_url": "GitLab कॉलबैक URL", + "Accounts_OAuth_Gitlab_id": "Gitlab Id", + "Accounts_OAuth_Gitlab_identity_path": "पहचान पथ", + "Accounts_OAuth_Gitlab_secret": "क्लाइंट Secret", + "Accounts_OAuth_Google": "Google लॉगिन", + "Accounts_OAuth_Google_callback_url": "Google कॉलबैक URL", + "Accounts_OAuth_Google_id": "Google Id", + "Accounts_OAuth_Google_secret": "Google Secret", + "Accounts_OAuth_Linkedin": "LinkedIn लॉगिन", + "Accounts_OAuth_Linkedin_callback_url": "LinkedIn कॉलबैक URL", + "Accounts_OAuth_Linkedin_id": "LinkedIn Id", + "Accounts_OAuth_Linkedin_secret": "LinkedIn Secret", + "Accounts_OAuth_Meteor": "Meteor Login", + "Accounts_OAuth_Meteor_callback_url": "Meteor कॉलबैक URL", + "Accounts_OAuth_Meteor_id": "Meteor Id", + "Accounts_OAuth_Meteor_secret": "Meteor Secret", + "Accounts_OAuth_Nextcloud": "OAuth सक्षम", + "Accounts_OAuth_Nextcloud_secret": "क्लाइंट Secret", + "Accounts_OAuth_Proxy_host": "प्रॉक्सी होस्ट", + "Accounts_OAuth_Proxy_services": "प्रॉक्सी सेवाएँ", + "Accounts_OAuth_Tokenpass": "Tokenpass लॉगइन", + "Accounts_OAuth_Tokenpass_callback_url": "Tokenpass कॉलबैक URL", + "Accounts_OAuth_Tokenpass_id": "Tokenpass Id", + "Accounts_OAuth_Tokenpass_secret": "Tokenpass Secret", + "Accounts_OAuth_Twitter": "ट्विटर लॉगइन", + "Accounts_OAuth_Twitter_callback_url": "ट्विटर कॉलबैक URL", + "Accounts_OAuth_Twitter_id": "ट्विटर Id", + "Accounts_OAuth_Twitter_secret": "ट्विटर Secret", + "Accounts_OAuth_Wordpress": "वर्डप्रेस लॉगिन", + "Accounts_OAuth_Wordpress_authorize_path": "पथ अधिकृत करें", + "Accounts_OAuth_Wordpress_callback_url": "वर्डप्रेस कॉलबैक URL", + "Accounts_OAuth_Wordpress_id": "वर्डप्रेस Id", + "Accounts_OAuth_Wordpress_identity_path": "पहचान पथ", + "Accounts_OAuth_Wordpress_identity_token_sent_via": "के जरिए पहचान टोकन भेजा गया", + "Accounts_OAuth_Wordpress_scope": "क्षेत्र", + "Accounts_OAuth_Wordpress_secret": "वर्डप्रेस Secret", + "Accounts_OAuth_Wordpress_server_type_custom": "कस्टम", + "Accounts_OAuth_Wordpress_server_type_wordpress_com": "Wordpress.com", + "Accounts_OAuth_Wordpress_server_type_wp_oauth_server": "WP OAuth Server Plugin", + "Accounts_OAuth_Wordpress_token_path": "टोकन पथ", + "Accounts_Password_Policy_AtLeastOneLowercase": "कम से कम एक लोअरकेस में", + "Accounts_Password_Policy_AtLeastOneLowercase_Description": "लागू करें कि पासवर्ड में कम से कम एक लोअरकेस वर्ण हो।", + "Accounts_Password_Policy_AtLeastOneNumber": "कम से कम एक नंबर", + "Accounts_Password_Policy_AtLeastOneNumber_Description": "लागू करें कि एक पासवर्ड में कम से कम एक संख्यात्मक चरित्र होता है।", + "Accounts_Password_Policy_AtLeastOneUppercase_Description": "लागू करें कि पासवर्ड में कम से कम एक लोअरकेस वर्ण हो।", + "Accounts_Registration_InviteUrlType_Direct": "सीधा", + "Accounts_RegistrationForm": "पंजीकरण पत्र", + "Accounts_RegistrationForm_Disabled": "उपयोग करने की अनुमति नहीं है", + "Accounts_RegistrationForm_Public": "जनता", + "Accounts_TwoFactorAuthentication_MaxDelta": "soochna", + "Additional_Feedback": "अतिरिक्त प्रतिक्रिया", + "App_status_auto_enabled": "सक्रिय", + "App_status_disabled": "उपयोग करने की अनुमति नहीं है", + "App_status_manually_enabled": "सक्रिय", + "Appearance": "दिखावट", + "Audio_Notifications_Default_Alert": "ऑडियो सूचनाएं डिफ़ॉल्ट चेतावनी", + "Cancel": "रद्द करना", + "Cancel_message_input": "रद्द करना", + "CAS_enabled": "सक्रिय", + "Client_Secret": "क्लाइंट Secret", + "Close_menu": "मेनू बंद करें", + "Custom": "कस्टम", + "Desktop_Notifications_Default_Alert": "डेस्कटॉप सूचनाएं डिफ़ॉल्ट चेतावनी", + "Disabled": "उपयोग करने की अनुमति नहीं है", + "Edit_Status": "स्थिति संपादित करें", + "Email_Notification_Mode_Disabled": "उपयोग करने की अनुमति नहीं है", + "Enable": "सक्षम करें", + "Enabled": "सक्रिय", + "Federation_Matrix_enabled": "सक्रिय", + "Filters": "फिल्टर", + "Home": "होम", + "How_friendly_was_the_chat_agent": "चैट एजेंट कितना दोस्ताना था?", + "How_knowledgeable_was_the_chat_agent": "चैट एजेंट कितना जानकार था?", + "How_responsive_was_the_chat_agent": "चैट एजेंट कितना उत्तरदायी था?", + "How_satisfied_were_you_with_this_chat": "आप इस चैट से कितने संतुष्ट थे?", + "Installation": "स्थापना", + "LDAP_Authentication": "सक्षम करें", + "LDAP_Enable": "सक्षम करें", + "LDAP_User_Search_Scope": "क्षेत्र", + "Livechat_registration_form": "पंजीकरण ", + "Mobile_Push_Notifications_Default_Alert": "मोबाइल सूचनाएं डिफ़ॉल्ट चेतावनी", + "New_messages": "नए संदेश", + "No": "नहीं", + "Options": "विकल्प", + "Outlook_Calendar_Enabled": "सक्रिय", + "Please_answer_survey": "कृपया इस चैट के बारे में त्वरित सर्वेक्षण का उत्तर देने के लिए एक क्षण लें", + "Please_fill_name_and_email": "कृपया नाम और ईमेल भरें", + "Public": "जनता", + "Push_enable": "सक्षम करें", + "Scope": "क्षेत्र", + "Select_a_department": "एक विभाग का चयन करें", + "Select_department": "एक विभाग का चयन करें", + "Send": "भेजना", + "Skip": "छोड़ें", + "Start_Chat": "बातचीत शुरू ", + "Survey": "सर्वेक्षण", + "Survey_instructions": "प्रत्येक प्रश्न को अपनी संतुष्टि के अनुसार रेट करें, 1 मतलब कि आप पूरी तरह से असंतुष्ट हैं और 5 का अर्थ है कि आप पूरी तरह से संतुष्ट हैं।", + "Thank_you_for_your_feedback": "आपकी प्रतिक्रिया के लिए आपका धन्यवाद", + "Type_your_email": "अपना ईमेल टाइप करें", + "Type_your_message": "अपना संदेश टाइप करें", + "Type_your_name": "अपना नाम लिखें", + "Upload_file_question": "दस्तावेज अपलोड करें?", + "User_left": "उपयोगकर्ता छोड़ दिया", + "We_are_offline_Sorry_for_the_inconvenience": "हम ऑफ़लाइन हैं। असुविधा के लिए खेद है।", + "Yes": "हाँ", + "You": "आप" +} \ No newline at end of file diff --git a/packages/i18n/src/locales/hi.i18n.json b/packages/i18n/src/locales/hi.i18n.json index c8d21e6638fc..25ab23420654 100644 --- a/packages/i18n/src/locales/hi.i18n.json +++ b/packages/i18n/src/locales/hi.i18n.json @@ -176,6 +176,7 @@ "Email_Notification_Mode_Disabled": "उपयोग करने की अनुमति नहीं है", "Enable": "सक्षम करें", "Enabled": "सक्रिय", + "Federation_Matrix_enabled": "सक्रिय", "Filters": "फिल्टर", "Home": "होम", "How_friendly_was_the_chat_agent": "चैट एजेंट कितना दोस्ताना था?", @@ -191,6 +192,7 @@ "New_messages": "नए संदेश", "No": "नहीं", "Options": "विकल्प", + "Outlook_Calendar_Enabled": "सक्रिय", "Please_answer_survey": "कृपया इस चैट के बारे में त्वरित सर्वेक्षण का उत्तर देने के लिए एक क्षण लें", "Please_fill_name_and_email": "कृपया नाम और ईमेल भरें", "Public": "जनता", diff --git a/packages/i18n/src/locales/hr.i18n.json b/packages/i18n/src/locales/hr.i18n.json index 4933ef5f94f9..5f3f16e6ed67 100644 --- a/packages/i18n/src/locales/hr.i18n.json +++ b/packages/i18n/src/locales/hr.i18n.json @@ -354,6 +354,7 @@ "Apply": "primijeniti", "Apply_and_refresh_all_clients": "Primjeni i osvježi sve klijente", "Apps": "Apps", + "Apps_context_premium": "Poduzeće", "Apps_Engine_Version": "Verzija Apps Enginea", "Apps_Settings": "Postavke aplikacije", "AutoLinker": "AutoLinker", @@ -1230,6 +1231,7 @@ "FEDERATION_Domain": "Domena", "FEDERATION_Status": "Status", "Retry_Count": "Ponovite račun", + "Federation_Matrix_enabled": "Omogućeno", "Field": "Polje", "Field_removed": "Polje je uklonjeno", "Field_required": "Polje je obavezno", @@ -1590,6 +1592,10 @@ "Layout_Terms_of_Service": "Uvjeti pružanja usluge", "LDAP": "LDAP", "LDAP_Description": "LDAP je hijerarhijska baza podataka koja mnoge tvrtke koriste za pružanje jedinstvene prijave - usluge za dijeljenje jedne lozinke između više web-mjesta i usluga. Za napredne podatke o konfiguraciji i primjere, obratite se našem wiki: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "Šifriranje", + "LDAP_DataSync_BackgroundSync": "Sinkronizacija pozadine", + "LDAP_Server_Type": "Vrsta poslužitelja", + "LDAP_Server_Type_Other": "Ostalo", "LDAP_Authentication": "Omogućiti", "LDAP_Authentication_Password": "Lozinka", "LDAP_Authentication_UserDN": "Korisnički DN", @@ -2800,6 +2806,8 @@ "Visitor_Navigation": "Navigacija Posjetitelja", "Visitor_page_URL": "URL stranice posjetitelja", "Visitor_time_on_site": "Vrijeme posjetitelja na stranici", + "VoIP_Management_Server_Username": "Korisničko ime", + "VoIP_Management_Server_Password": "Lozinka", "Wait_activation_warning": "Prije nego što se prijavite, vaš račun mora ručno aktivirati administrator.", "Warnings": "Upozorenja", "We_are_offline_Sorry_for_the_inconvenience": "Mi smo izvan mreže. Oprostite na neugodnosti.", @@ -2884,6 +2892,7 @@ "registration.component.form.emailOrUsername": "Email or username", "registration.component.form.username": "Korisničko ime", "registration.component.form.name": "Ime", + "registration.component.form.userAlreadyExist": "Korisničko ime već postoji. Pokušajte s drugim korisničkim imenom.", "registration.component.form.emailAlreadyExists": "Email već postoji", "registration.component.form.usernameAlreadyExists": "Korisničko ime već postoji. Pokušajte s drugim korisničkim imenom.", "registration.component.form.invalidEmail": "Uneseni e-mail nije valjan", diff --git a/packages/i18n/src/locales/hu.i18n.json b/packages/i18n/src/locales/hu.i18n.json index 5d724f8d69f9..69afdb75f233 100644 --- a/packages/i18n/src/locales/hu.i18n.json +++ b/packages/i18n/src/locales/hu.i18n.json @@ -490,6 +490,7 @@ "Apply_and_refresh_all_clients": "Alkalmaz és minden ügyfél frissítése", "Apps": "Alkalmazások", "Apps_context_installed": "Telepítve", + "Apps_context_premium": "Vállalati", "Apps_Engine_Version": "Alkalmazások motorjának verziója", "Apps_Essential_Alert": "Ez az alkalmazás elengedhetetlen a következő eseményekhez:", "Apps_Essential_Disclaimer": "A fent felsorolt események megszakadnak, ha ez az alkalmazás le van tiltva. Ha azt szeretné, hogy a Rocket.Chat ezen alkalmazás funkciói nélkül is működjön, akkor el kell távolítania azt.", @@ -3687,6 +3688,7 @@ "Privacy_summary": "Adatvédelem összegzése", "Private": "Személyes", "private": "személyes", + "Private_channels": "Személyes csatornák", "Private_Channel": "Személyes csatorna", "Private_Channels": "Személyes csatornák", "Private_Chats": "Személyes csevegések", @@ -5307,6 +5309,7 @@ "Your_workspace_is_ready": "A munkaterülete használatra kész 🎉", "Zapier": "Zapier", "registration.page.login.errors.wrongCredentials": "A felhasználó nem található vagy hibás a jelszó", + "registration.page.login.errors.invalidEmail": "Érvénytelen e-mail-cím", "registration.page.login.errors.loginBlockedForIp": "A bejelentkezés átmenetileg tiltva lett ennél az IP-címnél", "registration.page.login.errors.loginBlockedForUser": "A bejelentkezés átmenetileg tiltva lett ennél a felhasználónál", "registration.page.login.errors.licenseUserLimitReached": "A felhasználók legnagyobb száma el lett érve.", @@ -5330,6 +5333,8 @@ "registration.component.form.emailOrUsername": "E-mail-cím vagy felhasználónév", "registration.component.form.username": "Felhasználónév", "registration.component.form.name": "Név", + "registration.component.form.createAnAccount": "Fiók létrehozása", + "registration.component.form.userAlreadyExist": "A felhasználónév már létezik. Próbáljon más felhasználónevet.", "registration.component.form.emailAlreadyExists": "Az e-mail cím már létezik", "registration.component.form.usernameAlreadyExists": "Felhasználónév már létezik. Próbálj meg egy másik felhasználónevet.", "registration.component.form.invalidEmail": "Érvénytelen email cím", @@ -5339,9 +5344,11 @@ "registration.component.form.divider": "vagy", "registration.component.form.submit": "Elküldés", "registration.component.form.requiredField": "Ez a mező kötelező", + "registration.component.form.joinYourTeam": "Csatlakozás csapathoz", "registration.component.form.reasonToJoin": "A csatlakozás oka", "registration.component.form.invalidConfirmPass": "A két jelszó nem eggyezik", "registration.component.form.confirmPassword": "Erősítse meg a jelszavát", + "registration.component.form.confirmation": "Megerősítés", "registration.component.form.sendConfirmationEmail": "Megerősítő email elküldése", "onboarding.component.form.requiredField": "Ez a mező kötelező", "onboarding.component.form.steps": "{{currentStep}} / {{stepCount}} lépés", @@ -5402,6 +5409,7 @@ "onboarding.form.organizationInfoForm.fields.organizationSize.placeholder": "Kiválasztás", "onboarding.form.organizationInfoForm.fields.country.label": "Ország", "onboarding.form.organizationInfoForm.fields.country.placeholder": "Kiválasztás", + "onboarding.form.registerOfflineForm.title": "Kapcsolat nélküli regisztráció", "onboarding.form.registeredServerForm.title": "Kiszolgáló regisztrálása", "onboarding.form.registeredServerForm.included.push": "Mobil leküldéses értesítések", "onboarding.form.registeredServerForm.included.externalProviders": "Integráció külső szolgáltatókkal (WhatsApp, Facebook, Telegram, Twitter)", @@ -5419,7 +5427,11 @@ "Theme_dark": "Sötét", "Join_your_team": "Csatlakozás csapathoz", "Create_an_account": "Fiók létrehozása", + "Awaiting_confirmation": "Megerősítésre vár", + "RegisterWorkspace_Features_MobileNotifications_Title": "Mobil leküldéses értesítések", "RegisterWorkspace_Features_Marketplace_Title": "Piactér", + "RegisterWorkspace_Features_Omnichannel_Title": "Összcsatorna", + "cloud.RegisterWorkspace_Setup_Terms_Privacy": "Elfogadom a <1>használati feltételeket és az <3>adatvédelmi irányelveket", "Enterprise": "Vállalati", "UpgradeToGetMore_engagement-dashboard_Title": "Analitika", "UpgradeToGetMore_auditing_Title": "Üzenet ellenőrzés" diff --git a/packages/i18n/src/locales/id.i18n.json b/packages/i18n/src/locales/id.i18n.json index 9c078112ecbc..db105e17538e 100644 --- a/packages/i18n/src/locales/id.i18n.json +++ b/packages/i18n/src/locales/id.i18n.json @@ -320,6 +320,7 @@ "Apply": "Menerapkan", "Apply_and_refresh_all_clients": "Berlaku dan menyegarkan semua klien", "Apps": "Aplikasi", + "Apps_context_premium": "Perusahaan", "Apps_Settings": "Setelan Aplikasi", "Apps_WhatIsIt": "Aplikasi: Apakah Mereka?", "Apps_WhatIsIt_paragraph1": "Ikon baru di area administrasi! Apa artinya ini dan apa itu Apps?", @@ -490,6 +491,7 @@ "Confirm_New_Password_Placeholder": "Silakan masukkan kembali kata sandi baru ...", "Confirm_password": "Konfirmasikan kata sandi anda", "Confirm_your_password": "Konfirmasikan kata sandi anda", + "Connected": "Terhubung", "Connection_Closed": "Koneksi ditutup", "Connection_Reset": "Koneksi diatur ulang", "Consulting": "Konsultasi", @@ -1099,6 +1101,7 @@ "FEDERATION_Domain": "Domain", "FEDERATION_Status": "Status", "Retry_Count": "Coba lagi", + "Federation_Matrix_enabled": "Diaktifkan", "Field": "Bidang", "Field_removed": "bidang dihapus", "Field_required": "Bidang yang dibutuhkan", @@ -1457,6 +1460,10 @@ "Layout_Terms_of_Service": "Kebijakan Layanan", "LDAP": "LDAP", "LDAP_Description": "LDAP adalah basis data hirarki yang banyak digunakan perusahaan untuk menyediakan fasilitas single sing on untuk berbagi satu kata sandi diantara beberapa situs dan services. Untuk informasi dan contoh konfigurasi lebih lanjut, silahkan berkonsultasi di wiki kami: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "enkripsi", + "LDAP_DataSync_BackgroundSync": "Sinkronisasi Latar Belakang", + "LDAP_Server_Type": "Tipe Server", + "LDAP_Server_Type_Other": "Lain", "LDAP_Authentication": "Memungkinkan", "LDAP_Authentication_Password": "Kata sandi", "LDAP_Authentication_UserDN": "Pengguna DN", @@ -2672,6 +2679,8 @@ "Visitor_Navigation": "Navigasi pengunjung", "Visitor_page_URL": "URL halaman Pengunjung", "Visitor_time_on_site": "waktu pengunjung di situs", + "VoIP_Management_Server_Username": "Nama pengguna", + "VoIP_Management_Server_Password": "Kata sandi", "Wait_activation_warning": "Akun anda harus terlebih dahulu diaktifkan secara manual oleh seorang administrator untuk dapat melakukan login.", "Warnings": "Peringatan", "We_are_offline_Sorry_for_the_inconvenience": "Kami sedang offline. Maaf untuk ketidaknyamanannya.", @@ -2756,6 +2765,7 @@ "registration.component.form.emailOrUsername": "Email atau username", "registration.component.form.username": "Nama pengguna", "registration.component.form.name": "Nama", + "registration.component.form.userAlreadyExist": "Nama pengguna sudah ada. Silakan coba nama pengguna lain", "registration.component.form.emailAlreadyExists": "Email sudah terdaftar", "registration.component.form.usernameAlreadyExists": "Nama pengguna sudah ada. Silakan coba nama pengguna lain", "registration.component.form.invalidEmail": "Email yang anda masukkan tidak valid", diff --git a/packages/i18n/src/locales/it.i18n.json b/packages/i18n/src/locales/it.i18n.json index 737acbf3b847..79dcaded9d78 100644 --- a/packages/i18n/src/locales/it.i18n.json +++ b/packages/i18n/src/locales/it.i18n.json @@ -13,8 +13,8 @@ "__usersCount__member_joined_other": "+ {{count}} membri si sono uniti", "__usersCount__people_will_be_invited": "{{usersCount}} persone saranno invitate", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} non è più {{role}}, da {{user_by}}", - "__usersCount__member_joined_many": "+ {{count}} membri si sono uniti", "__username__was_set__role__by__user_by_": "A {{username}} è stato assegnato il ruolo di {{role}} da {{user_by}}", + "__usersCount__member_joined_many": "+ {{count}} membri si sono uniti", "__count__without__department__": "{{count}} senza reparto", "__count__without__tags__": "{{count}} senza tag", "__count__without__assignee__": "{{count}} non assegnate", @@ -320,7 +320,9 @@ "Allow_Invalid_SelfSigned_Certs": "Consenti i certificati SSL auto-firmati", "Allow_Invalid_SelfSigned_Certs_Description": "Consenti l'uso di certificati SSL non validi e auto-firmati per la validazione dei link e le anteprime.", "Allow_Marketing_Emails": "Consenti email di marketing", + "Apps_Count_Enabled_many": "{{count}} app abilitate", "Allow_switching_departments": "Consenti ai visitatori di cambiare dipartimento", + "Private_Apps_Count_Enabled_many": "{{count}} applicazioni private abilitate", "Almost_done": "Quasi fatto", "Alphabetical": "Alfabetico", "bold": "grassetto", @@ -411,10 +413,8 @@ "Apps_context_premium": "Premium", "Apps_Count_Enabled_one": "{{count}} app abilitate", "Apps_Count_Enabled_other": "{{count}} app abilitate", - "Apps_Count_Enabled_many": "{{count}} app abilitate", "Private_Apps_Count_Enabled_one": "{{count}} applicazione private abilitate", "Private_Apps_Count_Enabled_other": "{{count}} applicazioni private abilitate", - "Private_Apps_Count_Enabled_many": "{{count}} applicazioni private abilitate", "Apps_Count_Enabled_tooltip": "Gli spazi di lavoro comunitari possono abilitare fino a {{number}} applicazioni {{context}}", "Apps_disabled_when_Premium_trial_ended": "App disabilitate al termine della prova del piano Premium", "Apps_Game_Center": "Game Center", @@ -1322,6 +1322,7 @@ "error-failed-to-delete-department": "Eliminazione del reparto fallita", "error-field-unavailable": "{{field}} è già in uso :(", "error-file-too-large": "Il file è troppo grande", + "meteor_status_reconnect_in_many": "riprovo tra {{count}} secondi...", "error-importer-not-defined": "L'importatore non è stato definito correttamente, manca la classe di importazione.", "error-input-is-not-a-valid-field": "{{input}} non è una valido {{field}}", "error-invalid-actionlink": "Link d'azione non valido", @@ -1453,6 +1454,7 @@ "FEDERATION_Public_Key": "Chiave pubblica", "FEDERATION_Status": "Stato", "Retry_Count": "Conteggio tentativi", + "Federation_Matrix_enabled": "Abilitato", "Field": "Campo", "Field_removed": "Campo rimosso", "Field_required": "Campo richiesto", @@ -1568,7 +1570,6 @@ "get-password-policy-mustContainAtLeastOneUppercase-label": "Almeno una lettera maiuscola", "get-password-policy-mustContainAtLeastOneNumber-label": "Almeno un numero", "get-password-policy-mustContainAtLeastOneSpecialCharacter-label": "Almeno un simbolo", - "meteor_status_reconnect_in_many": "riprovo tra {{count}} secondi...", "github_no_public_email": "Non hai un email publica sul tuo account GitHub", "github_HEAD": "HEAD", "Give_a_unique_name_for_the_custom_oauth": "Dai un nome univoco per l'OAuth personalizzato", @@ -1803,6 +1804,7 @@ "Invite_user_to_join_channel_all_from": "Invita tutti gli utenti da [#channell] per unirsi a questo canale", "Invite_user_to_join_channel_all_to": "Invita tutti gli utenti di questo canale a unirsi [#channel]", "Invite_Users": "Invita utenti", + "IP": "IP", "IP_Address": "Indirizzo IP", "IRC_Channel_Join": "Output del comando JOIN", "IRC_Channel_Leave": "Output del comando PART", @@ -1918,7 +1920,10 @@ "LDAP_DataSync_Avatar": "Avatar", "LDAP_DataSync_Advanced": "Sincronizzazione avanzata", "LDAP_Enterprise": "Premium", + "LDAP_DataSync_BackgroundSync": "Sincronizzazione di sfondo", + "LDAP_Server_Type": "Tipo di server", "LDAP_Server_Type_Other": "Altro", + "LDAP_Advanced_Sync": "Sincronizzazione avanzata", "LDAP_Authentication": "Permettere", "LDAP_Authentication_Password": "Password", "LDAP_Authentication_UserDN": "DN utente", @@ -2290,6 +2295,7 @@ "Normal": "Normale", "Not_Available": "Non disponibile", "Not_found_or_not_allowed": "Non trovato o Non Permesso", + "Not_started": "Non iniziato/a", "Not_Visible_To_Workspace": "Non visibile nell'area di lavoro", "Nothing": "Niente", "Nothing_found": "Non abbiamo trovato nulla", @@ -2353,6 +2359,7 @@ "Organization_Name": "Nome dell'organizzazione", "Organization_Type": "Tipo di Organizzazione", "Original": "Originale", + "OS": "OS", "OS_Arch": "Architettura", "OS_Cpus": "Numero CPU", "OS_Freemem": "Memoria Libera", @@ -2599,6 +2606,7 @@ "RetentionPolicyRoom_OverrideGlobal": "Sostituisci la politica di conservazione globale", "RetentionPolicyRoom_ReadTheDocs": "Attenzione! Modificare queste impostazioni senza la massima cura può causare la distruzzione di tutta la cronologia dei messaggi. Leggere la documentazione qui prima di attivare la funzione.", "Role": "Ruolo", + "Roles": "Ruoli", "Role_Editing": "Ruolo Editing", "Role_removed": "Ruolo rimosso", "Room": "Canale", @@ -2657,6 +2665,7 @@ "SAML_Custom_user_data_fieldmap": "Campo Utente Dati Mappa", "SAML_Section_1_User_Interface": "Interfaccia utente", "SAML_Section_4_Roles": "Ruoli", + "SAML_Section_5_Mapping": "Mappatura", "Saturday": "Sabato", "Save": "Salva", "Save_changes": "Salva le modifiche", @@ -2981,6 +2990,7 @@ "Threads": "Thread", "Thursday": "Giovedì", "Time_in_seconds": "Tempo in secondi", + "Timeouts": "Timeout", "Timezone": "Fuso orario", "Title": "Titolo", "Title_bar_color": "Colore della Barra del Titolo", @@ -3097,6 +3107,7 @@ "Use_User_Preferences_or_Global_Settings": "Usa le Preferenze Utente o Globali", "User": "Utente", "User_menu": "Menu utente", + "User Search": "Ricerca utente", "User__username__is_now_a_leader_of__room_name_": "L'utente {{username}} è ora un leader di {{room_name}}", "User__username__is_now_a_moderator_of__room_name_": "L'Utente {{username}} è ora un moderatore di {{room_name}}", "User__username__is_now_an_owner_of__room_name_": "L'Utente {{username}} è ora proprietario di {{room_name}}", @@ -3251,6 +3262,8 @@ "Visitor_Navigation": "Navigazione Visitatori", "Visitor_page_URL": "URL Pagina Visitatori", "Visitor_time_on_site": "Tempo dei visitatori sul sito", + "VoIP_Management_Server_Username": "Nome utente", + "VoIP_Management_Server_Password": "Password", "Wait_activation_warning": "Prima di poter effettuare il login, il tuo account deve essere attivato manualmente da un amministratore.", "Warnings": "Avvisi", "We_are_offline_Sorry_for_the_inconvenience": "Non siamo in linea. Ci dispiace per l'inconveniente.", @@ -3335,6 +3348,7 @@ "Your_server_link": "Il tuo collegamento al server", "Your_workspace_is_ready": "Il tuo spazio di lavoro è pronto per l'uso 🎉", "registration.page.login.errors.wrongCredentials": "Utente non trovato o password sbagliata", + "registration.page.login.errors.AppUserNotAllowedToLogin": "Gli utenti dell'app non possono accedere direttamente.", "registration.page.registration.waitActivationWarning": "Prima di poter effettuare il login, il tuo account deve essere attivato manualmente da un amministratore.", "registration.page.login.forgot": "Password dimenticata", "registration.page.resetPassword.sent": "Se questa email è registrata, invieremo istruzioni su come reimpostare la propria password. Se non la ricevi in breve tempo, si prega di ritornare e riprovare.", @@ -3347,6 +3361,7 @@ "registration.component.form.emailOrUsername": "Email o nome utente", "registration.component.form.username": "Nome utente", "registration.component.form.name": "Nome", + "registration.component.form.userAlreadyExist": "Il nome utente esiste già. Si prega di provare un altro nome utente.", "registration.component.form.emailAlreadyExists": "Email già esistente", "registration.component.form.usernameAlreadyExists": "Il nome utente esiste già. Si prega di provare un altro nome utente.", "registration.component.form.invalidEmail": "L'email inserita non è valida", @@ -3359,6 +3374,7 @@ "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", + "onboarding.form.awaitConfirmationForm.content.securityCode": "Codice di sicurezza", "subscription.callout.allPremiumCapabilitiesDisabled": "Tutte le funzionalità premium disattivate", "subscription.callout.privateApps": "applicazioni private installate", "subscription.callout.marketplaceApps": "applicazioni del marketplace installate", @@ -3384,6 +3400,7 @@ "RegisterWorkspace_Features_ThirdPartyLogin_Disconnect": "Le opzioni di login di terze parti non saranno più disponibili.", "RegisterWorkspace_Syncing_Complete": "Sincronizzazione completata", "RegisterWorkspace_Connection_Error": "Si è verificato un errore di connessione", + "Premium": "Premium", "Enterprise": "impresa", "Operating_withing_plan_limits": "Attualmente entro i limiti del piano attivo", "Workspace_not_registered": "Spazio di lavoro non registrato", diff --git a/packages/i18n/src/locales/ja.i18n.json b/packages/i18n/src/locales/ja.i18n.json index 4aca5b5dec57..2e50c926f7bb 100644 --- a/packages/i18n/src/locales/ja.i18n.json +++ b/packages/i18n/src/locales/ja.i18n.json @@ -449,6 +449,7 @@ "Apply_and_refresh_all_clients": "すべてのクライアントを適用して更新", "Apps": "アプリ", "Apps_context_installed": "インストール済み", + "Apps_context_premium": "エンタープライズ", "Apps_Engine_Version": "アプリエンジンのバージョン", "Apps_Essential_Alert": "このアプリは次のイベントに不可欠です:", "Apps_Essential_Disclaimer": "このアプリが無効になっていると、上記のイベントが中断されます。このアプリの機能を使用せずにRocket.Chatを動作させたい場合は、アンインストールする必要があります。", @@ -1362,6 +1363,7 @@ "Desktop_Notifications_Not_Enabled": "デスクトップ通知が有効ではありません", "Details": "詳細", "line": "ライン", + "Device_Management_IP": "IP", "Different_Style_For_User_Mentions": "ユーザーメンションには異なるスタイル", "Livechat_Facebook_API_Key": "オムニChannelAPIキー", "Livechat_Facebook_API_Secret": "オムニChannelAPIシークレット", @@ -1846,6 +1848,7 @@ "FEDERATION_Test_Setup_Error": "設定を使用しているサーバーが見つかりませんでした。設定を確認してください。", "FEDERATION_Test_Setup_Success": "フェデレーション設定は正常に機能しており、他のサーバーがあなたを見つけることができます!", "Retry_Count": "再試行回数", + "Federation_Matrix_enabled": "有効", "Field": "フィールド", "Field_removed": "フィールドが削除されました", "Field_required": "必要なフィールド", @@ -3308,6 +3311,7 @@ "Privacy": "プライバシー", "Privacy_Policy": "プライバシーポリシー", "Private": "プライベート", + "Private_channels": "プライベートChannel", "Private_Channel": "プライベートChannel", "Private_Channels": "プライベートChannel", "Private_Chats": "プライベートチャット", @@ -3543,6 +3547,7 @@ "Unsafe_Url": "安全でないURL", "Rocket_Chat_Alert": "Rocket.Chatアラート", "Role": "ロール", + "Roles": "ロール", "Role_Editing": "ロールの編集", "Role_Mapping": "ロールマッピング", "Role_removed": "ロールを削除しました", @@ -4738,6 +4743,7 @@ "registration.component.form.emailOrUsername": "メールアドレスまたはユーザー名", "registration.component.form.username": "ユーザー名", "registration.component.form.name": "名前", + "registration.component.form.userAlreadyExist": "ユーザー名はすでに存在します。別のユーザー名をお試しください。", "registration.component.form.emailAlreadyExists": "メールはすでに存在します", "registration.component.form.usernameAlreadyExists": "ユーザー名はすでに存在します。別のユーザー名をお試しください。", "registration.component.form.invalidEmail": "入力されたメールアドレスは無効です", diff --git a/packages/i18n/src/locales/ka-GE.i18n.json b/packages/i18n/src/locales/ka-GE.i18n.json index 5b775db06eb6..7fab42bd2f14 100644 --- a/packages/i18n/src/locales/ka-GE.i18n.json +++ b/packages/i18n/src/locales/ka-GE.i18n.json @@ -694,6 +694,7 @@ "Confirm_password": "დაადასტურეთ თქვენი პაროლი", "Confirm_your_password": "დაადასტურეთ თქვენი პაროლი", "Connect": "დაკავშირება", + "Connected": "დაკავშირებულია", "Connection_Closed": "კავშირი დაიხურა", "Connection_Reset": "კავშირის გადატვირთვა", "Connectivity_Services": "დაკავშირების სერვისები", @@ -1495,6 +1496,7 @@ "FEDERATION_Test_Setup_Error": "ვერ ვიპოვნეთ თქვენი სერვერი თქვენი კონფიგურაციის გამოყენებით, გადახედეთ თქვენს პარამეტრებს.", "FEDERATION_Test_Setup_Success": "თქვენი ფედერაციის კონფიგურაცია მუშაობს და სხვა სერვერებს შეუძლიათ თქვენი პოვნა", "Retry_Count": "ხელახლა დათვლა", + "Federation_Matrix_enabled": "ჩართული", "Field": "ველი", "Field_removed": "ველი ამოღებულია", "Field_required": "ველი მოითხოვება", @@ -1952,6 +1954,13 @@ "Layout_Terms_of_Service": "მომსახურების პირობები", "LDAP": "LDAP", "LDAP_Description": "LDAP არის იერარქიული მონაცემთა ბაზა, რომელსაც მრავალი კომპანია იყენებს ერთი ავტორიზაციის შესაქმნელად - ერთი პაროლი სხვადასხვა მომსახურება და საიტებისთვის. კონფიგურაციის შესახებ ინფორმაციის და მაგალითებისთვის მიმართეთ ჩვენს ვიკებს: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "დაშიფვრა", + "LDAP_Connection_Timeouts": "თაიმაუტები", + "LDAP_UserSearch": "მომხმარებლის ძებნა", + "LDAP_DataSync_Advanced": "დამატებითი სინქრონიზაცია", + "LDAP_DataSync_BackgroundSync": "ფონის სინქრონიზაცია", + "LDAP_Server_Type": "სერვერის ტიპი", + "LDAP_Server_Type_Other": "სხვა", "LDAP_Advanced_Sync": "დამატებითი სინქრონიზაცია", "LDAP_Authentication": "ჩართვა", "LDAP_Authentication_Password": "პაროლი", @@ -2632,6 +2641,7 @@ "Privacy": "კონფიდენციალურობა", "Privacy_Policy": "კონფიდენციალურობის წესები", "Private": "პირადი", + "Private_channels": "პირადი არხები", "Private_Channel": "პირადი არხი", "Private_Channels": "პირადი არხები", "Private_Chats": "პირადი ჩეთები", @@ -3333,6 +3343,7 @@ "UI_Use_Name_Avatar": "სახელის სრული ინიციალების გამოყენება ავატარის გენერაციისთვის", "UI_Use_Real_Name": "გამოიყენეთ ნამდვილი სახელი", "unable-to-get-file": "ფაილის მიღება შეუძლებელია", + "unauthorized": "არაა უფლებამოსილი", "Unavailable": "მიუწვდომელია", "Unblock_User": "მომხმარებლის განბლოკვა", "Uncheck_All": "ყველა მონიშვნის მოხსნა", @@ -3558,6 +3569,8 @@ "Visitor_Navigation": "ვიზიტორთა ნავიგაცია", "Visitor_page_URL": "ვიზიტორის გვერდის URL", "Visitor_time_on_site": "ვიზიტის დრო საიტზე", + "VoIP_Management_Server_Username": "მომხმარებლის სახელი", + "VoIP_Management_Server_Password": "პაროლი", "Wait_activation_warning": "სანამ შეხვალთ, თქვენი ანგარიში ხელით უნდა გააქტიურდეს ადმინისტრატორის მიერ.", "Waiting_queue": "რიგის მოლოდინში", "Waiting_queue_message": "რიგის შეტყობინების მოლოდინი", @@ -3644,6 +3657,7 @@ "Your_server_link": "თქვენი სერვერის მისამართი", "Your_temporary_password_is_password": "თქვენი დროებითი პაროლია არის [password]", "Your_workspace_is_ready": "თქვენი სამუშაო გარემო მზად არის სამუშაოდ 🎉", + "registration.page.login.errors.wrongCredentials": "მომხმარებელი ვერ მოიძებნა ან პაროლია არასწორი ", "registration.page.login.errors.AppUserNotAllowedToLogin": "აპლიკაციის მომხამრებლებს არ აქვთ უფლება პირდაპირ შემოვიდნენ", "registration.page.registration.waitActivationWarning": "სანამ შეხვალთ, თქვენი ანგარიში ხელით უნდა გააქტიურდეს ადმინისტრატორის მიერ.", "registration.page.login.forgot": "დაგავიწყდათ პაროლი?", @@ -3654,6 +3668,7 @@ "registration.component.form.emailOrUsername": "ელ.ფოსტის ან მომხმარებლის სახელი", "registration.component.form.username": "მომხმარებლის სახელი", "registration.component.form.name": "სახელი", + "registration.component.form.userAlreadyExist": "სახელი უკვე არსებობს. გთხოვთ, სცადოთ სხვა სახელი.", "registration.component.form.emailAlreadyExists": "იმეილი უკვე არსებობს", "registration.component.form.usernameAlreadyExists": "სახელი უკვე არსებობს. გთხოვთ, სცადოთ სხვა სახელი.", "registration.component.form.invalidEmail": "შეყვანილი ელ.ფოსტა არასწორია", diff --git a/packages/i18n/src/locales/km.i18n.json b/packages/i18n/src/locales/km.i18n.json index fcaf5e71b14a..116fbfc12d6a 100644 --- a/packages/i18n/src/locales/km.i18n.json +++ b/packages/i18n/src/locales/km.i18n.json @@ -383,6 +383,7 @@ "Apply": "អនុវត្ត", "Apply_and_refresh_all_clients": "អនុវត្តនិងធ្វើឱ្យអតិថិជនទាំងអស់", "Apps": "កម្មវិធី", + "Apps_context_premium": "សហគ្រាស", "Apps_Engine_Version": "កំណែកម្មវិធីម៉ាស៊ីន", "Apps_Marketplace_Deactivate_App_Prompt": "តើអ្នកពិតជាចង់បិទកម្មវិធីនេះមែនទេ?", "Apps_Marketplace_Login_Required_Description": "ការទិញកម្មវិធីពីផ្សាររ៉ក់ខេត។ តំរូវអោយចុះឈ្មោះកន្លែងធ្វើការរបស់អ្នកហើយចូល។", @@ -1341,6 +1342,7 @@ "FEDERATION_Domain": "ដែន", "FEDERATION_Status": "ស្ថានភាព", "Retry_Count": "ព្យាយាមរាប់ឡើងវិញ", + "Federation_Matrix_enabled": "បានបើក", "Field": "វាល", "Field_removed": "វាលបានយកចេញ", "Field_required": "ត្រូវការវាល", @@ -1732,6 +1734,12 @@ "Layout_Sidenav_Footer_Dark_description": "​ទំហំ​បាន​គឺ 260x70", "Layout_Terms_of_Service": "ល័ក្ខខ័ណ្ឌ​នៃ​សេវាកម្ម", "LDAP": "ប្រើ LDAP", + "LDAP_Connection_Encryption": "ការអ៊ីនគ្រីប", + "LDAP_Connection_Timeouts": "អស់ពេល", + "LDAP_UserSearch": "ស្វែងរកអ្នកប្រើ", + "LDAP_DataSync_BackgroundSync": "ធ្វើសមកាលកម្មផ្ទៃខាងក្រោយ", + "LDAP_Server_Type": "ប្រភេទម៉ាស៊ីនបម្រើ", + "LDAP_Server_Type_Other": "ផ្សេងទៀត", "LDAP_Authentication": "អនុញ្ញាត", "LDAP_Authentication_Password": "ពាក្យសម្ងាត់", "LDAP_Authentication_UserDN": "អ្នកប្រើ DN", @@ -2301,6 +2309,7 @@ "Reactions": "ប្រតិកម្ម", "Read_by": "អានដោយ", "Read_only": "អាច​បាន​តែ​អាន", + "This_room_is_read_only": "បន្ទប់នេះត្រូវបានអានតែប៉ុណ្ណោះ", "Read_only_changed_successfully": "បានតែអានបានប្ដូរដោយជោគជ័យ", "Read_only_channel": "អានតែឆានែល", "Read_only_group": "អានតែក្រុម", @@ -3014,6 +3023,8 @@ "Visitor_Navigation": "ចំនួនអ្នកទស្សនារុករក", "Visitor_page_URL": "URL របស់ទំព័រអ្នកទស្សនា", "Visitor_time_on_site": "ពេលដែលអ្នកទស្សនានៅលើគេហទំព័រ", + "VoIP_Management_Server_Username": "ឈ្មោះ​អ្នកប្រើប្រាស់", + "VoIP_Management_Server_Password": "ពាក្យសម្ងាត់", "Wait_activation_warning": "មុន​ពេល​ចូល​ប្រើប្រាស់​គណនី​អ្នក​ត្រូវ​តែ​ទទួល​បាន​ការ​អនុញ្ញាតិ​ពី​អ្នក​គ្រប់​គ្រង​ជាមុន​សិន​", "Warnings": "ការព្រមាន", "We_are_offline_Sorry_for_the_inconvenience": "យើងមាននៅក្រៅបណ្ដាញ។ សូមអភ័យទោសចំពោះការរំខាននេះ។", @@ -3102,6 +3113,8 @@ "registration.component.login.userNotFound": "រកមិនឃើញអ្នកប្រើ", "registration.component.resetPassword": "កំណត់​ពាក្យ​សម្ងាត់​ឡើងវិញ", "registration.component.form.username": "ឈ្មោះ​អ្នកប្រើប្រាស់", + "registration.component.form.name": "Name", + "registration.component.form.userAlreadyExist": "ឈ្មោះ​របស់​អ្នកប្រើប្រាស់​ធ្លាប់​មាន​ហើយ។ សូមសាកល្បងឈ្មោះអ្នកប្រើប្រាស់ផ្សេងទៀត។", "registration.component.form.emailAlreadyExists": "អ៊ី​ម៉ែ​ល​ដែល​មាន​រួច​ហើយ", "registration.component.form.usernameAlreadyExists": "ឈ្មោះ​របស់​អ្នកប្រើប្រាស់​ធ្លាប់​មាន​ហើយ។ សូមសាកល្បងឈ្មោះអ្នកប្រើប្រាស់ផ្សេងទៀត។", "registration.component.form.invalidEmail": "អ៊ី​មែល​ដែល​បញ្ចូល​មិន​ត្រឹម​ត្រូវ", diff --git a/packages/i18n/src/locales/ko.i18n.json b/packages/i18n/src/locales/ko.i18n.json index fe7eb9b0177d..468da203f883 100644 --- a/packages/i18n/src/locales/ko.i18n.json +++ b/packages/i18n/src/locales/ko.i18n.json @@ -428,6 +428,7 @@ "Apply_and_refresh_all_clients": "적용하고 모든 클라이언트를 새로 고침", "Apps": "Apps", "Apps_context_installed": "설치됨", + "Apps_context_premium": "기업", "Apps_Engine_Version": "앱 엔진 버전", "Apps_Essential_Alert": "이 앱은 다음과 같은 이벤트에 필수적입니다.", "Apps_Essential_Disclaimer": "이 앱을 사용 중지하면 위에 나열된 이벤트가 중단됩니다. 이 앱의 기능없이 작동하려면 Rocket.Chat을 제거해야합니다.", @@ -793,6 +794,7 @@ "Confirm_password": "비밀번호를 확인하세요", "Confirm_your_password": "비밀번호를 확인하세요", "Connect": "연결", + "Connected": "연결됨", "Connection_Closed": "연결이 닫혔습니다.", "Connection_Reset": "연결 재설정", "Connectivity_Services": "연결 서비스", @@ -1630,6 +1632,7 @@ "FEDERATION_Test_Setup_Error": "설정을 사용하여 서버를 찾을 수 없습니다. 설정을 검토하십시오.", "FEDERATION_Test_Setup_Success": "Federation 설정이 작동중이고, 다른 서버에서도 찾을 수 있습니다!", "Retry_Count": "다시 시도 횟수", + "Federation_Matrix_enabled": "활성화", "Field": "필드", "Field_removed": "필드 지우기", "Field_required": "필요한 필드", @@ -2112,7 +2115,15 @@ "Layout_Sidenav_Footer_Dark_description": "바닥글 크기는 260x70 입니다.", "Layout_Terms_of_Service": "이용약관", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "암호화", "LDAP_Connection_successful": "LDAP 연결 성공", + "LDAP_Connection_Timeouts": "시간 초과", + "LDAP_UserSearch": "사용자 검색", + "LDAP_DataSync_DataMap": "매핑", + "LDAP_DataSync_Advanced": "고급 동기화", + "LDAP_DataSync_BackgroundSync": "백그라운드 동기화", + "LDAP_Server_Type": "서버 유형", + "LDAP_Server_Type_Other": "기타", "LDAP_Advanced_Sync": "고급 동기화", "LDAP_Authentication": "사용", "LDAP_Authentication_Password": "비밀번호", @@ -2854,6 +2865,7 @@ "Privacy": "개인정보", "Privacy_Policy": "개인정보취급방침", "Private": "비공개", + "Private_channels": "비공개 Channel", "Private_Channel": "비공개 대화방", "Private_Channels": "비공개 Channel", "Private_Chats": "비공개 대화", @@ -2923,6 +2935,7 @@ "Reactions": "반응", "Read_by": "읽기", "Read_only": "읽기 전용", + "This_room_is_read_only": "이 대화방은 읽기 전용입니다.", "Read_only_changed_successfully": "읽기 전용 변경 완료", "Read_only_channel": "읽기 전용 Channel", "Read_only_group": "읽기 전용 비공개 대화방", @@ -3047,6 +3060,7 @@ "Robot_Instructions_File_Content": "Robots.txt 파일 내용", "Rocket_Chat_Alert": "Rocket.Chat 알림", "Role": "역할", + "Roles": "역할", "Role_Editing": "역할 편집", "Role_Mapping": "역할 매핑", "Role_removed": "역할이 제거되었습니다.", @@ -3887,6 +3901,8 @@ "Visitor_Navigation": "방문자 탐색", "Visitor_page_URL": "방문자 페이지 URL", "Visitor_time_on_site": "방문자 체류 시간", + "VoIP_Management_Server_Username": "사용자명", + "VoIP_Management_Server_Password": "비밀번호", "Wait_activation_warning": "관리자가 계정을 수동으로 활성화시켜야 사용할 수 있습니다.", "Waiting_queue": "지연 대기열", "Waiting_queue_message": "지연 대기열 메시지", @@ -4009,6 +4025,7 @@ "registration.component.resetPassword": "비밀번호 초기화", "registration.component.form.username": "사용자명", "registration.component.form.name": "이름", + "registration.component.form.userAlreadyExist": "사용자명이 이미 존재합니다. 다른 사용자명을 입력해보세요.", "registration.component.form.emailAlreadyExists": "이메일이 이미 있습니다.", "registration.component.form.usernameAlreadyExists": "사용자명이 이미 존재합니다. 다른 사용자명을 입력해보세요.", "registration.component.form.invalidEmail": "입력한 이메일이 잘못되었습니다.", diff --git a/packages/i18n/src/locales/ku.i18n.json b/packages/i18n/src/locales/ku.i18n.json index b82aff287a0f..b5b69caa0143 100644 --- a/packages/i18n/src/locales/ku.i18n.json +++ b/packages/i18n/src/locales/ku.i18n.json @@ -319,6 +319,7 @@ "Apply": "Bikaranîn", "Apply_and_refresh_all_clients": "Apply û refresh hemû mişterîyên", "Apps": "Apps", + "Apps_context_premium": "Enterprise", "Apps_Settings": "Mîhengên App App", "Apps_WhatIsIt": "Apps: Ew çi ye?", "Apps_WhatIsIt_paragraph1": "Mîhengek nû ya di rêveberiya îdarî de! Ev çi tê wateya û çi ye?", @@ -1096,6 +1097,7 @@ "FEDERATION_Domain": "Domain", "FEDERATION_Status": "Cî", "Retry_Count": "Vebijêrk", + "Federation_Matrix_enabled": "çalake", "Field": "Erd", "Field_removed": "meydanê", "Field_required": "Zevî hewce ye", @@ -1452,6 +1454,10 @@ "Layout_Sidenav_Footer_Dark_description": "size footer e 260 x 70px", "Layout_Terms_of_Service": "Terms of Service", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "Şîfrekirin,", + "LDAP_DataSync_BackgroundSync": "Hevpeywendiya paşde", + "LDAP_Server_Type": "Server Type", + "LDAP_Server_Type_Other": "Yên din", "LDAP_Authentication": "Bikêrkirin", "LDAP_Authentication_Password": "Şîfre", "LDAP_Authentication_UserDN": "DN bikarhêner", @@ -1982,6 +1988,7 @@ "Reactions": "reaksiyonên", "Read_by": "Pîroz bixwînin", "Read_only": "Tenê bixwînin", + "This_room_is_read_only": "Ev odeyê tenê xwendinê ye", "Read_only_changed_successfully": "Tenê bi serkeftî veguherîn", "Read_only_channel": "Tenê Channel Only Read", "Read_only_group": "Tenê Komê Hilbijartin", @@ -2656,6 +2663,8 @@ "Visitor_Navigation": "Navigation Mêvan", "Visitor_page_URL": "URL rûpel Mêvan", "Visitor_time_on_site": "dem Mêvan li ser malpera", + "VoIP_Management_Server_Username": "Navê bikarhêner", + "VoIP_Management_Server_Password": "Şîfre", "Wait_activation_warning": "Berî ku hun nikarim têkevim, hesabê xwe divê bi destan an administrator aktîfkirin.", "Warnings": "Hişyar", "We_are_offline_Sorry_for_the_inconvenience": "Em ne offline. Mixabin ji ber nerihetîya.", @@ -2738,6 +2747,7 @@ "registration.component.resetPassword": "تێپەڕەوشە دابنێرەوە", "registration.component.form.username": "Navê bikarhêner", "registration.component.form.name": "ناو", + "registration.component.form.userAlreadyExist": "Ev nav tê bikaranîn. Ji kerema xwe bikarhênerek din bixwazin.", "registration.component.form.emailAlreadyExists": "Email jixwe heye", "registration.component.form.usernameAlreadyExists": "Ev nav tê bikaranîn. Ji kerema xwe bikarhênerek din bixwazin.", "registration.component.form.invalidEmail": "ئیمەیڵی نوسراو هەڵەیە.", diff --git a/packages/i18n/src/locales/lo.i18n.json b/packages/i18n/src/locales/lo.i18n.json index 4fc5eeb0c766..7bcd89dc402a 100644 --- a/packages/i18n/src/locales/lo.i18n.json +++ b/packages/i18n/src/locales/lo.i18n.json @@ -330,6 +330,7 @@ "Apply": "ນໍາໃຊ້", "Apply_and_refresh_all_clients": "ສະຫມັກຕໍາແລະໂຫຼດຫນ້າຈໍຄືນລູກຄ້າທັງຫມົດ", "Apps": "Apps", + "Apps_context_premium": "Enterprise", "Apps_Settings": "ການຕັ້ງຄ່າແອັບຯ", "Apps_WhatIsIt": "ແອັບພລິເຄຊັນ: ພວກເຂົາແມ່ນຫຍັງ?", "Apps_WhatIsIt_paragraph1": "ໄອຄອນໃຫມ່ໃນພື້ນທີ່ບໍລິຫານ! ນີ້ຫມາຍຄວາມວ່າແນວໃດແລະສິ່ງທີ່ເປັນແອັບຯ?", @@ -1133,6 +1134,7 @@ "FEDERATION_Domain": "ໂດເມນ", "FEDERATION_Status": "ສະຖານະພາບ", "Retry_Count": "Retry Count", + "Federation_Matrix_enabled": "ເປີດການໃຊ້ງານ", "Field": "ພາກສະຫນາມ", "Field_removed": "ພາກສະຫນາມອອກ", "Field_required": "ຕ້ອງການພາກສະຫນາມ", @@ -1494,6 +1496,11 @@ "Layout_Terms_of_Service": "ເງື່ອນໄຂການໃຫ້ບໍລິການ", "LDAP": "LDAP", "LDAP_Description": "LDAP ເປັນຖານຂໍ້ມູນລໍາດັບຊັ້ນທີ່ຫຼາຍບໍລິສັດນໍາໃຊ້ເພື່ອສະຫນອງການເຂົ້າສູ່ລະບົບໃນ - ສະຖານທີ່ສໍາລັບການແລກປ່ຽນລະຫັດຜ່ານຫນຶ່ງລະຫວ່າງສະຖານທີ່ແລະບໍລິການທີ່ຫຼາກຫຼາຍ. ສໍາລັບຂໍ້ມູນການຕັ້ງຄ່າຂັ້ນສູງແລະຕົວຢ່າງ, ກະລຸນາປືກສາຫາລື wiki ຂອງພວກເຮົາ: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "ການເຂົ້າລະຫັດ", + "LDAP_DataSync_Advanced": "ຊິງຂັ້ນສູງ", + "LDAP_DataSync_BackgroundSync": "Background Sync", + "LDAP_Server_Type": "Server Type", + "LDAP_Server_Type_Other": "ອື່ນໆ", "LDAP_Advanced_Sync": "ຊິງຂັ້ນສູງ", "LDAP_Authentication": "ເປີດໃຊ້ງານ", "LDAP_Authentication_Password": "ລະຫັດຜ່ານ", @@ -2701,6 +2708,8 @@ "Visitor_Navigation": "ຈໍານວນຜູ້ເຂົ້າ Navigation", "Visitor_page_URL": "URL ຫນ້າຈໍານວນຜູ້ເຂົ້າ", "Visitor_time_on_site": "ທີ່ໃຊ້ເວລາຈໍານວນຜູ້ເຂົ້າຢູ່ໃນເວັບໄຊ", + "VoIP_Management_Server_Username": "ຊື່ຜູ້ໃຊ້", + "VoIP_Management_Server_Password": "ລະຫັດຜ່ານ", "Wait_activation_warning": "ກ່ອນທີ່ທ່ານຈະສາມາດເຂົ້າສູ່ລະບົບ, ບັນຊີຂອງທ່ານຕ້ອງໄດ້ຮັບການເປີດໃຊ້ວຽກດ້ວຍຕົນເອງໂດຍຜູ້ບໍລິຫານ.", "Warnings": "ຄໍາເຕືອນ", "We_are_offline_Sorry_for_the_inconvenience": "ພວກເຮົາມີຄວາມອອຟໄລ. ຂໍ​ອະ​ໄພ​ໃນ​ຄວາມ​ບໍ່​ສະ​ດວກ.", @@ -2785,6 +2794,7 @@ "registration.component.form.emailOrUsername": "ອີ​ເມລ​ຫຼື​ຊື່​ຜູ້​ໃຊ້", "registration.component.form.username": "ຊື່ຜູ້ໃຊ້", "registration.component.form.name": "ຊື່", + "registration.component.form.userAlreadyExist": "ຊື່ຜູ້ໃຊ້ມີຢູ່ແລ້ວ. ກະລຸນາລອງໃຊ້ຊື່ຜູ້ໃຊ້ອີກຄັ້ງຫນຶ່ງ.", "registration.component.form.emailAlreadyExists": "Email ຢູ່ແລ້ວ", "registration.component.form.usernameAlreadyExists": "ຊື່ຜູ້ໃຊ້ມີຢູ່ແລ້ວ. ກະລຸນາລອງໃຊ້ຊື່ຜູ້ໃຊ້ອີກຄັ້ງຫນຶ່ງ.", "registration.component.form.invalidEmail": "ອີເມວນັ້ນບໍ່ຖືກຕ້ອງ", diff --git a/packages/i18n/src/locales/lt.i18n.json b/packages/i18n/src/locales/lt.i18n.json index 45eb0f343c17..3a866a790e77 100644 --- a/packages/i18n/src/locales/lt.i18n.json +++ b/packages/i18n/src/locales/lt.i18n.json @@ -355,6 +355,7 @@ "Apply": "Taikyti", "Apply_and_refresh_all_clients": "Taikykite ir atnaujinkite visus klientus", "Apps": "Programos", + "Apps_context_premium": "Įmonė", "Apps_Game_Center_Back": "Grįžti į žaidimų centrą", "Apps_Game_Center_Invite_Friends": "Pakvieskite draugus prisijungti", "Apps_Game_Center_Play_Game_Together": "@here Žaiskime {{name}} kartu!", @@ -1154,6 +1155,7 @@ "FEDERATION_Domain": "Domenas", "FEDERATION_Status": "Statusas", "Retry_Count": "Pakartotinai kartoti", + "Federation_Matrix_enabled": "Įjungtas", "Field": "Laukas", "Field_removed": "Laukas pašalintas", "Field_required": "Reikalingas laukas", @@ -1513,6 +1515,10 @@ "Layout_Terms_of_Service": "Paslaugų teikimo sąlygos", "LDAP": "LDAP", "LDAP_Description": "LDAP yra hierarchinė duomenų bazė, kurią daugelis kompanijų naudoja vieninteliam prisijungimui - tai galimybė dalintis vienu slaptažodžiu tarp kelių svetainių ir paslaugų. Išsamesnės konfigūracijos informacijos ir pavyzdžių rasite mūsų wiki: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "Šifravimas", + "LDAP_DataSync_BackgroundSync": "Fono sinchronizavimas", + "LDAP_Server_Type": "Serverio tipas", + "LDAP_Server_Type_Other": "Kitas", "LDAP_Authentication": "Įgalinti", "LDAP_Authentication_Password": "Slaptažodis", "LDAP_Authentication_UserDN": "Vartotojo DN", @@ -2543,6 +2549,7 @@ "Unarchive": "Unarchyvuoti", "unarchive-room": "Unarchive kambarys", "unarchive-room_description": "Leidimas archyvuoti kanalus", + "unauthorized": "Neleidžiama", "Unblock_User": "Atblokuoti naudotoją", "Unignore": "Neignore", "Uninstall": "Pašalinti", @@ -2719,6 +2726,8 @@ "Visitor_Navigation": "Lankytojo navigacija", "Visitor_page_URL": "Lankytojo puslapio URL", "Visitor_time_on_site": "Lankytojo laikas svetainėje", + "VoIP_Management_Server_Username": "Vartotojo vardas", + "VoIP_Management_Server_Password": "Slaptažodis", "Wait_activation_warning": "Prieš prisijungdami, administratoriaus rankiniu būdu turi įjungti savo sąskaitą.", "Warnings": "Įspėjimai", "We_are_offline_Sorry_for_the_inconvenience": "Mes neprisijungę. Atsiprašome už nepatogumus.", @@ -2793,6 +2802,7 @@ "Your_push_was_sent_to_s_devices": "Jūsų paspaudimas buvo išsiųstas į%s įrenginius", "Your_server_link": "Jūsų serverio nuoroda", "Your_workspace_is_ready": "Jūsų darbo vieta yra paruošta naudoti 🎉", + "registration.page.login.errors.wrongCredentials": "Vartotojas nerastas arba neteisingas slaptažodis", "registration.page.registration.waitActivationWarning": "Prieš prisijungdami, administratoriaus rankiniu būdu turi įjungti savo sąskaitą.", "registration.page.login.forgot": "Pamiršai slaptažodį", "registration.page.resetPassword.sent": "Jei šis el. Laiškas yra užregistruotas, atsiųsime instrukcijas, kaip iš naujo nustatyti slaptažodį. Jei netrukus negausite el. Laiško, grįžkite ir bandykite dar kartą.", @@ -2802,6 +2812,7 @@ "registration.component.form.emailOrUsername": "El. pastas arba vartotojo vardas", "registration.component.form.username": "Vartotojo vardas", "registration.component.form.name": "Pavadinimas", + "registration.component.form.userAlreadyExist": "Vardas jau egzistuoja. Pabandykite kitą vartotojo vardą.", "registration.component.form.emailAlreadyExists": "El. Laiškas jau egzistuoja", "registration.component.form.usernameAlreadyExists": "Vardas jau egzistuoja. Pabandykite kitą vartotojo vardą.", "registration.component.form.invalidEmail": "Įvestas el. Pašto adresas neteisingas", diff --git a/packages/i18n/src/locales/lv.i18n.json b/packages/i18n/src/locales/lv.i18n.json index 50fd40831e5f..697c5b577600 100644 --- a/packages/i18n/src/locales/lv.i18n.json +++ b/packages/i18n/src/locales/lv.i18n.json @@ -325,6 +325,7 @@ "Apply": "Pieteikties", "Apply_and_refresh_all_clients": "Pieteikties un atsvaidzināt visus klientus", "Apps": "Lietotnes", + "Apps_context_premium": "Uzņēmums", "Apps_Settings": "Lietotnes iestatījumi", "Apps_WhatIsIt": "Litetotnes: Kas tās tādas?", "Apps_WhatIsIt_paragraph1": "Jauna ikona administrācijas laukā! Ko tas nozīmē un kas ir lietotnes?", @@ -1112,6 +1113,7 @@ "FEDERATION_Domain": "Domēns", "FEDERATION_Status": "Stāvoklis", "Retry_Count": "Mēģinājumu skaits", + "Federation_Matrix_enabled": "Iespējots", "Field": "Lauks", "Field_removed": "Lauks noņemts", "Field_required": "Vajadzīgs lauks", @@ -1471,6 +1473,10 @@ "Layout_Terms_of_Service": "Pakalpojumu sniegšanas noteikumi", "LDAP": "LDAP", "LDAP_Description": "LDAP ir hierarhiska datu bāze, ko daudzi uzņēmumi izmanto, lai nodrošinātu vienotu pieteikšanos - telpu kur dalties ar vienu paroli starp vairākām vietnēm un pakalpojumiem. Papildu konfigurācijas informācijai un piemēriem, lūdzu skatiet mūsu wiki: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "Šifrēšana", + "LDAP_DataSync_BackgroundSync": "Fona sinhronizācija", + "LDAP_Server_Type": "Servera veids", + "LDAP_Server_Type_Other": "Cits", "LDAP_Authentication": "Iespējot", "LDAP_Authentication_Password": "Parole", "LDAP_Authentication_UserDN": "Lietotāja DN", @@ -2495,6 +2501,7 @@ "Unarchive": "Izņemt no arhīva", "unarchive-room": "Izņemt istabu no arhīva", "unarchive-room_description": "Atļauja izņemt kanālus no arhīva", + "unauthorized": "Nav atļauts", "Unblock_User": "Atbloķēt lietotāju", "Unignore": "Noņemt ignorēt", "Uninstall": "Atinstalēt", @@ -2662,6 +2669,8 @@ "Visitor_Navigation": "Apmeklētāju navigācija", "Visitor_page_URL": "Apmeklētāja lapas URL", "Visitor_time_on_site": "Apmeklētāja laiks vietnē", + "VoIP_Management_Server_Username": "Lietotājvārds", + "VoIP_Management_Server_Password": "Parole", "Wait_activation_warning": "Pirms jūs varat pieteikties, administratoram manuāli jāaktivizē jūsu kontu .", "Warnings": "Brīdinājumi", "We_are_offline_Sorry_for_the_inconvenience": "Mēs esam bezsaistē. Atvainojamies par sagādātajām neērtībām.", @@ -2734,6 +2743,7 @@ "Your_push_was_sent_to_s_devices": "Jūsu push tika nosūtīts uz %s ierīcēm", "Your_server_link": "Jūsu servera saite", "Your_workspace_is_ready": "Jūsu darbastacija ir gatava lietošanai 🎉", + "registration.page.login.errors.wrongCredentials": "Lietotājs nav atrasts vai ir nepareiza parole", "registration.page.registration.waitActivationWarning": "Pirms jūs varat pieteikties, administratoram manuāli jāaktivizē jūsu kontu .", "registration.page.login.forgot": "Aizmirsi savu paroli", "registration.page.resetPassword.sent": "Ja šis e-pasts ir reģistrēts, mēs nosūtīsim norādījumus par to, kā atiestatīt savu paroli. Ja drīzumā nesaņemsiet e-pasta ziņojumu, lūdzam, atgriezieties un mēģiniet vēlreiz.", @@ -2743,6 +2753,7 @@ "registration.component.form.emailOrUsername": "E-pasts vai lietotājvārds", "registration.component.form.username": "Lietotājvārds", "registration.component.form.name": "Vārds", + "registration.component.form.userAlreadyExist": "Lietotājvārds jau eksistē. Lūdzu, izmēģiniet citu lietotājvārdu.", "registration.component.form.emailAlreadyExists": "E-pasta adrese jau pastāv", "registration.component.form.usernameAlreadyExists": "Lietotājvārds jau eksistē. Lūdzu, izmēģiniet citu lietotājvārdu.", "registration.component.form.invalidEmail": "Ievadītais e-pasts nav derīgs", diff --git a/packages/i18n/src/locales/mn.i18n.json b/packages/i18n/src/locales/mn.i18n.json index 8520175e2189..b8dd78554925 100644 --- a/packages/i18n/src/locales/mn.i18n.json +++ b/packages/i18n/src/locales/mn.i18n.json @@ -319,6 +319,7 @@ "Apply": "Хүсэлт гаргах", "Apply_and_refresh_all_clients": "Бүх үйлчлүүлэгчийг өргөж, сэргээнэ үү", "Apps": "Аппууд", + "Apps_context_premium": "Аж ахуйн нэгж", "Apps_Settings": "Апп тохиргоо", "Apps_WhatIsIt": "Апп: Тэдгээр нь юу вэ?", "Apps_WhatIsIt_paragraph1": "Захиргааны бүс дэх шинэ дүрс Энэ нь юу гэсэн үг вэ, Apps гэж юу вэ?", @@ -1095,6 +1096,7 @@ "FEDERATION_Domain": "Домэйн", "FEDERATION_Status": "Статус", "Retry_Count": "Дахин оролдох", + "Federation_Matrix_enabled": "Идэвхжүүлсэн", "Field": "Талбар", "Field_removed": "Талбарыг арилгасан", "Field_required": "Шаардлагатай талбар", @@ -1453,6 +1455,10 @@ "Layout_Terms_of_Service": "Үйлчилгээний нөхцөл", "LDAP": "LDAP", "LDAP_Description": "LDAP нь олон компаниуд сайтууд болон үйлчилгээнүүдийн хооронд нэг нууц үг солилцох боломжийг олгодог шаталбар бүхий өгөгдлийн сан юм. Нэмэлт тохиргооны мэдээлэл болон жишээнүүдийн хувьд манай викитэй холбогдоно уу: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "Шифрлэлт", + "LDAP_DataSync_BackgroundSync": "Үндсэн синх", + "LDAP_Server_Type": "Серверийн төрөл", + "LDAP_Server_Type_Other": "Бусад", "LDAP_Authentication": "Бататгах", "LDAP_Authentication_Password": "Нууц үг", "LDAP_Authentication_UserDN": "Хэрэглэгчийн DN", @@ -2481,6 +2487,7 @@ "Unarchive": "Unarchive", "unarchive-room": "Unarchive Room", "unarchive-room_description": "Ноцтой суваг руу зөвшөөрөл өгөх", + "unauthorized": "Эрх мэдэлгүй байх", "Unblock_User": "Хэрэглэгчийг хориглох", "Unignore": "Unignore", "Uninstall": "Устгах", @@ -2654,6 +2661,8 @@ "Visitor_Navigation": "Зорчигч навигацийн", "Visitor_page_URL": "Зочлогч хуудасны URL", "Visitor_time_on_site": "Сайтын цаг", + "VoIP_Management_Server_Username": "Хэрэглэгчийн нэр", + "VoIP_Management_Server_Password": "Нууц үг", "Wait_activation_warning": "Нэвтрэхийн өмнө, таны данс администратор гараар идэвхжүүлсэн байх ёстой.", "Warnings": "Анхааруулга", "We_are_offline_Sorry_for_the_inconvenience": "Бид офлайн байна. Төвөг удсанд уучлаарай.", @@ -2727,6 +2736,7 @@ "Your_push_was_sent_to_s_devices": "Таны түлхэлт %s төхөөрөмж рүү илгээгдсэн", "Your_server_link": "Таны серверийн холбоос", "Your_workspace_is_ready": "Таны ажлын талбарыг ашиглахад бэлэн байна", + "registration.page.login.errors.wrongCredentials": "Хэрэглэгч олдсонгүй эсвэл буруу нууц үг", "registration.page.registration.waitActivationWarning": "Нэвтрэхийн өмнө, таны данс администратор гараар идэвхжүүлсэн байх ёстой.", "registration.page.login.forgot": "Нууц үгээ мартсан", "registration.page.resetPassword.sent": "Хэрэв энэ имэйл бүртгэгдсэн бол бид таны нууц үгийг шинэчлэх талаар зааварчилгааг илгээх болно. Хэрэв та имэйлээ удахгүй хүлээж авахгүй бол эргэж ороод дахин оролдоно уу.", @@ -2736,6 +2746,7 @@ "registration.component.form.emailOrUsername": "И-мэйл эсвэл Хэрэглэгчийн нэр", "registration.component.form.username": "Хэрэглэгчийн нэр", "registration.component.form.name": "Нэр", + "registration.component.form.userAlreadyExist": "Хэрэглэгчийн нэр өмнө байна. Өөр хэрэглэгчийн нэрийг оруулна уу.", "registration.component.form.emailAlreadyExists": "Имэйл аль хэдийн байна", "registration.component.form.usernameAlreadyExists": "Хэрэглэгчийн нэр өмнө байна. Өөр хэрэглэгчийн нэрийг оруулна уу.", "registration.component.form.invalidEmail": "Оруулсан имэйл буруу байна", diff --git a/packages/i18n/src/locales/ms-MY.i18n.json b/packages/i18n/src/locales/ms-MY.i18n.json index 2c14909ec239..4089904315cb 100644 --- a/packages/i18n/src/locales/ms-MY.i18n.json +++ b/packages/i18n/src/locales/ms-MY.i18n.json @@ -319,6 +319,7 @@ "Apply": "Memohon", "Apply_and_refresh_all_clients": "Dikenakan dan menyegarkan semua pelanggan", "Apps": "Apps", + "Apps_context_premium": "Enterprise", "Apps_Settings": "Tetapan Apl", "Apps_WhatIsIt": "Apps: Apakah Mereka?", "Apps_WhatIsIt_paragraph1": "Ikon baru di kawasan pentadbiran! Apa maksudnya dan apa yang Apps?", @@ -489,6 +490,7 @@ "Confirm_New_Password_Placeholder": "Sila masukkan semula kata laluan baru ...", "Confirm_password": "Sahkan kata laluan anda", "Confirm_your_password": "Sahkan kata laluan anda", + "Connected": "Bersambung", "Connection_Closed": "Sambungan ditutup", "Connection_Reset": "Tetap semula sambungan", "Consulting": "Perundingan", @@ -1097,6 +1099,7 @@ "FEDERATION_Domain": "domain", "FEDERATION_Status": "Status", "Retry_Count": "Cuba semula", + "Federation_Matrix_enabled": "didayakan", "Field": "Field", "Field_removed": "Field dikeluarkan", "Field_required": "Bidang diperlukan", @@ -1455,6 +1458,10 @@ "Layout_Terms_of_Service": "Terma Perkhidmatan", "LDAP": "LDAP", "LDAP_Description": "LDAP adalah pangkalan data hierarki bahawa banyak syarikat gunakan untuk menyediakan single sign on - kemudahan untuk berkongsi satu kata laluan antara beberapa tapak dan perkhidmatan. Untuk maklumat konfigurasi lanjutan dan contoh, sila rujuk wiki kami: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "penyulitan", + "LDAP_DataSync_BackgroundSync": "Penyegerakan Latar Belakang", + "LDAP_Server_Type": "Jenis pelayan", + "LDAP_Server_Type_Other": "lain", "LDAP_Authentication": "Membolehkan", "LDAP_Authentication_Password": "Kata laluan", "LDAP_Authentication_UserDN": "DN pengguna", @@ -2668,6 +2675,8 @@ "Visitor_Navigation": "Navigation pelawat", "Visitor_page_URL": "URL halaman Pelawat", "Visitor_time_on_site": "kali pengunjung di laman web", + "VoIP_Management_Server_Username": "Nama pengguna", + "VoIP_Management_Server_Password": "Kata laluan", "Wait_activation_warning": "Sebelum anda boleh log masuk, akaun anda mesti diaktifkan secara manual oleh pentadbiran.", "Warnings": "Amaran", "We_are_offline_Sorry_for_the_inconvenience": "Kami berada di luar talian. Maaf atas kesulitan.", @@ -2752,6 +2761,7 @@ "registration.component.form.emailOrUsername": "E-mel atau nama pengguna", "registration.component.form.username": "Nama pengguna", "registration.component.form.name": "Nama", + "registration.component.form.userAlreadyExist": "Nama pengguna sudah wujud. Sila cuba nama pengguna lain.", "registration.component.form.emailAlreadyExists": "E-mel telah wujud", "registration.component.form.usernameAlreadyExists": "Nama pengguna sudah wujud. Sila cuba nama pengguna lain.", "registration.component.form.invalidEmail": "E-mel yang dimasukkan tidak sah", diff --git a/packages/i18n/src/locales/nl.i18n.json b/packages/i18n/src/locales/nl.i18n.json index 29a69550e75e..a5626058a8fe 100644 --- a/packages/i18n/src/locales/nl.i18n.json +++ b/packages/i18n/src/locales/nl.i18n.json @@ -452,6 +452,7 @@ "Apply_and_refresh_all_clients": "Toepassen en alle klanten opnieuw laden", "Apps": "Apps", "Apps_context_installed": "Geïnstalleerd", + "Apps_context_premium": "Onderneming", "Apps_Engine_Version": "Apps Engine-versie", "Apps_Essential_Alert": "Deze app is essentieel voor de volgende evenementen:", "Apps_Essential_Disclaimer": "De hierboven genoemde gebeurtenissen worden onderbroken als deze app is uitgeschakeld. Als je wilt dat Rocket.Chat werkt zonder de functionaliteit van deze app, moet je deze verwijderen", @@ -1374,6 +1375,7 @@ "Desktop_Notifications_Not_Enabled": "Desktopmeldingen zijn niet ingeschakeld", "Details": "Details", "line": "lijn", + "Device_Management_IP": "IP", "Different_Style_For_User_Mentions": "Andere stijl voor gebruikersvermeldingen", "Livechat_Facebook_API_Key": "OmniChannel API-sleutel", "Livechat_Facebook_API_Secret": "OmniChannel API-geheim", @@ -1860,6 +1862,7 @@ "FEDERATION_Test_Setup_Error": "Kan uw server niet vinden met uw installatie. Controleer uw instellingen.", "FEDERATION_Test_Setup_Success": "Uw federatie-installatie werkt en andere servers kunnen u vinden!", "Retry_Count": "Aantal pogingen", + "Federation_Matrix_enabled": "Ingeschakeld", "Field": "Veld", "Field_removed": "Veld verwijderd", "Field_required": "Veld vereist", @@ -3333,6 +3336,7 @@ "Privacy": "Privacy", "Privacy_Policy": "Privacybeleid", "Private": "Privé", + "Private_channels": "Privékanalen", "Private_Channel": "Privékanaal", "Private_Channels": "Privékanalen", "Private_Chats": "Privéchats", @@ -3409,6 +3413,7 @@ "Reactions": "Reacties", "Read_by": "Gelezen door", "Read_only": "Alleen lezen", + "This_room_is_read_only": "Deze kamer is alleen-lezen", "Read_only_changed_successfully": "Alleen lezen is succesvol gewijzigd", "Read_only_channel": "Alleen lezen kanaal", "Read_only_group": "Alleen lezen groep", @@ -3571,6 +3576,7 @@ "Unsafe_Url": "Onveilige URL", "Rocket_Chat_Alert": "Rocket.Chat-waarschuwing", "Role": "Rol", + "Roles": "Rollen", "Role_Editing": "Rolbewerking", "Role_Mapping": "Roltoewijzing", "Role_removed": "Rol verwijderd", @@ -4779,6 +4785,7 @@ "registration.component.resetPassword": "Wachtwoord opnieuw instellen", "registration.component.form.username": "Gebruikersnaam", "registration.component.form.name": "Naam", + "registration.component.form.userAlreadyExist": "Gebruikersnaam bestaat al. Probeer een andere gebruikersnaam.", "registration.component.form.emailAlreadyExists": "E-mailadres bestaat al", "registration.component.form.usernameAlreadyExists": "Gebruikersnaam bestaat al. Probeer een andere gebruikersnaam.", "registration.component.form.invalidEmail": "Het ingevoerde e-mailadres is ongeldig", diff --git a/packages/i18n/src/locales/no.i18n.json b/packages/i18n/src/locales/no.i18n.json index 7d25488a7c40..0397bf518f7c 100644 --- a/packages/i18n/src/locales/no.i18n.json +++ b/packages/i18n/src/locales/no.i18n.json @@ -565,6 +565,7 @@ "Apps_Interface_IPreMessageDeletePrevent": "Hendelse som skjer før en melding slettes", "Apps_Interface_IPreMessageSentExtend": "Hendelse som skjer før en melding sendes", "Apps_Interface_IPreMessageSentModify": "Hendelse som skjer før en melding sendes", + "Apps_Interface_IPreMessageSentPrevent": "Hendelse som skjer før en melding sendes", "Apps_License_Message_maxSeats": "Lisensen tar ikke imot gjeldende antall aktive brukere. Vennligst øk antall seter", "Apps_License_Message_publicKey": "Det har oppstått en feil under dekryptering av lisensen. Synkroniser arbeidsområdet ditt i Connectivity Service og prøv på nytt", "Apps_License_Message_renewal": "Lisensen er utløpt og må fornyes", @@ -1930,10 +1931,12 @@ "Favorites": "Favoritter", "Feature_Depends_on_Livechat_Visitor_navigation_as_a_message_to_be_enabled": "Denne funksjonen avhenger av \"Send besøkende navigasjonshistorikk som en melding\" for å være aktivert.", "Federation_Example_matrix_server": "Eksempel: matrix.org", + "Federation_Public_key": "Offentlig nøkkel", "FEDERATION_Domain": "Domene", "FEDERATION_Public_Key": "Offentlig nøkkel", "FEDERATION_Status": "Status", "Retry_Count": "Prøv på nytt", + "Federation_Matrix_enabled": "aktivert", "Federation_Matrix_id": "AppService-ID", "Federation_Matrix_hs_token": "Hjemmeserver-token", "Federation_Matrix_as_token": "AppService-token", @@ -2523,6 +2526,10 @@ "LDAP_DataSync_Advanced": "Avansert synkronisering", "LDAP_DataSync_Roles": "Synkroniser roller", "LDAP_DataSync_Channels": "Synkroniser kanaler", + "LDAP_DataSync_BackgroundSync": "Bakgrunnssynkronisering", + "LDAP_Server_Type": "Server Type", + "LDAP_Server_Type_Other": "Andre", + "LDAP_Advanced_Sync": "Avansert synkronisering", "LDAP_Authentication": "Aktiver", "LDAP_Authentication_Password": "Passord", "LDAP_Authentication_UserDN": "Bruker DN", @@ -3350,6 +3357,7 @@ "Read_by": "Les av", "Read_only": "Les bare", "Readability": "Lesbarhet", + "This_room_is_read_only": "Dette rommet er kun skrivebeskyttet", "Read_only_changed_successfully": "Bare lest endret", "Read_only_channel": "Les kun kanal", "Read_only_group": "Read Only Group", @@ -3482,6 +3490,7 @@ "Notes": "Notater", "Unsafe_Url": "Usikker URL", "Role": "rolle", + "Roles": "Roller", "Role_Editing": "Rollredigering", "Role_Mapping": "Rollekobling", "Role_removed": "Rolle fjernet", @@ -3566,6 +3575,7 @@ "SAML_Section_2_Certificate": "Sertifikat", "SAML_Section_3_Behavior": "Oppførsel", "SAML_Section_4_Roles": "Roller", + "SAML_Section_5_Mapping": "Kartlegging", "SAML_Section_6_Advanced": "Avansert", "Saturday": "lørdag", "Save": "Lagre", @@ -4039,6 +4049,7 @@ "Threads": "Tråder", "Thursday": "Torsdag", "Time_in_seconds": "Tid i sekunder", + "Timeouts": "Tidsavbrudd", "Timezone": "Tidssone", "Title": "Tittel", "Title_bar_color": "Tittel bar farge", @@ -4074,6 +4085,7 @@ "Total_Threads": "Totalt antall tråder", "Total_visitors": "Totalt antall besøkende", "TOTP Invalid [totp-invalid]": "Kode eller passord er ugyldig", + "totp-invalid": "Kode eller passord er ugyldig", "Transcript_Enabled": "Spør besøkende hvis de vil ha en transkripsjon etter at Chat er lukket", "Transcript_message": "Melding til å vise når du spør om transkripsjon", "Transcript_of_your_livechat_conversation": "Transkripsjon av livechat-samtalen.", @@ -4186,6 +4198,7 @@ "Use_User_Preferences_or_Global_Settings": "Bruk brukerinnstillinger eller globale innstillinger", "User": "Bruker", "User_menu": "Brukermeny", + "User Search": "Brukersøk", "User__username__is_now_a_leader_of__room_name_": "Bruker {{username}} er nå leder av {{room_name}}", "User__username__is_now_a_moderator_of__room_name_": "Bruker {{username}} er nå en moderator av {{room_name}}", "User__username__is_now_an_owner_of__room_name_": "Bruker {{username}} er nå eier av {{room_name}}", @@ -4307,6 +4320,7 @@ "video_conference_ended": "_Samtalen har sluttet._", "video_livechat_started": "_Startet en videosamtale._", "video_direct_calling": "_Ringer._", + "video_direct_ended": "_Samtalen er avsluttet._", "View_mode": "Visningsmodus", "View_All": "Se alle medlemmer", "View_channels": "Se kanaler", @@ -4362,6 +4376,8 @@ "Visitor_Navigation": "Visitor Navigasjon", "Visitor_page_URL": "URL for besøkende siden", "Visitor_time_on_site": "Besøkende tid på stedet", + "VoIP_Management_Server_Username": "Brukernavn", + "VoIP_Management_Server_Password": "Passord", "Voip_call_duration": "Samtalen varte i {{duration}}", "Voip_call_ended_unexpectedly": "Samtalen ble avbrutt uventet: {{reason}}", "Wait_activation_warning": "Før du kan logge inn, må kontoen din aktiveres manuelt av en administrator.", @@ -4467,6 +4483,10 @@ "Youre_not_a_part_of__channel__and_I_mentioned_you_there": "Du er ikke en del av {{channel}} og jeg nevnte deg der", "registration.page.login.errors.wrongCredentials": "Bruker finnes ikke eller så er passordet feil", "registration.page.login.errors.invalidEmail": "Ugyldig e-post", + "registration.page.login.errors.loginBlockedForIp": "Innlogging er midlertidig blokkert for denne IP-adressen", + "registration.page.login.errors.loginBlockedForUser": "Innlogging er midlertidig blokkert for denne brukeren", + "registration.page.login.errors.licenseUserLimitReached": "Maksimalt antall brukere er nådd.", + "registration.page.login.errors.AppUserNotAllowedToLogin": "Appbrukere har ikke lov til å logge inn direkte.", "registration.page.registration.waitActivationWarning": "Før du kan logge inn, må kontoen din aktiveres manuelt av en administrator.", "registration.page.login.register": "Ny her? <1>Opprett en konto", "registration.page.resetPassword.sent": "Hvis denne e-posten er registrert, sender vi instruksjoner om hvordan du tilbakestiller passordet ditt. Hvis du ikke mottar en epost, vennligst kom tilbake og prøv igjen.", @@ -4498,6 +4518,7 @@ "registration.component.form.reasonToJoin": "Årsak til å bli med", "registration.component.form.invalidConfirmPass": "Passordbekreftelsen stemmer ikke overens med passordet", "registration.component.form.confirmPassword": "Bekreft passordet ditt", + "registration.component.form.confirmation": "Bekreftelse", "registration.component.form.sendConfirmationEmail": "Send bekreftelses-e-post", "onboarding.component.form.action.registerWorkspace": "Registrer arbeidsområde", "onboarding.component.form.action.registerOffline": "Registrer deg offline", @@ -4518,6 +4539,7 @@ "Community_Private_apps_limit_exceeded": "Grensen for Community-apper er overskredet.", "Theme_high_contrast": "Høy kontrast", "Highlighted_chosen_word": "Uthevet valgt ord", + "Join_your_team": "Bli med teamet ditt", "Create_a_password": "Opprett et passord", "Create_an_account": "Opprett en konto", "Get_all_apps": "Få alle appene teamet ditt trenger", @@ -4535,9 +4557,11 @@ "User_status_temporarily_disabled": "Brukerstatus er midlertidig deaktivert", "Use_token": "Bruk token", "Disconnected": "Frakoblet", + "Security_code": "Sikkerhetskode", "Registration_Token": "Registreringstoken", "RegisterWorkspace_Button": "Registrer arbeidsområde", "RegisterWorkspace_Features_Marketplace_Disconnect": "Det vil ikke lenger være mulig å installere apper.", + "RegisterWorkspace_Features_ThirdPartyLogin_Title": "Tredjeparts innlogging", "RegisterWorkspace_Setup_Steps": "Steg {{step}} av {{numberOfSteps}}", "RegisterWorkspace_Setup_Have_Account_Title": "Har en konto?", "RegisterWorkspace_Setup_No_Account_Title": "Har du ikke en konto?", diff --git a/packages/i18n/src/locales/pl.i18n.json b/packages/i18n/src/locales/pl.i18n.json index 4461e54d899c..0d2325e2e47e 100644 --- a/packages/i18n/src/locales/pl.i18n.json +++ b/packages/i18n/src/locales/pl.i18n.json @@ -1,7 +1,7 @@ { "500": "Wewnętrzny błąd serwera", - "__count__empty_rooms_will_be_removed_automatically": "Liczba pokojów do automatycznego usunięcia: {{count}}.", "__count__message_pruned_few": "{{count}} wiadomość(i) usuniętych", + "__count__empty_rooms_will_be_removed_automatically": "Liczba pokojów do automatycznego usunięcia: {{count}}.", "__count__empty_rooms_will_be_removed_automatically__rooms__": "Puste pokoje ({{count}}) zostaną automatycznie usunięte:
          {{rooms}}.", "__count__message_pruned_one": "{{count}} wiadomość(i) usuniętych", "__count__message_pruned_other": "{{count}} wiadomość(i) usuniętych", @@ -11,8 +11,8 @@ "__usersCount__member_joined_other": "+ {{count}} członków dołączyło", "__usersCount__people_will_be_invited": "{{usersCount}} ludzi zostanie zostanie zaproszonych", "__username__is_no_longer__role__defined_by__user_by_": "Użytkownik {{username}} nie ma już roli {{role}}; zmienił to użytkownik {{user_by}}", - "__usersCount__member_joined_many": "+ {{count}} członków dołączyło", "__username__was_set__role__by__user_by_": "Użytkownik {{username}} otrzymał rolę {{role}} od użytkownika {{user_by}}", + "__usersCount__member_joined_many": "+ {{count}} członków dołączyło", "This_room_encryption_has_been_enabled_by__username_": "Użytkownik {{username}} włączył szyfrowanie w tym pokoju", "This_room_encryption_has_been_disabled_by__username_": "Użytkownik {{username}} wyłączył szyfrowanie w tym pokoju", "disabled": "wyłączone", @@ -441,6 +441,7 @@ "API_Shield_Types_Description": "Typy osłon do włączenia jako lista rozdzielona przecinkami. Do wyboru „online”, „channel” lub „*” w przypadku wszystkich", "Apps_Framework_Development_Mode": "Włącz tryb programistyczny", "API_Shield_user_require_auth": "Wymagaj uwierzytelnienia dla osłon użytkowników", + "Calls_in_queue_many": "{{count}} połączeń w kolejce", "API_Token": "Token API", "Apps_Framework_Development_Mode_Description": "Tryb programistyczny zezwala na instalację aplikacji niepochodzących z Rocket.Chat Marketplace.", "API_Tokenpass_URL": "Adres URL serwera Tokenpass", @@ -475,6 +476,7 @@ "App_support_url": "adres URL pomocy", "App_Url_to_Install_From": "Zainstaluj z adresu URL", "App_Url_to_Install_From_File": "Zainstaluj z pliku", + "Calls_in_queue_few": "{{count}} połączeń w kolejce", "App_user_not_allowed_to_login": "Użytkownicy aplikacji nie mogą logować się bezpośrednio.", "Appearance": "Wygląd", "Application_added": "Aplikacja dodana", @@ -486,6 +488,7 @@ "Apps": "Aplikacje", "Apps_context_explore": "Poznaj", "Apps_context_installed": "Zainstalowana", + "Apps_context_premium": "Enterprise", "Apps_Engine_Version": "Wersja silnika aplikacji", "Apps_Essential_Alert": "Ta aplikacja jest niezbędna dla następujących zdarzeń:", "Apps_Essential_Disclaimer": "Zdarzenia wymienione powyżej zostaną zakłócone, jeśli ta aplikacja jest wyłączona. Jeśli chcesz, aby Rocket.Chat działał bez tej aplikacji, musisz ją odinstalować", @@ -522,7 +525,6 @@ "Apps_License_Message_appId": "Licencja nie została wydana dla tej aplikacji", "Apps_License_Message_bundle": "Licencja wydana dla paczki, która nie zawiera aplikacji", "Apps_License_Message_expire": "Licencja straciła ważność i musi zostać odnowiona", - "Calls_in_queue_many": "{{count}} połączeń w kolejce", "Apps_License_Message_maxSeats": "Licencja nie jest dostosowana do obecnej liczby aktywnych użytkowników. Zwiększ liczbę miejsc", "Apps_License_Message_publicKey": "Wystąpił błąd podczas próby odszyfrowania licencji. Zsynchronizuj przestrzeń roboczą w usługach łączności i spróbuj ponownie", "Apps_License_Message_renewal": "Licencja wygasła i wymaga odnowienia", @@ -574,7 +576,6 @@ "Apps_Permissions_livechat-message_read": "Uzyskaj dostęp do informacji o wiadomościach Livechat", "Apps_Permissions_livechat-message_write": "Zmień informacje o wiadomościach Livechat", "Apps_Permissions_livechat-room_read": "Uzyskaj dostęp do informacji o pokojach Livechat", - "Calls_in_queue_few": "{{count}} połączeń w kolejce", "Apps_Permissions_livechat-room_write": "Zmień informacje o pokojach Livechat", "Apps_Permissions_livechat-department_read": "Uzyskaj dostęp do informacji o działach Livechat", "Apps_Permissions_livechat-department_multiple": "Dostęp do informacji o wielu działach Livechat", @@ -1771,6 +1772,7 @@ "Enter_Normal": "Tryb normalny (wysyłaj klawiszem Enter)", "Enter_to": "Naciśnij Enter: ", "Enter_your_E2E_password": "Wprowadź swoje hasło E2E", + "message_counter_many": "{{count}} wiadomości", "Entertainment": "Rozrywka", "Error": "Błąd", "Error_something_went_wrong": "Ups! Coś poszło nie tak. Odśwież stronę lub skontaktuj się z administratorem.", @@ -1819,6 +1821,7 @@ "error-forwarding-department-target-not-allowed": "Przekazywanie do docelowego działu nie jest dozwolone.", "error-guests-cant-have-other-roles": "Goście nie mogą mieć żadnych innych ról.", "error-import-file-extract-error": "Nie można wyodrębnić pliku importu.", + "meteor_status_reconnect_in_many": "spróbuj jeszcze raz za {{count}} sekund...", "error-import-file-is-empty": "Zaimportowany plik wydaje się być pusty.", "error-import-file-missing": "Plik, który ma zostać zaimportowany, nie został znaleziony w podanej ścieżce.", "error-importer-not-defined": "Importer nie została określona prawidłowo, to brakuje klasy Import.", @@ -1916,6 +1919,7 @@ "error-this-is-not-a-livechat-room": "To nie jest pokój Livechata", "error-token-already-exists": "Token o tej nazwie już istnieje", "error-token-does-not-exists": "Token nie istnieje", + "message_counter_few": "{{count}} wiadomości", "error-too-many-requests": "Błąd, zbyt wiele żądań. Proszę zwolnij. Musisz czekać {{seconds}} sekund przed ponowną próbą.", "error-transcript-already-requested": "Transkrypcja jest już wymagana", "error-unpinning-message": "Wiadomość nie może być odpięta", @@ -1969,6 +1973,7 @@ "Extension_Number": "Numer wewnętrzny", "Extension_Status": "Status rozszerzenia", "External": "Zewnętrzny", + "meteor_status_reconnect_in_few": "spróbuj jeszcze raz za {{count}} sekund...", "External_Domains": "Domeny zewnętrzne", "External_Queue_Service_URL": "URL usługi kolejki zewnętrznej", "External_Service": "Usługa zewnętrzna", @@ -2130,7 +2135,6 @@ "Finish": "Koniec", "Finish_Registration": "Zakończ rejestrację", "First_Channel_After_Login": "Pierwszy Channel po zalogowaniu", - "message_counter_many": "{{count}} wiadomości", "First_response_time": "Czas pierwszej reakcji", "Flags": "Flagi", "Follow_message": "Śledź wiadomość", @@ -2193,7 +2197,6 @@ "get-password-policy-mustContainAtLeastOneUppercase": "Hasło powinno zawierać co najmniej jedną wielką literę", "get-server-info": "Pobierz info o serwerze", "get-server-info_description": "Pozwolenie na uzyskanie informacji o serwerze", - "meteor_status_reconnect_in_many": "spróbuj jeszcze raz za {{count}} sekund...", "github_no_public_email": "Nie posiadasz publicznego konta e-mail przypisanego do swojego profilu GitHub.", "github_HEAD": "HEAD", "Give_a_unique_name_for_the_custom_oauth": "Podaj unikalną nazwę dla własnego OAuth", @@ -2297,7 +2300,6 @@ "Ignore_Two_Factor_Authentication": "Ignorowanie uwierzytelniania dwuskładnikowego", "Images": "Obrazki", "IMAP_intercepter_already_running": "Intercom protokołu IMAP już działa", - "message_counter_few": "{{count}} wiadomości", "IMAP_intercepter_Not_running": "Intercom protokołu IMAP Nie działa", "Impersonate_next_agent_from_queue": "Podszywaj się pod kolejnego agenta z kolejki", "Impersonate_user": "Podszywać się pod użytkownika", @@ -2373,7 +2375,6 @@ "Install": "Zainstaluj", "Install_Extension": "Zainstaluj rozszerzenie", "Install_FxOs": "Zainstaluj Rocket.Chat w Firefoksie", - "meteor_status_reconnect_in_few": "spróbuj jeszcze raz za {{count}} sekund...", "Install_FxOs_done": "Świetnie! Możesz teraz włączać Rocket.Chat poprzez ikonę na ekranie głównym. Życzymy miłego korzystania z Rocket.Chat!", "Install_FxOs_error": "Niestety, coś nie zadziałało! Wystąpił następujący błąd:", "Install_FxOs_follow_instructions": "Potwierdź instalowanie aplikacji na twoim urządzeniu (gdy wyskoczy pytanie naciśnij przycisk \"Zainstaluj\").", @@ -3631,6 +3632,7 @@ "Privacy_policy": "Polityka Prywatności", "Privacy_summary": "Podsumowanie dotyczące prywatności", "Private": "Prywatny", + "Private_channels": "Kanały prywatne", "Private_Channel": "Prywatny kanał", "Private_Channels": "Kanały prywatne", "Private_Chats": "Prywatne czaty", @@ -5248,6 +5250,7 @@ "registration.component.form.username": "Nazwa użytkownika", "registration.component.form.name": "Nazwa", "registration.component.form.createAnAccount": "Utwórz konto", + "registration.component.form.userAlreadyExist": "Nazwa użytkownika już istnieje. Spróbuj użyć innej nazwy użytkownika.", "registration.component.form.emailAlreadyExists": "Ten email jest zajęty", "registration.component.form.usernameAlreadyExists": "Nazwa użytkownika już istnieje. Spróbuj użyć innej nazwy użytkownika.", "registration.component.form.invalidEmail": "E-mail jest nieprawidłowy", diff --git a/packages/i18n/src/locales/pt-BR.i18n.json b/packages/i18n/src/locales/pt-BR.i18n.json index 982f90b04157..0976838bf4bd 100644 --- a/packages/i18n/src/locales/pt-BR.i18n.json +++ b/packages/i18n/src/locales/pt-BR.i18n.json @@ -13,8 +13,8 @@ "__usersCount__member_joined_other": "+ {{count}} membros entraram", "__usersCount__people_will_be_invited": "{{usersCount}} usuários vão ser convidados", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} não pertence mais a {{role}}, por {{user_by}}", - "__usersCount__member_joined_many": "+ {{count}} membros entraram", "__username__was_set__role__by__user_by_": "{{username}} foi definido como {{role}} por {{user_by}}", + "__usersCount__member_joined_many": "+ {{count}} membros entraram", "__count__without__department__": "{{count}} sem departamento", "__count__without__tags__": "{{count}} sem tags", "__count__without__assignee__": "{{count}} sem responsável", @@ -442,6 +442,7 @@ "API_Shield_Types_Description": "Tipos de escudos para habilitar como uma lista separada por vírgulas; escolha entre `online`, `canal` ou `*` para todos", "Apps_Framework_Development_Mode": "Habilitar modo de desenvolvimento", "API_Shield_user_require_auth": "Exigir autenticaçāo para escudos de usuários", + "Calls_in_queue_many": "{{count}} chamadas na fila", "API_Token": "Token da API", "Apps_Framework_Development_Mode_Description": "O modo de desenvolvimento permite a instalação de aplicativos que não são do Marketplace do Rocket.Chat.", "API_Tokenpass_URL": "URL do servidor Tokenpass", @@ -485,6 +486,7 @@ "Apply_and_refresh_all_clients": "Aplicar e atualizar todos os clientes", "Apps": "Aplicativos", "Apps_context_installed": "Instalado", + "Apps_context_premium": "Enterprise", "Apps_Engine_Version": "Versão do mecanismo de aplicativos", "Apps_Essential_Alert": "Este aplicativo é essencial para os seguintes eventos:", "Apps_Essential_Disclaimer": "Eventos listados acima serão interrompidos se esse app for desativado. Se você quiser que o Rocket.Chat funcione sem a funcionalidade deste aplicativo, você precisará desinstalá-lo", @@ -521,7 +523,6 @@ "Apps_License_Message_appId": "A licença foi gerada para um app diferente", "Apps_License_Message_bundle": "A licença foi gerada para um Bundle que não contém este app", "Apps_License_Message_expire": "A licença não é mais válida e precisa ser renovada", - "Calls_in_queue_many": "{{count}} chamadas na fila", "Apps_License_Message_maxSeats": "A licença não comporta a quantidade atual de usuários ativos. Aumente o número de lugares.", "Apps_License_Message_publicKey": "Ocorreu um erro ao tentar descriptografar a licença. Sincronize seu espaço de trabalho em Serviços de conectividade e tente novamente", "Apps_License_Message_renewal": "A licença expirou e precisa ser renovada", @@ -1420,6 +1421,7 @@ "Device_Changes_Not_Available": "Mudanças de dispositivo não estão disponíveis neste navegador, para disponíbilidade garantida, use o aplicativo desktop oficial do Rocket.Chat.", "Device_Changes_Not_Available_Insecure_Context": "Mudanças de dispositivo somente estão disponíveis em contextos seguros. (https://)", "line": "linha", + "Device_Management_IP": "IP", "Different_Style_For_User_Mentions": "Estilo diferente para as menções do usuário", "Livechat_Facebook_API_Key": "Chave da API OmniChannel", "Livechat_Facebook_API_Secret": "Secret da API OmniChannel", @@ -1671,6 +1673,7 @@ "Enter_Normal": "Modo normal (enviar com Enter)", "Enter_to": "Enter para", "Enter_your_E2E_password": "Digite sua senha E2E", + "message_counter_many": "{{count}} mensagens", "Entertainment": "Entretenimento", "Error": "Erro", "Error_404": "Erro 404", @@ -1717,6 +1720,7 @@ "error-forwarding-department-target-not-allowed": "O encaminhamento para o departamento selecionado não é permitido.", "error-guests-cant-have-other-roles": "Usuários visitantes não podem ter nenhuma outra função.", "error-import-file-extract-error": "Falha ao extrair o arquivo importado.", + "meteor_status_reconnect_in_many": "tentando novamente em {{count}} segundos...", "error-import-file-is-empty": "Arquivo importado parece estar vazio.", "error-import-file-missing": "O arquivo a ser importado não foi encontrado no caminho especificado.", "error-importer-not-defined": "O importador não foi definido corretamente; está faltando a classe Import.", @@ -1911,6 +1915,7 @@ "FEDERATION_Test_Setup_Error": "Não foi possível encontrar seu servidor usando sua configuração. Revise suas configurações.", "FEDERATION_Test_Setup_Success": "Sua configuração de federação está funcionando e outros servidores podem encontrá-lo!", "Retry_Count": "Tentar contagem novamente", + "Federation_Matrix_enabled": "Ativado", "Field": "Campo", "Field_removed": "Campo removido", "Field_required": "Campo obrigatório", @@ -2003,7 +2008,6 @@ "Finish": "Finalizar", "Finish_Registration": "Finalizar registro", "First_Channel_After_Login": "Primeiro canal após o login", - "message_counter_many": "{{count}} mensagens", "First_response_time": "Tempo para primeira resposta", "Flags": "Sinalizações", "Follow_message": "Seguir mensagem", @@ -2065,7 +2069,6 @@ "get-password-policy-mustContainAtLeastOneSpecialCharacter": "A senha deve conter no mínimo um caractere especial", "get-password-policy-mustContainAtLeastOneUppercase": "A senha deve conter no mínimo um caractere maiúsculo", "get-server-info": "Obter informações do servidor", - "meteor_status_reconnect_in_many": "tentando novamente em {{count}} segundos...", "github_no_public_email": "Você não possui um e-mail público em sua conta do GitHub", "github_HEAD": "HEAD", "Give_a_unique_name_for_the_custom_oauth": "Dê um nome exclusivo para o oauth customizado", @@ -3407,6 +3410,7 @@ "Privacy": "Privacidade", "Privacy_Policy": "Política de privacidade", "Private": "Privado", + "Private_channels": "Canais privados", "Private_Channel": "Canal privado", "Private_Channels": "Canais privados", "Private_Chats": "Conversas privadas", @@ -3484,6 +3488,7 @@ "Reactions": "Reações", "Read_by": "Lido por", "Read_only": "Somente leitura", + "This_room_is_read_only": "Esta sala é somente leitura", "Read_only_changed_successfully": "Somente leitura foi alterado com sucesso", "Read_only_channel": "Canal somente leitura", "Read_only_group": "Grupo somente de leitura", @@ -3655,6 +3660,7 @@ "Unsafe_Url": "URL não seguro", "Rocket_Chat_Alert": "Alerta Rocket.Chat", "Role": "Função", + "Roles": "Funções", "Role_Editing": "Edição de função", "Role_Mapping": "Mapeamento de funções", "Role_removed": "Função removida", @@ -4423,6 +4429,7 @@ "unarchive-room": "Desarquivar Sala", "unarchive-room_description": "Permissão para desarquivar canais", "Unassigned": "Não atribuído", + "unauthorized": "Não autorizado", "Unavailable": "Indisponível", "Unblock_User": "Desbloquear usuário", "Uncheck_All": "Desmarcar todos", @@ -4875,6 +4882,7 @@ "Your_TOTP_has_been_reset": "Seu TOTP de dois fatores foi redefinido.", "Your_workspace_is_ready": "O seu espaço de trabalho está pronto para usar 🎉", "Zapier": "Zapier", + "registration.page.login.errors.wrongCredentials": "Usuário não encontrado ou senha incorreta", "registration.page.login.errors.loginBlockedForIp": "O login foi temporariamente bloqueado para este IP", "registration.page.login.errors.loginBlockedForUser": "O login foi temporariamente bloqueado para este Usuário", "registration.page.login.errors.licenseUserLimitReached": "O número máximo de usuários foi atingido.", @@ -4886,6 +4894,7 @@ "registration.component.resetPassword": "Redefinir senha", "registration.component.form.username": "Nome de usuário", "registration.component.form.name": "Nome", + "registration.component.form.userAlreadyExist": "O nome de usuário já existe. Tente outro nome de usuário.", "registration.component.form.emailAlreadyExists": "E-mail já existe", "registration.component.form.usernameAlreadyExists": "O nome de usuário já existe. Tente outro nome de usuário.", "registration.component.form.invalidEmail": "O e-mail informado é inválido", diff --git a/packages/i18n/src/locales/pt.i18n.json b/packages/i18n/src/locales/pt.i18n.json index 0d8b0884a477..d60379ef7ed9 100644 --- a/packages/i18n/src/locales/pt.i18n.json +++ b/packages/i18n/src/locales/pt.i18n.json @@ -390,6 +390,7 @@ "Apply_and_refresh_all_clients": "Aplicar e actualizar todos os clientes", "Apps": "Aplicações", "Apps_context_installed": "Instalado", + "Apps_context_premium": "Empreendimento", "Apps_Engine_Version": "Versão do Motor da Aplicação", "Apps_Marketplace_Deactivate_App_Prompt": "Queres mesmo desativar esta aplicação?", "Apps_Marketplace_Login_Required_Description": "A compra de aplicações do Marketplace Rocket.Chat requer o registo no sua área de trabalho e o login.", @@ -1326,6 +1327,7 @@ "FEDERATION_Public_Key_Description": "Esta é a chave que pode partilhar com outros.", "FEDERATION_Status": "Estado", "Retry_Count": "Contagem de tentativas", + "Federation_Matrix_enabled": "Activo", "Field": "Campo", "Field_removed": "Campo removido", "Field_required": "Campo requerido", @@ -1723,6 +1725,12 @@ "Layout_Terms_of_Service": "Termos de Serviço", "LDAP": "LDAP", "LDAP_Description": "LDAP é um banco de dados hierárquico que muitas empresas usam para fornecer single sign on - uma facilidade para compartilhar uma senha entre múltiplos sites e serviços. Para exemplos e informações de configurações avançadas, por favor consulte nosso wiki: https://rocket.chat/docs/administrator-guides/authentication/ldap/", + "LDAP_Connection_Encryption": "Criptografia", + "LDAP_Connection_Timeouts": "Tempos limite", + "LDAP_UserSearch": "Pesquisa de usuário", + "LDAP_DataSync_BackgroundSync": "Sincronização fundo", + "LDAP_Server_Type": "Tipo de servidor", + "LDAP_Server_Type_Other": "Outro", "LDAP_Authentication": "Habilitar", "LDAP_Authentication_Password": "Senha", "LDAP_Authentication_UserDN": "DN do utilizador", @@ -3070,6 +3078,8 @@ "Visitor_Navigation": "Navegação do convidado", "Visitor_page_URL": "URL da página do convidado", "Visitor_time_on_site": "Tempo do convidado no site", + "VoIP_Management_Server_Username": "Nome de utilizador", + "VoIP_Management_Server_Password": "Palavra-passe", "Wait_activation_warning": "Antes de se conectar, a sua conta deverá ser manualmente activa por um administrador.", "Warnings": "Alertas", "We_are_offline_Sorry_for_the_inconvenience": "Estamos offline. Pedimos desculpa pela inconveniência.", @@ -3165,6 +3175,7 @@ "registration.component.form.emailOrUsername": "Email ou nome de utilizador", "registration.component.form.username": "Nome de utilizador", "registration.component.form.name": "Nome", + "registration.component.form.userAlreadyExist": "O nome de utilizador já existe. Por favor, tente outro nome de utilizador.", "registration.component.form.emailAlreadyExists": "Email já registado", "registration.component.form.usernameAlreadyExists": "O nome de utilizador já existe. Por favor, tente outro nome de utilizador.", "registration.component.form.invalidEmail": "O email introduzido é inválido", diff --git a/packages/i18n/src/locales/ro.i18n.json b/packages/i18n/src/locales/ro.i18n.json index ab8234afc67b..21e950c87678 100644 --- a/packages/i18n/src/locales/ro.i18n.json +++ b/packages/i18n/src/locales/ro.i18n.json @@ -320,6 +320,7 @@ "Apply": "aplica", "Apply_and_refresh_all_clients": "Se aplică și actualizați toți clienții", "Apps": "Aplicații", + "Apps_context_premium": "Afacere", "Apps_Settings": "Setările aplicației", "Apps_WhatIsIt": "Aplicații: Ce sunt acestea?", "Apps_WhatIsIt_paragraph1": "O nouă pictogramă în zona de administrare! Ce înseamnă aceasta și ce sunt aplicațiile?", @@ -1098,6 +1099,7 @@ "FEDERATION_Domain": "Domeniu", "FEDERATION_Status": "Stare", "Retry_Count": "Retry Count", + "Federation_Matrix_enabled": "Activat", "Field": "Camp", "Field_removed": "câmp îndepărtat", "Field_required": "Câmp obligatoriu", @@ -1456,6 +1458,10 @@ "Layout_Sidenav_Footer_Dark_description": "Dimensiunea footer-ului este de 260 x 70px", "Layout_Terms_of_Service": "Condții de utilizare", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "Encriptare", + "LDAP_DataSync_BackgroundSync": "Background Sync", + "LDAP_Server_Type": "Tip server", + "LDAP_Server_Type_Other": "Alte", "LDAP_Authentication": "Permite", "LDAP_Authentication_Password": "Parolă", "LDAP_Authentication_UserDN": "DN utilizator", @@ -1986,6 +1992,7 @@ "Reactions": "Reacții", "Read_by": "Citește de", "Read_only": "Numai citire", + "This_room_is_read_only": "Această cameră este numai pentru citire", "Read_only_changed_successfully": "Citirea a fost modificată cu succes", "Read_only_channel": "Numai canal de citire", "Read_only_group": "Citiți numai grupul", @@ -2661,6 +2668,8 @@ "Visitor_Navigation": "Navigare vizitator", "Visitor_page_URL": "Pagina URL a vizitatorului", "Visitor_time_on_site": "Timpul vizitatorului pe site", + "VoIP_Management_Server_Username": "Utilizator", + "VoIP_Management_Server_Password": "Parolă", "Wait_activation_warning": "Ca să vă puteți autentifica, contul dumneavoastră trebuie să fie activat manual de către un administrator.", "Warnings": "Avertismente", "We_are_offline_Sorry_for_the_inconvenience": "Suntem offline. Scuze pentru neplăcerile provocate.", @@ -2742,6 +2751,7 @@ "registration.component.resetPassword": "Resetează parola", "registration.component.form.username": "Utilizator", "registration.component.form.name": "Nume", + "registration.component.form.userAlreadyExist": "Nume de utilizator deja existent. Încercați un alt nume de utilizator.", "registration.component.form.emailAlreadyExists": "Adresa de e-mail există deja", "registration.component.form.usernameAlreadyExists": "Nume de utilizator deja existent. Încercați un alt nume de utilizator.", "registration.component.form.invalidEmail": "Adresa de email folosită este invalidă", diff --git a/packages/i18n/src/locales/ru.i18n.json b/packages/i18n/src/locales/ru.i18n.json index da6f72b0e15b..b014b6da9d56 100644 --- a/packages/i18n/src/locales/ru.i18n.json +++ b/packages/i18n/src/locales/ru.i18n.json @@ -1,7 +1,7 @@ { "500": "Внутренняя ошибка сервера", - "__count__empty_rooms_will_be_removed_automatically": "{{count}}пустые комнаты будут удалены автоматически.", "__count__message_pruned_few": "{{count}} сообщений удалено", + "__count__empty_rooms_will_be_removed_automatically": "{{count}}пустые комнаты будут удалены автоматически.", "__count__empty_rooms_will_be_removed_automatically__rooms__": "{{count}} пустых чатов будет удалено автоматически:
          {{rooms}}.", "__count__message_pruned_one": "{{count}} сообщение удалено", "__count__message_pruned_other": "{{count}} сообщений удалено", @@ -11,8 +11,8 @@ "__usersCount__member_joined_other": "+ {{count}} участников присоединилось", "__usersCount__people_will_be_invited": "{{usersCount}} человек будет приглашено", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} больше не {{role}} по решению {{user_by}}", - "__usersCount__member_joined_many": "+ {{count}} участников присоединилось", "__username__was_set__role__by__user_by_": "{{username}} был установлен {{role}} по решению {{user_by}}", + "__usersCount__member_joined_many": "+ {{count}} участников присоединилось", "This_room_encryption_has_been_enabled_by__username_": "Шифрование этой комнаты было включено {{username}}", "This_room_encryption_has_been_disabled_by__username_": "Шифрование этой комнаты было отключено {{username}}", "Enabled_E2E_Encryption_for_this_room": "включено шифрование E2E для этой комнаты", @@ -385,9 +385,11 @@ "Allow_Invalid_SelfSigned_Certs_Description": "Разрешить недействительные и самоподписанные SSL сертификаты для проверки ссылок и предосмотра.", "Allow_Marketing_Emails": "Разрешить маркетинговые E-mail", "Allow_Online_Agents_Outside_Business_Hours": "Разрешить агентов в сети вне рабочих часов", + "Apps_Count_Enabled_many": "{{count}} приложений(-я) включено", "Allow_Online_Agents_Outside_Office_Hours": "Разрешить агентов в сети вне рабочего времени", "Allow_Save_Media_to_Gallery": "Разрешить сохранять медиа данные в галерее", "Allow_switching_departments": "Разрешить посетителю сменить отдел", + "Private_Apps_Count_Enabled_many": "{{count}} приватных приложений включено", "Almost_done": "Почти сделано", "Alphabetical": "По алфавиту", "bold": "жирный", @@ -415,11 +417,13 @@ "API_Analytics": "Аналитика", "API_CORS_Origin": "Заголовок CORS Origin", "API_Apply_permission_view-outside-room_on_users-list": "Примените разрешение `view-outside-room` к api `users.list`", + "Apps_Count_Enabled_few": "{{count}} приложений(-я) включено", "API_Apply_permission_view-outside-room_on_users-list_Description": "Временная настройка для принудительного разрешения. Будет удалена в следующем крупном обновлении в рамках изменения для постоянного применения разрешения", "API_Default_Count": "Количество по-умолчанию", "API_Default_Count_Description": "Количество результатов REST API для использования по-умолчанию, если потребитель не указал его.", "API_Drupal_URL": "URL сервера Drupal", "API_Drupal_URL_Description": "Пример: `https://domain.com` (без слэша в конце)", + "Private_Apps_Count_Enabled_few": "{{count}} приватных приложений включено", "API_Embed": "Встроенный просмотр ссылок", "API_Embed_Description": "Включить ли предварительный просмотр ссылок, когда пользователь выкладывает ссылку на веб-сайт.", "API_EmbedIgnoredHosts": "Вставить игнорируемые хосты", @@ -446,21 +450,18 @@ "API_GitHub_Enterprise_URL_Description": "Пример: `https://domain.com` (без завершающего слеша)", "API_Gitlab_URL": "GitLab URL", "API_Personal_Access_Token_Generated": "Идентификатор персонального доступа успешно сгенерирован", - "Apps_Count_Enabled_few": "{{count}} приложений(-я) включено", "API_Personal_Access_Token_Generated_Text_Token_s_UserId_s": "Пожалуйста, аккуратно сохраните токен, так как вы больше не сможете его просматривать.
          Токен: {{token}}
          Ваш Id пользователя: {{userId}} ", - "Apps_Count_Enabled_many": "{{count}} приложений(-я) включено", "API_Personal_Access_Token_Name": "Идентификатор имени персонального доступа", "API_Personal_Access_Tokens_Regenerate_It": "Пересоздать токен", - "Private_Apps_Count_Enabled_few": "{{count}} приватных приложений включено", "API_Personal_Access_Tokens_Regenerate_Modal": "Если вы потеряли или забыли свой токен, вы можете его восстановить, но помните, что все приложения, использующие этот токен, должны быть обновлены", "API_Personal_Access_Tokens_Remove_Modal": "Вы действительно хотите удалить этот токен доступа?", "API_Personal_Access_Tokens_To_REST_API": "Личные токены доступа к rest API", - "Private_Apps_Count_Enabled_many": "{{count}} приватных приложений включено", "API_Rate_Limiter": "Ограничение частоты запросов к API", "API_Shield_Types": "Типы бейджей", "API_Shield_Types_Description": "Типы бейджей в виде списка с разделением запятой, выберите `online`, `channel` либо используйте `*` для выбора всех", "Apps_Framework_Development_Mode": "Включить режим разработки", "API_Shield_user_require_auth": "Требование аутентификации для users shields", + "Calls_in_queue_many": "{{count}} Звонков в очереди", "API_Token": "API Токен", "Apps_Framework_Development_Mode_Description": "Режим разработки позволяет устанавливать Приложения не из Торговой площадки Rocket.Chat.", "API_Tokenpass_URL": "Tokenpass Server URL", @@ -498,6 +499,7 @@ "App_support_url": "URL поддержки", "App_Url_to_Install_From": "Установить с URL", "App_Url_to_Install_From_File": "Установить из файла", + "Calls_in_queue_few": "{{count}} Звонков в очереди", "App_user_not_allowed_to_login": "Пользователям приложений не разрешается напрямую входить в систему.", "Appearance": "Внешний вид", "Application_added": "Приложение добавлено", @@ -510,6 +512,7 @@ "Apps_context_installed": "Установлен", "Apps_context_requested": "Запрошено", "Apps_context_private": "Приватные приложения", + "Apps_context_premium": "Корпорация", "Apps_Count_Enabled_one": "{{count}} приложение включено", "Apps_Count_Enabled_other": "{{count}} приложений(-я) включено", "Private_Apps_Count_Enabled_one": "{{count}} приватное приложение включено", @@ -551,7 +554,6 @@ "Apps_License_Message_appId": "Лицензия на это приложение не выдавалась", "Apps_License_Message_bundle": "Лицензия выдана на пакет, не содержащий приложение", "Apps_License_Message_expire": "Лицензия больше не действительна и нуждается в продлении", - "Calls_in_queue_many": "{{count}} Звонков в очереди", "Apps_License_Message_maxSeats": "Лицензия не рассчитана на текущее количество активных пользователей. Пожалуйста, увеличьте количество мест", "Apps_License_Message_publicKey": "Произошла ошибка при попытке расшифровать лицензию. Пожалуйста, синхронизируйте рабочее пространство в службах подключения и повторите попытку", "Apps_License_Message_renewal": "Срок действия лицензии истек и ее необходимо продлить", @@ -603,7 +605,6 @@ "Apps_Permissions_livechat-message_read": "Доступ к информации о сообщениях Livechat", "Apps_Permissions_livechat-message_write": "Изменение информации о сообщениях Livechat", "Apps_Permissions_livechat-room_read": "Доступ к информации о чате Livechat", - "Calls_in_queue_few": "{{count}} Звонков в очереди", "Apps_Permissions_livechat-room_write": "Изменение информации о чате Livechat", "Apps_Permissions_livechat-department_read": "Доступ к информации отдела Livechat", "Apps_Permissions_livechat-department_multiple": "Доступ к информации нескольких отделов Livechat", @@ -1768,6 +1769,7 @@ "Enter_Normal": "Обычный режим (отправка по Enter)", "Enter_to": "Войти в", "Enter_your_E2E_password": "Введите пароль E2E", + "message_counter_many": "{{count}} сообщения", "Entertainment": "Развлечения", "Error": "Ошибка", "Error_something_went_wrong": "Ой! Что-то пошло не так. Пожалуйста, перезагрузите страницу или обратитесь к администратору.", @@ -1816,6 +1818,7 @@ "error-forwarding-department-target-not-allowed": "Переадресация в целевой отдел не допускается.", "error-guests-cant-have-other-roles": "Гостевые пользователи не могут иметь других ролей.", "error-import-file-extract-error": "Не удалось извлечь файл импорта.", + "meteor_status_reconnect_in_many": "пытается снова через {{count}} секунд ...", "error-import-file-is-empty": "Импортированный файл кажется пустым.", "error-import-file-missing": "Импортируемый файл не найден по указанному пути.", "error-importer-not-defined": "Импортер не был определен правильно, отсутствует класс импорта.", @@ -1910,6 +1913,7 @@ "error-this-is-not-a-livechat-room": "Это не Livechat комната", "error-token-already-exists": "Токен с этим именем уже существует", "error-token-does-not-exists": "Токен не существует", + "message_counter_few": "{{count}} сообщения", "error-too-many-requests": "Ошибка, слишком много запросов. вы должны подождать {{seconds}} секунд перед тем, как попробовать снова.", "error-transcript-already-requested": "Транскрипт уже запрошен", "error-unpinning-message": "Сообщение не может быть откреплено", @@ -1962,6 +1966,7 @@ "Extension_Number": "Номер расширения", "Extension_Status": "Статус расширения", "External": "Внешний", + "meteor_status_reconnect_in_few": "пытается снова через {{count}} секунд ...", "External_Domains": "Внешние Домены", "External_Queue_Service_URL": "URL внешнего сервера очередей", "External_Service": "Внешний сервис", @@ -2010,6 +2015,7 @@ "FEDERATION_Test_Setup_Success": "Ваша федерация работает, и другие серверы могут найти вас!", "Retry_Count": "Число повторных попыток", "Federation_Matrix": "Федерация V2", + "Federation_Matrix_enabled": "Включено", "Field": "Поле", "Field_removed": "Поле удалено", "Field_required": "Обязательное поле", @@ -2104,7 +2110,6 @@ "Finish": "Завершить", "Finish_Registration": "Завершение регистрации", "First_Channel_After_Login": "Открыть канал после авторизации", - "message_counter_many": "{{count}} сообщения", "First_response_time": "Время первого ответа", "Flags": "Флаги", "Follow_message": "Подписаться на сообщение", @@ -2163,7 +2168,6 @@ "get-password-policy-mustContainAtLeastOneSpecialCharacter": "Пароль должен содержать как минимум один специальный символ", "get-password-policy-mustContainAtLeastOneUppercase": "Пароль должен содержать как минимум одну заглавную букву", "get-server-info": "Получить информацию о сервере", - "meteor_status_reconnect_in_many": "пытается снова через {{count}} секунд ...", "github_no_public_email": "В вашей учетной записи GitHub отсутствует публично доступный адрес электронной почты", "github_HEAD": "HEAD", "Give_a_unique_name_for_the_custom_oauth": "Выберите имя OAuth сервиса", @@ -2261,7 +2265,6 @@ "Ignore_Two_Factor_Authentication": "Игнорировать двухфакторную авторизацию", "Images": "Изображения", "IMAP_intercepter_already_running": "Перехватчик IMAP уже запущен", - "message_counter_few": "{{count}} сообщения", "IMAP_intercepter_Not_running": "Перехватчик IMAP не запущен", "Impersonate_next_agent_from_queue": "Выполните олицетворение следующего агента из очереди", "Impersonate_user": "Представляться пользователем", @@ -2337,7 +2340,6 @@ "Install": "Устанавить", "Install_Extension": "Установить расширение", "Install_FxOs": "Установить Rocket.Chat на Firefox", - "meteor_status_reconnect_in_few": "пытается снова через {{count}} секунд ...", "Install_FxOs_done": "Отлично! Теперь вы можете использовать Rocket.Chat через иконку на вашем рабочем столе. Развлекайтесь вместе с Rocket.Chat!", "Install_FxOs_error": "Извините, что-то пошло не так! Появилась следующая ошибка:", "Install_FxOs_follow_instructions": "Подтвердите установку приложения на ваше устройство (при запросе нажмите \"Установить\").", @@ -3374,6 +3376,7 @@ "Organization_Name": "Название организации", "Organization_Type": "Тип организации", "Original": "Оригинальный", + "OS": "ОС", "OS_Arch": "Архитектура ОС", "OS_Cpus": "Количество процессоров в ОС", "OS_Freemem": "Свободное количество памяти", @@ -3504,6 +3507,7 @@ "Privacy": "Приватность", "Privacy_Policy": "Политика конфиденциальности", "Private": "Закрытый канал", + "Private_channels": "Закрытый канал", "Private_Apps": "Приватные приложения", "Private_Channel": "Закрытый канал", "Private_Channels": "Закрытый канал", @@ -3581,6 +3585,7 @@ "Reactions": "Реакции", "Read_by": "Читать", "Read_only": "Только для чтения", + "This_room_is_read_only": "Этот чат доступен только для чтения", "Read_only_changed_successfully": "Режим \"только для чтения\" успешно изменен", "Read_only_channel": "Канал только для чтения", "Read_only_group": "Группа только для чтения", @@ -3746,6 +3751,7 @@ "Unsafe_Url": "Небезопасный URL", "Rocket_Chat_Alert": "Rocket.Chat Alert", "Role": "Роль", + "Roles": "Роли", "Role_Editing": "Редактировать роль", "Role_Mapping": "Сопоставление ролей", "Role_removed": "Роль удалена", @@ -4990,6 +4996,7 @@ "registration.component.resetPassword": "Восстановить пароль", "registration.component.form.username": "Имя пользователя", "registration.component.form.name": "Имя", + "registration.component.form.userAlreadyExist": "Такой пользователь уже существует. Пожалуйста, выберите другое имя.", "registration.component.form.emailAlreadyExists": "Такой адрес электронной почты уже используется", "registration.component.form.usernameAlreadyExists": "Такой пользователь уже существует. Пожалуйста, выберите другое имя.", "registration.component.form.invalidEmail": "Введен некорректный адрес электронной почты", diff --git a/packages/i18n/src/locales/sk-SK.i18n.json b/packages/i18n/src/locales/sk-SK.i18n.json index ed0b76c0e3b2..8e80bd7a0aff 100644 --- a/packages/i18n/src/locales/sk-SK.i18n.json +++ b/packages/i18n/src/locales/sk-SK.i18n.json @@ -322,6 +322,7 @@ "Apply": "Použiť", "Apply_and_refresh_all_clients": "Použiť a obnoviť všetkých klientov", "Apps": "aplikácie", + "Apps_context_premium": "podnik", "Apps_Settings": "Nastavenia aplikácií", "Apps_WhatIsIt": "Aplikácie: Čo sú zač?", "Apps_WhatIsIt_paragraph1": "Nová ikona v administratívnej oblasti! Čo to znamená a čo sú aplikácie?", @@ -1109,6 +1110,7 @@ "FEDERATION_Domain": "Doména", "FEDERATION_Status": "Postavenie", "Retry_Count": "Opakovať počet", + "Federation_Matrix_enabled": "povolené", "Field": "Lúka", "Field_removed": "Pole bolo odstránené", "Field_required": "Vyžaduje sa pole", @@ -1467,6 +1469,10 @@ "Layout_Sidenav_Footer_Dark_description": "Veľkosť päty je 260 x 70 pixlov", "Layout_Terms_of_Service": "Podmienky služby", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "šifrovanie", + "LDAP_DataSync_BackgroundSync": "Synchronizácia pozadia", + "LDAP_Server_Type": "Typ servera", + "LDAP_Server_Type_Other": "ostatné", "LDAP_Authentication": "umožniť", "LDAP_Authentication_Password": "Heslo", "LDAP_Authentication_UserDN": "DN užívateľa", @@ -1996,6 +2002,7 @@ "Reactions": "reakcie", "Read_by": "Prečítajte si", "Read_only": "Iba na čítanie", + "This_room_is_read_only": "Táto miestnosť je iba na čítanie", "Read_only_changed_successfully": "Čítanie bolo úspešne zmenené", "Read_only_channel": "Kanál iba na čítanie", "Read_only_group": "Skupina iba na čítanie", @@ -2496,6 +2503,7 @@ "Unarchive": "Zrušiť archiváciu", "unarchive-room": "Rozbaľovacia miestnosť", "unarchive-room_description": "Povolenie na dearchizáciu kanálov", + "unauthorized": "Nie je povolené", "Unblock_User": "Odblokovať používateľa", "Unignore": "prestať ignorovať", "Uninstall": "Uninstall", @@ -2671,6 +2679,8 @@ "Visitor_Navigation": "Navigácia pre návštevníkov", "Visitor_page_URL": "Adresa URL stránky návštevníka", "Visitor_time_on_site": "Čas návštevnosti na stránkach", + "VoIP_Management_Server_Username": "Používateľské meno", + "VoIP_Management_Server_Password": "Heslo", "Wait_activation_warning": "Skôr ako sa môžete prihlásiť, váš účet musí byť ručne aktivovaný správcom.", "Warnings": "varovanie", "We_are_offline_Sorry_for_the_inconvenience": "Sme offline. Ospravedlňujem sa za nepríjemnosť.", @@ -2745,6 +2755,7 @@ "Your_push_was_sent_to_s_devices": "Posun bol odoslaný na zariadenia%s", "Your_server_link": "Váš odkaz na server", "Your_workspace_is_ready": "Váš pracovný priestor je pripravený na použitie 🎉", + "registration.page.login.errors.wrongCredentials": "Používateľ nebol nájdený alebo nesprávne heslo", "registration.page.registration.waitActivationWarning": "Skôr ako sa môžete prihlásiť, váš účet musí byť ručne aktivovaný správcom.", "registration.page.resetPassword.sent": "Ak je tento e-mail zaregistrovaný, pošleme pokyny na obnovenie hesla. Ak v krátkom čase nedostanete e-mail, vráťte sa a skúste to znova.", "registration.component.login": "Prihlásiť sa", @@ -2752,6 +2763,7 @@ "registration.component.resetPassword": "Obnoviť heslo", "registration.component.form.username": "Používateľské meno", "registration.component.form.name": "Názov", + "registration.component.form.userAlreadyExist": "Užívateľské meno už existuje. Skúste iné používateľské meno.", "registration.component.form.emailAlreadyExists": "Email už existuje", "registration.component.form.usernameAlreadyExists": "Užívateľské meno už existuje. Skúste iné používateľské meno.", "registration.component.form.invalidEmail": "Zadaný e-mail je neplatný", diff --git a/packages/i18n/src/locales/sl-SI.i18n.json b/packages/i18n/src/locales/sl-SI.i18n.json index 781710d36d50..d08c083ee9ce 100644 --- a/packages/i18n/src/locales/sl-SI.i18n.json +++ b/packages/i18n/src/locales/sl-SI.i18n.json @@ -318,6 +318,7 @@ "Apply": "Uporabi", "Apply_and_refresh_all_clients": "Dodaj in osveži vse stranke", "Apps": "Aplikacije", + "Apps_context_premium": "Podjetje", "Apps_Settings": "Nastavitve aplikacije", "Apps_WhatIsIt": "Aplikacije: kaj so?", "Apps_WhatIsIt_paragraph1": "Nova ikona v upravnem območju! Kaj to pomeni in kakšne so aplikacije?", @@ -1090,6 +1091,7 @@ "FEDERATION_Domain": "Domena", "FEDERATION_Status": "Stanje", "Retry_Count": "Število ponovnih poskusov", + "Federation_Matrix_enabled": "Omogočeno", "Field": "Polje", "Field_removed": "Polje je odstranjeno", "Field_required": "Polje je zahtevano", @@ -1447,6 +1449,10 @@ "Layout_Sidenav_Footer_Dark_description": "Velikost noge 260 x 70 px", "Layout_Terms_of_Service": "Pogoji storitev", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "Šifriranje", + "LDAP_DataSync_BackgroundSync": "Sinhronizacija v ozadju", + "LDAP_Server_Type": "Vrsta strežnika", + "LDAP_Server_Type_Other": "Drugo", "LDAP_Authentication": "Omogči", "LDAP_Authentication_Password": "Geslo", "LDAP_Authentication_UserDN": "Uporabnik DN", @@ -1976,6 +1982,7 @@ "Reactions": "Odzivi", "Read_by": "Prebrano", "Read_only": "Samo za branje", + "This_room_is_read_only": "Ta soba je samo za branje", "Read_only_changed_successfully": "Uspešno spremenjeno v samo za branje", "Read_only_channel": "Kanal samo za branje", "Read_only_group": "Skupina samo za branje", @@ -2476,6 +2483,7 @@ "Unarchive": "Odarhiviraj", "unarchive-room": "Odarhiviraj sobo", "unarchive-room_description": "Dovoljenje za odarhiviranje kanalov", + "unauthorized": "Brez pooblastila ", "Unblock_User": "Odblokiraj uporabnika", "Unignore": "Unignore", "Uninstall": "Odstrani", @@ -2651,6 +2659,8 @@ "Visitor_Navigation": "Navigacija obiskovalcev", "Visitor_page_URL": "URL spletnega mesta za obiskovalce", "Visitor_time_on_site": "Čas, ki ga je obiskovalec porabil na spletni strani", + "VoIP_Management_Server_Username": "Uporabniško ime", + "VoIP_Management_Server_Password": "Geslo", "Wait_activation_warning": "Preden se lahko prijavite, mora skrbnik ročno aktivirati vaš račun.", "Warnings": "Opozorila", "We_are_offline_Sorry_for_the_inconvenience": "Brez povezave. Oprostite za nevšečnosti.", @@ -2725,6 +2735,7 @@ "Your_push_was_sent_to_s_devices": "Vaš potisk je bil poslan v naprave %s", "Your_server_link": "Povezava strežnika", "Your_workspace_is_ready": "Vaš delovni prostor je pripravljen za uporabo 🎉", + "registration.page.login.errors.wrongCredentials": "Napačno geslo ali neznan uporabnik", "registration.page.registration.waitActivationWarning": "Preden se lahko prijavite, mora skrbnik ročno aktivirati vaš račun.", "registration.page.resetPassword.sent": "Če je ta e-poštni naslov registriran, vam bomo poslali navodila za ponastavitev gesla. Če elektronske pošte ne boste prejeli v kratkem, prosimo, poskusite znova. ", "registration.component.login": "Prijava", @@ -2732,6 +2743,7 @@ "registration.component.resetPassword": "Ponastavitev gesla", "registration.component.form.username": "Uporabniško ime", "registration.component.form.name": "Ime", + "registration.component.form.userAlreadyExist": "Uporabniško ime že obstaja. Poskusite drugo uporabniško ime.", "registration.component.form.emailAlreadyExists": "E-potšni naslov že obstaja", "registration.component.form.usernameAlreadyExists": "Uporabniško ime že obstaja. Poskusite drugo uporabniško ime.", "registration.component.form.invalidEmail": "Vneseni e-poštni naslov je neveljaven", diff --git a/packages/i18n/src/locales/sq.i18n.json b/packages/i18n/src/locales/sq.i18n.json index 202d02670595..80ff384d4cc4 100644 --- a/packages/i18n/src/locales/sq.i18n.json +++ b/packages/i18n/src/locales/sq.i18n.json @@ -320,6 +320,7 @@ "Apply": "aplikoni", "Apply_and_refresh_all_clients": "Aplikoni dhe rifreskoni të gjithë klientët", "Apps": "Apps", + "Apps_context_premium": "Ndërmarrje", "Apps_Settings": "Cilësimet e aplikacionit", "Apps_WhatIsIt": "Aplikacionet: Cilat janë ato?", "Apps_WhatIsIt_paragraph1": "Një ikonë e re në zonën e administratës! Çfarë do të thotë kjo dhe cilat janë Apps?", @@ -1100,6 +1101,7 @@ "FEDERATION_Domain": "fushë", "FEDERATION_Status": "status", "Retry_Count": "Përsëritni Numrin", + "Federation_Matrix_enabled": "enabled", "Field": "fushë", "Field_removed": "Field hequr", "Field_required": "Fusha e kërkuar", @@ -1456,6 +1458,10 @@ "Layout_Sidenav_Footer_Dark_description": "Madhësia Footer është 260 x 70px", "Layout_Terms_of_Service": "Kushtet e Shërbimit", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "encryption", + "LDAP_DataSync_BackgroundSync": "Sinkronizimi i sfondit", + "LDAP_Server_Type": "Lloji i serverit", + "LDAP_Server_Type_Other": "tjetër", "LDAP_Authentication": "mundësoj", "LDAP_Authentication_Password": "Fjalëkalim", "LDAP_Authentication_UserDN": "Përdoruesi DN", @@ -1986,6 +1992,7 @@ "Reactions": "reagimet", "Read_by": "Lexo nga", "Read_only": "Lexo vetem", + "This_room_is_read_only": "Kjo dhomë lexohet vetëm", "Read_only_changed_successfully": "Lexo vetëm ndryshoi me sukses", "Read_only_channel": "Lexo vetëm kanalin", "Read_only_group": "Lexo Grupin Vetëm", @@ -2662,6 +2669,8 @@ "Visitor_Navigation": "Visitor Navigation", "Visitor_page_URL": "faqe Visitor URL", "Visitor_time_on_site": "Ora Visitor në faqen", + "VoIP_Management_Server_Username": "Emri i përdoruesit", + "VoIP_Management_Server_Password": "Fjalëkalim", "Wait_activation_warning": "Para se ju të identifikoheni, llogaria juaj duhet të aktivizohet nga një administrator.", "Warnings": "Paralajmërimet", "We_are_offline_Sorry_for_the_inconvenience": "Ne jemi offline. Na vjen keq për bezdisjen.", @@ -2744,6 +2753,7 @@ "registration.component.resetPassword": "Rivendos fjalëkalimin", "registration.component.form.username": "Emri i përdoruesit", "registration.component.form.name": "Emër", + "registration.component.form.userAlreadyExist": "Emri i përdoruesit tashmë ekziston. Të lutem provo një tjetër emër përdoruesi.", "registration.component.form.emailAlreadyExists": "Emaili ekziston", "registration.component.form.usernameAlreadyExists": "Emri i përdoruesit tashmë ekziston. Të lutem provo një tjetër emër përdoruesi.", "registration.component.form.invalidEmail": "Email-i vendosur është i pavlefshëm", diff --git a/packages/i18n/src/locales/sr.i18n.json b/packages/i18n/src/locales/sr.i18n.json index 80c9b9ae65fb..adec89c9568b 100644 --- a/packages/i18n/src/locales/sr.i18n.json +++ b/packages/i18n/src/locales/sr.i18n.json @@ -82,6 +82,7 @@ "Accounts_OAuth_GitHub_Enterprise_secret": "Тајна клијента", "Accounts_OAuth_Github_id": "Ид клијента", "Accounts_OAuth_Github_secret": "Тајна клијента", + "Accounts_OAuth_Gitlab": "ОАутх Омогућено", "Accounts_OAuth_Gitlab_identity_path": "Путања до идентитета", "Accounts_OAuth_Gitlab_secret": "Тајна клијента", "Accounts_OAuth_Google_callback_url": "Гоогле УРЛ за повратни позив", @@ -255,6 +256,7 @@ "Apply": "Примени", "Apply_and_refresh_all_clients": "Примени и освежи све клијенте", "Apps": "Програми", + "Apps_context_premium": "Предузеће", "Apps_Settings": "Подешавања апликације", "Apps_WhatIsIt": "Апликације: Шта су оне?", "Apps_WhatIsIt_paragraph1": "Нова икона у области администрације! Шта ово значи и шта су апликације?", @@ -998,6 +1000,7 @@ "FEDERATION_Domain": "Домен", "FEDERATION_Status": "Стање", "Retry_Count": "Покушајте поново", + "Federation_Matrix_enabled": "Оmogućeno", "Field": "Поље", "Field_removed": "Поље уклоњено", "Field_required": "Неопходно поље", @@ -1017,6 +1020,7 @@ "FileUpload_Error": "Грешка при постављању датотеке", "FileUpload_File_Empty": "Датотека је празна", "FileUpload_FileSystemPath": "Системска путања", + "FileUpload_GoogleStorage_Proxy_Avatars": "Проки Аватарс", "FileUpload_GoogleStorage_Secret": "Гоогле Стораге Сецрет", "FileUpload_GoogleStorage_Secret_Description": "Пратите [ова упутства](https://github.com/CulturalMe/meteor-slingshot#google-cloud) и залепите резултат овде.", "FileUpload_MaxFileSize": "Максимална величина уплоад сизе (ин битес)", @@ -1308,6 +1312,10 @@ "Layout_Sidenav_Footer_Dark_description": "Фоотер је величине 260 х 70 пиксела", "Layout_Terms_of_Service": "Услови коришћења", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "Шифровање", + "LDAP_DataSync_BackgroundSync": "Синхронизација позадине", + "LDAP_Server_Type": "Тип сервера", + "LDAP_Server_Type_Other": "Друго", "LDAP_Authentication": "Омогући", "LDAP_Authentication_Password": "Лозинка", "LDAP_Authentication_UserDN": "Корисник ДН", @@ -1352,6 +1360,7 @@ "LDAP_Login_Fallback_Description": "Ако пријава на ЛДАП-у није успешна, покушајте да се пријавите у систему подразумеваног / локалног налога. Помаже када је ЛДАП из неког разлога пао.", "LDAP_Merge_Existing_Users": "Споји постојеће кориснике", "LDAP_Merge_Existing_Users_Description": "* Опрез! * Када увозите корисника из ЛДАП-а и корисник са истим корисничким именом већ постоји, ЛДАП инфо и лозинка ће бити постављени у постојећи корисник.", + "LDAP_Port": "лука", "LDAP_Reconnect": "Поново повежите", "LDAP_Reconnect_Description": "Покушајте да се аутоматски повежете када је веза прекинута из неког разлога током извршавања операција", "LDAP_Reject_Unauthorized": "Одбиј неовлашћено", @@ -1817,6 +1826,7 @@ "Reactions": "reakcije", "Read_by": "Прочитајте", "Read_only": "Само за читање", + "This_room_is_read_only": "Ова соба је само за читање", "Read_only_changed_successfully": "Читање је само успешно промењено", "Read_only_channel": "Само за читање канала", "Read_only_group": "Група само за читање", @@ -2472,6 +2482,8 @@ "Visitor_Navigation": "посетилац Навигација", "Visitor_page_URL": "Посетилац веб страница", "Visitor_time_on_site": "Посетилац време на сајту", + "VoIP_Management_Server_Username": "Корисничко име", + "VoIP_Management_Server_Password": "Лозинка", "Wait_activation_warning": "Пре него што се пријавите, ваш налог мора да буде ручно активиран од стране администратора.", "Warnings": "Упозорења", "We_are_offline_Sorry_for_the_inconvenience": "Ми смо онлине. Жао због непријатности.", @@ -2553,6 +2565,7 @@ "registration.component.resetPassword": "Поново постави лозинку", "registration.component.form.username": "Корисничко име", "registration.component.form.name": "Име", + "registration.component.form.userAlreadyExist": "Корисничко име већ постоји. Молимо покушајте друго корисничко име.", "registration.component.form.emailAlreadyExists": "Е-пошта већ постоји", "registration.component.form.usernameAlreadyExists": "Корисничко име већ постоји. Молимо покушајте друго корисничко име.", "registration.component.form.invalidEmail": "Унета је неисправна адреса е-поште", diff --git a/packages/i18n/src/locales/sv.i18n.json b/packages/i18n/src/locales/sv.i18n.json index 83471ec5983c..4059cf4a1932 100644 --- a/packages/i18n/src/locales/sv.i18n.json +++ b/packages/i18n/src/locales/sv.i18n.json @@ -12,6 +12,7 @@ "__username__was_set__role__by__user_by_": "{{username}} sattes {{role}} av {{user_by}}", "This_room_encryption_has_been_enabled_by__username_": "Rummets kryptering har aktiverats av {{username}}", "This_room_encryption_has_been_disabled_by__username_": "Rummets kryptering har inaktiverats av {{username}}", + "Third_party_login": "Inloggning från tredje part", "Enabled_E2E_Encryption_for_this_room": "aktivera end-to-end-kryptering för rummet", "disabled": "Inaktiverad", "Disabled_E2E_Encryption_for_this_room": "inaktivera end-to-end-kryptering för rummet", @@ -504,6 +505,7 @@ "Apps_context_installed": "Installerad", "Apps_context_requested": "Förfrågningar", "Apps_context_private": "Privata appar", + "Apps_context_premium": "Enterprise", "Apps_Count_Enabled_one": "{{count}} app aktiverad", "Apps_Count_Enabled_other": "{{count}} appar aktiverade", "Private_Apps_Count_Enabled_one": "{{count}} privat app aktiverad", @@ -3836,6 +3838,7 @@ "Privacy_summary": "Integritetsöversikt", "Private": "Privat", "private": "privat", + "Private_channels": "Privata kanaler", "Private_Apps": "Privata appar", "Private_Channel": "Privat kanal", "Private_Channels": "Privata kanaler", @@ -5571,6 +5574,7 @@ "onboarding.component.form.action.next": "Nästa", "onboarding.component.form.action.skip": "Hoppa över det här steget", "onboarding.component.form.action.register": "Registrera", + "onboarding.component.form.action.registerWorkspace": "Registrera arbetsytan", "onboarding.component.form.action.confirm": "Bekräfta", "onboarding.component.form.termsAndConditions": "Jag godkänner <1>villkoren och <3>integritetspolicyn", "onboarding.component.emailCodeFallback": "Fick du inget e-postmeddelande? <1>Skicka igen eller <3>ändra e-postadressen", @@ -5611,6 +5615,7 @@ "onboarding.form.adminInfoForm.fields.password.placeholder": "Skapa lösenord", "onboarding.form.adminInfoForm.fields.keepPosted.label": "Håll mig informerad om Rocket.Chat-uppdateringar", "onboarding.form.awaitConfirmationForm.title": "Väntar på bekräftelse", + "onboarding.form.awaitConfirmationForm.content.securityCode": "Säkerhetskod", "onboarding.form.organizationInfoForm.title": "Organisationsinfo", "onboarding.form.organizationInfoForm.subtitle": "Bara lite till. Vi behöver den här informationen för att anpassa din arbetsyta", "onboarding.form.organizationInfoForm.fields.organizationName.label": "Organisationsnamn", @@ -5623,6 +5628,7 @@ "onboarding.form.organizationInfoForm.fields.organizationSize.placeholder": "Välj", "onboarding.form.organizationInfoForm.fields.country.label": "Land", "onboarding.form.organizationInfoForm.fields.country.placeholder": "Välj", + "onboarding.form.registerOfflineForm.title": "Registrera dig offline", "onboarding.form.registeredServerForm.title": "Registrera din server", "onboarding.form.registeredServerForm.included.push": "Mobila push-meddelanden", "onboarding.form.registeredServerForm.included.externalProviders": "Integrering med externa leverantörer (WhatsApp, Facebook, Telegram, Twitter)", diff --git a/packages/i18n/src/locales/ta-IN.i18n.json b/packages/i18n/src/locales/ta-IN.i18n.json index b31adfdda065..4ee378f67319 100644 --- a/packages/i18n/src/locales/ta-IN.i18n.json +++ b/packages/i18n/src/locales/ta-IN.i18n.json @@ -320,6 +320,7 @@ "Apply": "விண்ணப்பிக்கவும்", "Apply_and_refresh_all_clients": "விண்ணப்பிக்க மற்றும் அனைத்து வாடிக்கையாளர்கள் புதுப்பிக்க", "Apps": "ஆப்ஸ்", + "Apps_context_premium": "நிறுவன", "Apps_Settings": "பயன்பாட்டின் அமைப்புகள்", "Apps_WhatIsIt": "பயன்பாடுகள்: அவர்கள் என்ன?", "Apps_WhatIsIt_paragraph1": "நிர்வாக பகுதியில் ஒரு புதிய ஐகான்! இது என்ன அர்த்தம் மற்றும் பயன்பாடுகள் என்ன?", @@ -1099,6 +1100,7 @@ "FEDERATION_Domain": "டொமைன்", "FEDERATION_Status": "நிலைமை", "Retry_Count": "மீண்டும் முயற்சி செய்", + "Federation_Matrix_enabled": "இயக்கப்பட்டது", "Field": "களம்", "Field_removed": "துறையில் நீக்கப்பட்டது", "Field_required": "புலம் தேவை", @@ -1457,6 +1459,10 @@ "Layout_Terms_of_Service": "சேவை விதிமுறைகள்", "LDAP": "LDAP,", "LDAP_Description": "பல தளங்கள் மற்றும் சேவைகள் இடையே ஒரு கடவுச்சொல்லை பகிர்ந்து ஒரு வசதி - LDAP, பல நிறுவனங்கள் ஒற்றை அடையாளம் வழங்க பயன்படுத்த என்று ஒரு படிநிலை தரவுத்தள உள்ளது. https://rocket.chat/docs/administrator-guides/authentication/ldap/: மேம்பட்ட கட்டமைப்பு தகவல் மற்றும் உதாரணங்கள், எங்கள் விக்கி கலந்தாலோசிக்கவும்.", + "LDAP_Connection_Encryption": "குறியாக்க", + "LDAP_DataSync_BackgroundSync": "பின்னணி ஒத்திசைவு", + "LDAP_Server_Type": "சர்வர் வகை", + "LDAP_Server_Type_Other": "மற்ற", "LDAP_Authentication": "இயக்கு", "LDAP_Authentication_Password": "கடவுச்சொல்", "LDAP_Authentication_UserDN": "பயனர் DN", @@ -2664,6 +2670,8 @@ "Visitor_Navigation": "பார்வையாளர் நேவிகேஷன்", "Visitor_page_URL": "பார்வையாளர் பக்கம் URL", "Visitor_time_on_site": "தளத்தில் பார்வையாளர் நேரம்", + "VoIP_Management_Server_Username": "பயனர் பெயர்", + "VoIP_Management_Server_Password": "கடவுச்சொல்", "Wait_activation_warning": "நீங்கள் உள்நுழைய முடியும் முன், உங்கள் கணக்கு நிர்வாகியால் மூலம் செயல்படுத்தப்படுகிறது வேண்டும்.", "Warnings": "எச்சரிக்கைகள்", "We_are_offline_Sorry_for_the_inconvenience": "நாம் லைனில் உள்ளனர். சிரமத்திற்கு வருந்துகிறோம்.", @@ -2747,6 +2755,7 @@ "registration.component.form.emailOrUsername": "மின்னஞ்சல் அல்லது பயனர் பெயர்", "registration.component.form.username": "பயனர் பெயர்", "registration.component.form.name": "பெயர்", + "registration.component.form.userAlreadyExist": "பெயர் ஏற்கனவே உள்ளது. மற்றொரு பயனர்பெயரை முயற்சிக்கவும்.", "registration.component.form.emailAlreadyExists": "மின்னஞ்சல் ஏற்கனவே உள்ளது", "registration.component.form.usernameAlreadyExists": "பெயர் ஏற்கனவே உள்ளது. மற்றொரு பயனர்பெயரை முயற்சிக்கவும்.", "registration.component.form.invalidEmail": "உள்ளிட்ட மின்னஞ்சல் தவறானது", diff --git a/packages/i18n/src/locales/th-TH.i18n.json b/packages/i18n/src/locales/th-TH.i18n.json index 1c786cf47aa9..996333840758 100644 --- a/packages/i18n/src/locales/th-TH.i18n.json +++ b/packages/i18n/src/locales/th-TH.i18n.json @@ -319,6 +319,7 @@ "Apply": "ใช้", "Apply_and_refresh_all_clients": "ใช้และรีเฟรชลูกค้าทั้งหมด", "Apps": "ปพลิเคชัน", + "Apps_context_premium": "องค์กร", "Apps_Settings": "การตั้งค่าของแอป", "Apps_WhatIsIt": "แอป: อะไรคือพวกเขา?", "Apps_WhatIsIt_paragraph1": "ไอคอนใหม่ในพื้นที่การบริหาร! นี่หมายถึงอะไรและ Apps คืออะไร?", @@ -1095,6 +1096,7 @@ "FEDERATION_Domain": "โดเมน", "FEDERATION_Status": "สถานะ", "Retry_Count": "ลองนับใหม่", + "Federation_Matrix_enabled": "เปิดการใช้งาน", "Field": "สนาม", "Field_removed": "ลบฟิลด์แล้ว", "Field_required": "ต้องระบุฟิลด์", @@ -1451,6 +1453,10 @@ "Layout_Sidenav_Footer_Dark_description": "ขนาดท้ายคือ 260 x 70px", "Layout_Terms_of_Service": "เงื่อนไขการให้บริการ", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "การเข้ารหัสลับ", + "LDAP_DataSync_BackgroundSync": "พื้นหลังซิงค์", + "LDAP_Server_Type": "ประเภทเซิร์ฟเวอร์", + "LDAP_Server_Type_Other": "อื่น ๆ", "LDAP_Authentication": "ทำให้สามารถ", "LDAP_Authentication_Password": "รหัสผ่าน", "LDAP_Authentication_UserDN": "DN ผู้ใช้", @@ -1980,6 +1986,7 @@ "Reactions": "ปฏิกิริยา", "Read_by": "อ่านโดย", "Read_only": "อ่านเท่านั้น", + "This_room_is_read_only": "ห้องนี้เป็นแบบอ่านอย่างเดียว", "Read_only_changed_successfully": "อ่านแล้วเปลี่ยนได้สำเร็จแล้ว", "Read_only_channel": "ช่องอ่านอย่างเดียว", "Read_only_group": "กลุ่มแบบอ่านอย่างเดียว", @@ -2478,6 +2485,7 @@ "Unarchive": "ยกเลิกการเก็บ", "unarchive-room": "ห้องที่ยกเลิกการจอง", "unarchive-room_description": "การอนุญาตให้ยกเลิกการเก็บถาวรช่อง", + "unauthorized": "ไม่มีอำนาจ", "Unblock_User": "ผู้ใช้ยกเลิกการปิดกั้น", "Unignore": "ให้ความสนใจ", "Uninstall": "ถอนการติดตั้ง", @@ -2649,6 +2657,8 @@ "Visitor_Navigation": "การนำทางของผู้เยี่ยมชม", "Visitor_page_URL": "URL ของหน้าผู้เข้าชม", "Visitor_time_on_site": "เวลาผู้เยี่ยมชมในไซต์", + "VoIP_Management_Server_Username": "ชื่อผู้ใช้", + "VoIP_Management_Server_Password": "รหัสผ่าน", "Wait_activation_warning": "ก่อนที่คุณจะสามารถเข้าสู่ระบบบัญชีของคุณต้องเปิดใช้งานด้วยตนเองโดยผู้ดูแลระบบ", "Warnings": "คำเตือน", "We_are_offline_Sorry_for_the_inconvenience": "เราออฟไลน์ ขออภัยในความไม่สะดวก.", @@ -2723,6 +2733,7 @@ "Your_push_was_sent_to_s_devices": "การกดของคุณถูกส่งไปยังอุปกรณ์%s", "Your_server_link": "ลิงค์เซิร์ฟเวอร์ของคุณ", "Your_workspace_is_ready": "พื้นที่ทำงานของคุณพร้อมใช้งานแล้ว🎉", + "registration.page.login.errors.wrongCredentials": "ผู้ใช้ไม่พบหรือรหัสผ่านไม่ถูกต้อง", "registration.page.registration.waitActivationWarning": "ก่อนที่คุณจะสามารถเข้าสู่ระบบบัญชีของคุณต้องเปิดใช้งานด้วยตนเองโดยผู้ดูแลระบบ", "registration.page.resetPassword.sent": "หากอีเมลนี้ได้รับการลงทะเบียนเราจะส่งคำแนะนำเกี่ยวกับวิธีรีเซ็ตรหัสผ่านของคุณ หากคุณไม่ได้รับอีเมลในไม่ช้าโปรดกลับมาลองอีกครั้ง", "registration.component.login": "เข้าสู่ระบบ", @@ -2730,6 +2741,7 @@ "registration.component.resetPassword": "รีเซ็ตรหัสผ่าน", "registration.component.form.username": "ชื่อผู้ใช้", "registration.component.form.name": "ชื่อ", + "registration.component.form.userAlreadyExist": "ชื่อผู้ใช้อยู่แล้ว. โปรดลองชื่อผู้ใช้อื่น", "registration.component.form.emailAlreadyExists": "มีอีเมลอยู่แล้ว", "registration.component.form.usernameAlreadyExists": "ชื่อผู้ใช้อยู่แล้ว. โปรดลองชื่อผู้ใช้อื่น", "registration.component.form.invalidEmail": "อีเมลที่ป้อนไม่ถูกต้อง", diff --git a/packages/i18n/src/locales/tr.i18n.json b/packages/i18n/src/locales/tr.i18n.json index 3986ee173be0..c4899dab439e 100644 --- a/packages/i18n/src/locales/tr.i18n.json +++ b/packages/i18n/src/locales/tr.i18n.json @@ -373,6 +373,7 @@ "Apply_and_refresh_all_clients": "Uygulayın ve tüm istemcilerin yenilemek", "Apps": "Uygulamalar", "Apps_context_installed": "Yüklü", + "Apps_context_premium": "Kuruluş", "Apps_Engine_Version": "Uygulamalar Motoru Sürümü", "Apps_Marketplace_Deactivate_App_Prompt": "Gerçekten bu uygulamayı devre dışı bırakmak istiyor musunuz?", "Apps_Marketplace_Login_Required_Title": "Market Oturum Açması Gerekli", @@ -617,6 +618,7 @@ "Confirm_password": "Parolanızı onaylayın", "Confirm_your_password": "Parolanızı onaylayın", "Connect": "Bağlan", + "Connected": "Bağlantı sağlandı", "Connection_Closed": "Bağlantı kapandı", "Connection_Reset": "Bağlantı sıfırlama", "Connectivity_Services": "Bağlantı Hizmetleri", @@ -1343,6 +1345,7 @@ "FEDERATION_Test_Setup": "Test yüklemesi", "FEDERATION_Test_Setup_Error": "Kurulumunuzu kullanarak sunucu bulunamadı,", "Retry_Count": "Yeniden Dene Sayımı", + "Federation_Matrix_enabled": "Etkin", "Field": "Alan", "Field_removed": "Alan çıkarıldı", "Field_required": "Alan gerekli", @@ -1751,6 +1754,12 @@ "Layout_Sidenav_Footer_Dark_description": "Alt kısım 260x70 piksel boyutundadır", "Layout_Terms_of_Service": "Kullanım Koşulları", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "Şifreleme", + "LDAP_Connection_Timeouts": "Zaman Aşımları", + "LDAP_UserSearch": "Kullanıcı Arama", + "LDAP_DataSync_BackgroundSync": "Arka Plan Senkronizasyonu", + "LDAP_Server_Type": "Sunucu Türü", + "LDAP_Server_Type_Other": "Diğer", "LDAP_Authentication": "Etkinleştir", "LDAP_Authentication_Password": "Şifre", "LDAP_Authentication_UserDN": "Kullanıcı DN", @@ -2371,6 +2380,7 @@ "Reactions": "Tepkiler", "Read_by": "Okuyan", "Read_only": "Yalnızca Oku", + "This_room_is_read_only": "Bu oda salt okunur", "Read_only_changed_successfully": "Salt okunur sadece başarıyla değiştirildi", "Read_only_channel": "Salt Okunur", "Read_only_group": "Salt Okunur Grup", @@ -2480,6 +2490,7 @@ "Robot_Instructions_File_Content": "Robots.txt Dosya İçeriği", "Rocket_Chat_Alert": "Rocket.Chat Uyarısı", "Role": "Rol", + "Roles": "Roller", "Role_Editing": "Rol düzenleniyor", "Role_removed": "Rol kaldırıldı", "Room": "Oda", @@ -3152,6 +3163,8 @@ "Visitor_Navigation": "Ziyaretçi Dolaşımı", "Visitor_page_URL": "Ziyaretçi sayfası URL'si", "Visitor_time_on_site": "Sitedeki ziyaretçi süresi", + "VoIP_Management_Server_Username": "Kullanıcı Adı", + "VoIP_Management_Server_Password": "Şifre", "Wait_activation_warning": "Oturum açmadan önce, hesabınızın yönetici tarafından etkinleştirilmesi gerekiyor.", "Warning": "Uyarı", "Warnings": "Uyarılar", @@ -3248,6 +3261,7 @@ "registration.component.resetPassword": "Şifreyi sıfırla", "registration.component.form.username": "Kullanıcı Adı", "registration.component.form.name": "Ad", + "registration.component.form.userAlreadyExist": "Kullanıcı adı zaten var. Lütfen başka bir kullanıcı adı deneyin.", "registration.component.form.emailAlreadyExists": "Bu e-posta zaten var", "registration.component.form.usernameAlreadyExists": "Kullanıcı adı zaten var. Lütfen başka bir kullanıcı adı deneyin.", "registration.component.form.invalidEmail": "Girilen e-posta geçersiz", diff --git a/packages/i18n/src/locales/ug.i18n.json b/packages/i18n/src/locales/ug.i18n.json index 737d8c8e4b9a..ba8077844663 100644 --- a/packages/i18n/src/locales/ug.i18n.json +++ b/packages/i18n/src/locales/ug.i18n.json @@ -45,6 +45,7 @@ "Accounts_OAuth_Custom_id": "ID تېرمىنال", "Accounts_OAuth_Custom_Identity_Path": "سالاھىيەت ئادرېسى", "Accounts_OAuth_Custom_Login_Style": "كىرىش خىلى", + "Accounts_OAuth_Custom_Scope": "دائىرىسى", "Accounts_OAuth_Custom_Secret": "ئاچقۇچ", "Accounts_OAuth_Custom_Token_Path": "ئادرېسىToken", "Accounts_OAuth_Custom_Token_Sent_Via": "شەرتلىك بەلگە يوللاش ئارقىلىق", @@ -157,6 +158,7 @@ "Animals_and_Nature": "ھايۋانات ۋە تەبىئەت", "API": "API", "API_Analytics": "ئانالىز قىلىش", + "API_Drupal_URL_Description": "ئايىغىدىكى يانتۇ سىزىق كىرەكسىز `https://domain.com` مەسلەن", "API_Embed": "قىستۇرۇش", "API_EmbedIgnoredHosts": "ئۇنتۇلغان كومپيۇتېرنى قىستۇرۇش", "API_EmbedIgnoredHosts_Description": "localhost, 127.0.0.1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 ئادرېسى مەسلەن CIDR پەش ئارقىلىق بۆلۈنگەن", @@ -430,6 +432,7 @@ "Favorite_Rooms": "ياخشى كۆرگەن ئۆينى ئىشلىتىش", "Favorites": "ساقلاش", "FEDERATION_Domain": "دائىرە نامى", + "Federation_Matrix_enabled": "ئىشلىتىلدى", "Field": "خەت بۆلىكى", "Field_removed": "خەت بۆلىكى يۇيۇۋېتىلىدۇى", "File_exceeds_allowed_size_of_bytes": "بايتتىن ئېشىپ كەتتى {{size}} ھۆججەتنىڭ چوڭ كىچىكلىكى رۇخسەت قىلىنش", @@ -585,6 +588,7 @@ "Layout_Terms_of_Service": "مۇلازىمەت تارمىقى", "LDAP": "LDAP", "LDAP_Description": "ئادەتتە شىركەتلەر تەرىپىدىن ئاددىي نۇقتا كىرىش مېخانىزىمى قىلىپ قوللىنىدۇ .بۇ خىلمېخانىزىم بىر ئەزانى ئوخشاش بىر يۈرۈش ھېسابات نومۇرى ۋە مەخپىي نومۇر ئارقىلىق كۆپلىگەن تور بەت ۋەمۇلازىمەتكە كىرگىلى بولىدۇ.كۆپلىگەن LDAPگۇۋاھنامىسىنى تەڭشەش ۋە مىساللىرىنى ئىچكىرى چۈشەنمەكچى بولسىڭىز بىزنىڭ wikiتور بېتىمىزدىن پايدىلانسىڭىز بولىدۇ https://rocket.chat/docs/administrator-guides/authentication/ldap/ (ېنىك مۇندەرىجىنى زىيارەت قىلىش كېلىشىمى)LDAP", + "LDAP_Connection_Encryption": "مەخپىيلەشتۈرۈش ئۇسۇلى", "LDAP_Authentication": "LDAP", "LDAP_Authentication_Password": "پارول", "LDAP_Authentication_UserDN_Description": "`cn=Administrator,cn=Users,dc=Example,dc=com`دە ئۈچىنچى تەرەپنىڭ توپلىشىشى ئۈچۈن قۇرۇلغان ھېسابات نومۇرى. ئىناۋەتلىك بولغان پۈتۈن ئىسىم ئىشلىتىڭ مەسىلەن: . LDAP كۆپ ئەھۋالدا ، ئۇ ئەزا \n نىڭ ئىچىدە ئىزدەش ۋە دەلىللەش رولىنى ئۆتەيدۇ.LDAP ئەزا باشقا ئەزالار كىرگەن چاغدا LDAP بۇ", @@ -1177,6 +1181,8 @@ "Visitor_Navigation": "زىيارەت قىلغۇچى يول باشلىغۇچى", "Visitor_page_URL": "زىيارەت قىلغۇچى تور بەت ئادرېسى", "Visitor_time_on_site": "زىيارەت قىلغۇچىنىڭ تور بەتتە تۇرغان ۋاقتى", + "VoIP_Management_Server_Username": "ئەزا ئىسمى", + "VoIP_Management_Server_Password": "پارول", "Wait_activation_warning": "سىزنىڭ ئاكونتىڭىز چوقۇم باشقۇرغۇچى قولى بىلەن قوزغاتقاندىن كېيىن ئاندىن كىرگىلى بولىدۇ.", "We_are_offline_Sorry_for_the_inconvenience": "ھازىر توردا ئادەم يوق ،. قولايسىزلىق ئېلىپ كەلگەن بولسا چۈشۈنۈىشۈڭىزنى ئۈمىد قىلىمىز .", "We_have_sent_password_email": "بىز سىزگە پارولنى قايتىدىن ئۆزگەرتىش ئىلخىتىنى ئەۋەتتۇق ، ئەگەر تاپشۇرۇپ ئالمىغان بولسىڭىز ، قايتىدىن سىناڭ.", diff --git a/packages/i18n/src/locales/uk.i18n.json b/packages/i18n/src/locales/uk.i18n.json index d09362b93dcc..94d470171761 100644 --- a/packages/i18n/src/locales/uk.i18n.json +++ b/packages/i18n/src/locales/uk.i18n.json @@ -410,6 +410,7 @@ "Apply_and_refresh_all_clients": "Застосувати і оновити для всіх клієнтів", "Apps": "Застосунки", "Apps_context_installed": "Встановлено", + "Apps_context_premium": "Підприємство", "Apps_Engine_Version": "Версія ядра застосунків", "Apps_Game_Center": "Ігровий центр", "Apps_Game_Center_Back": "Повернутися до ігрового центру", @@ -1290,6 +1291,7 @@ "Enter_Normal": "Нормальний режим (відправляти клавішою \"Enter\")", "Enter_to": "Ввійти в", "Enter_your_E2E_password": "Введіть свій пароль E2E", + "message_counter_many": "{{count}} повідомлень", "Entertainment": "Розваги", "Error": "Помилка", "Error_404": "Помилка: 404", @@ -1401,6 +1403,7 @@ "error-this-is-not-a-livechat-room": "Це не Livechat кімната", "error-token-already-exists": "Токен з таким іменем вже існує", "error-token-does-not-exists": "Токен не існує", + "message_counter_few": "{{count}} повідомлень", "error-too-many-requests": "Помилка, занадто багато запитів. Будь ласка сповільнитися. Ви повинні почекати {{seconds}} секунд, перш ніж знову спробувати.", "error-user-has-no-roles": "Користувач не має ролей", "error-user-is-not-activated": "Користувача не активовано", @@ -1470,6 +1473,7 @@ "FEDERATION_Test_Setup_Error": "Не вдалося знайти ваш сервер за допомогою налаштувань, перегляньте свої налаштування.", "FEDERATION_Test_Setup_Success": "Ваша настройка федерації працює, і інші сервери можуть знайти вас!", "Retry_Count": "Повторити граф", + "Federation_Matrix_enabled": "Увімкнено", "Field": "Поле", "Field_removed": "Поле видалено", "Field_required": "Поле обов'язкове", @@ -1549,7 +1553,6 @@ "Filters": "Фільтри", "Financial_Services": "Фінансові послуги", "First_Channel_After_Login": "Перший канал після входу", - "message_counter_many": "{{count}} повідомлень", "First_response_time": "Час першої відповіді", "Flags": "Прапори", "Follow_message": "Відслідковувати повідомлення", @@ -1669,7 +1672,6 @@ "Ignored": "Ігнорується", "Images": "Зображення", "IMAP_intercepter_already_running": "Інтерфейтер IMAP вже працює", - "message_counter_few": "{{count}} повідомлень", "IMAP_intercepter_Not_running": "Інтерфейс IMAP не працює", "Impersonate_next_agent_from_queue": "Використовуйте наступного представника з черги", "Impersonate_user": "Уособлення користувача", @@ -1915,6 +1917,12 @@ "Layout_Terms_of_Service": "Умови обслуговування", "LDAP": "LDAP", "LDAP_Description": "LDAP є ієрархічною базу даних, що багато компаній використовують для забезпечення єдиного входу - об'єкта для спільного використання одного пароля між декількома сайтами і послугами. Для отримання попередньої інформації про конфігурацію і приклади, будь ласка, зверніться до нашої вікі: https://rocket.chat/docs/administrator-guides/authentication/ldap/.", + "LDAP_Connection_Encryption": "шифрування", + "LDAP_Connection_Timeouts": "Час очікування", + "LDAP_UserSearch": "Пошук користувачів", + "LDAP_DataSync_BackgroundSync": "Фонова синхронізація", + "LDAP_Server_Type": "Тип сервера", + "LDAP_Server_Type_Other": "Інший", "LDAP_Authentication": "Увімкнути", "LDAP_Authentication_Password": "Пароль", "LDAP_Authentication_UserDN": "Користувач DN", @@ -3240,6 +3248,8 @@ "Visitor_Navigation": "відвідувач навігації", "Visitor_page_URL": "URL-адреса сторінки відвідувача", "Visitor_time_on_site": "Час для відвідувачів на сайті", + "VoIP_Management_Server_Username": "Ім'я користувача", + "VoIP_Management_Server_Password": "Пароль", "Wait_activation_warning": "Перед тим, як увійти, ваш обліковий запис повинен бути активована вручну адміністратором.", "Warning": "Увага", "Warnings": "Попередження", @@ -3339,6 +3349,7 @@ "registration.component.form.emailOrUsername": "Адреса електронної пошти або логін", "registration.component.form.username": "Ім'я користувача", "registration.component.form.name": "Ім'я", + "registration.component.form.userAlreadyExist": "Ім'я користувача вже існує. Будь ласка, спробуйте інше ім'я користувача.", "registration.component.form.emailAlreadyExists": "Email вже існує", "registration.component.form.usernameAlreadyExists": "Ім'я користувача вже існує. Будь ласка, спробуйте інше ім'я користувача.", "registration.component.form.invalidEmail": "Невірний email", diff --git a/packages/i18n/src/locales/vi-VN.i18n.json b/packages/i18n/src/locales/vi-VN.i18n.json index 002d257306b0..a2fc50733b1e 100644 --- a/packages/i18n/src/locales/vi-VN.i18n.json +++ b/packages/i18n/src/locales/vi-VN.i18n.json @@ -380,6 +380,7 @@ "Apply": "Áp dụng", "Apply_and_refresh_all_clients": "Áp dụng và làm mới tất cả các khách hàng", "Apps": "Ứng dụng", + "Apps_context_premium": "Doanh nghiệp", "Apps_Essential_Alert": "Ứng dụng này rất cần thiết cho các sự kiện sau:", "Apps_Essential_Disclaimer": "Các sự kiện được liệt kê ở trên sẽ bị gián đoạn nếu ứng dụng này bị vô hiệu hóa. Nếu bạn muốn Rocket.Chat hoạt động mà không có chức năng của ứng dụng này, bạn cần gỡ cài đặt nó", "Apps_Framework_Source_Package_Storage_Type": "Loại lưu trữ gói nguồn của ứng dụng", @@ -585,6 +586,7 @@ "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", + "Connected": "Đã kết nối", "Connection_Closed": "Kêt nôi bị đong", "Connection_Reset": "Đặt lại kết nối", "Consulting": "Tư vấn", @@ -1193,6 +1195,7 @@ "FEDERATION_Domain": "Tên miền", "FEDERATION_Status": "Trạng thái", "Retry_Count": "Thử lại đếm", + "Federation_Matrix_enabled": "Đã bật", "Field": "Cánh đồng", "Field_removed": "Trường đã bị xóa", "Field_required": "Trường bắt buộc", @@ -1549,6 +1552,10 @@ "Layout_Sidenav_Footer_Dark_description": "Kích thước chân trang là 260 x 70px", "Layout_Terms_of_Service": "Điều khoản dịch vụ", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "Mã hóa", + "LDAP_DataSync_BackgroundSync": "Đồng bộ hóa nền", + "LDAP_Server_Type": "Loại máy chủ", + "LDAP_Server_Type_Other": "Khác", "LDAP_Authentication": "Bật", "LDAP_Authentication_Password": "Mật khẩu", "LDAP_Authentication_UserDN": "DN người dùng", @@ -2086,6 +2093,7 @@ "Reactions": "Biểu cảm", "Read_by": "Đọc bởi", "Read_only": "Chỉ đọc", + "This_room_is_read_only": "Phòng này chỉ đọc", "Read_only_changed_successfully": "Đã đọc thành công chỉ đọc", "Read_only_channel": "Kênh chỉ đọc", "Read_only_group": "Nhóm chỉ đọc", @@ -2586,6 +2594,7 @@ "Unarchive": "Hủy lưu trữ", "unarchive-room": "Phòng chưa lưu trữ", "unarchive-room_description": "Cho phép hủy bỏ các kênh", + "unauthorized": "Chưa được phép", "Unblock_User": "Mở khoá người dùng", "Unignore": "Hủy bỏ", "Uninstall": "Gỡ cài đặt", @@ -2758,6 +2767,8 @@ "Visitor_Navigation": "Điều hướng truy cập", "Visitor_page_URL": "URL của trang truy cập", "Visitor_time_on_site": "Thời gian khách thăm trên trang web", + "VoIP_Management_Server_Username": "Tên đăng nhập", + "VoIP_Management_Server_Password": "Mật khẩu", "Wait_activation_warning": "Trước khi bạn có thể đăng nhập, tài khoản của bạn phải được quản trị viên kích hoạt theo cách thủ công.", "Warnings": "Cảnh báo", "We_are_offline_Sorry_for_the_inconvenience": "Chúng tôi đang offline. Xin lỗi vì sự bất tiện.", @@ -2832,6 +2843,7 @@ "Your_push_was_sent_to_s_devices": "Đã được gửi tới % thiết bị", "Your_server_link": "Đường dẫn máy chủ của bạn", "Your_workspace_is_ready": "Workspace của bạn đã sẵn sàng", + "registration.page.login.errors.wrongCredentials": "Người dùng không tìm thấy hoặc không chính xác mật khẩu", "registration.page.login.errors.AppUserNotAllowedToLogin": "Người dùng ứng dụng không được phép đăng nhập trực tiếp.", "registration.page.registration.waitActivationWarning": "Trước khi bạn có thể đăng nhập, tài khoản của bạn phải được quản trị viên kích hoạt theo cách thủ công.", "registration.page.resetPassword.sent": "Nếu email này đã được đăng ký, chúng tôi sẽ gửi hướng dẫn về cách đặt lại mật khẩu của bạn. Nếu bạn không nhận được email sớm, vui lòng quay lại và thử lại.", @@ -2840,6 +2852,7 @@ "registration.component.resetPassword": "Đặt lại mật khẩu", "registration.component.form.username": "Tên đăng nhập", "registration.component.form.name": "Tên", + "registration.component.form.userAlreadyExist": "Tên này đã có người dùng. Vui lòng thử tên người dùng khác.", "registration.component.form.emailAlreadyExists": "Email đã tồn tại", "registration.component.form.usernameAlreadyExists": "Tên này đã có người dùng. Vui lòng thử tên người dùng khác.", "registration.component.form.invalidEmail": "Email nhập vào không hợp lệ", diff --git a/packages/i18n/src/locales/zh-HK.i18n.json b/packages/i18n/src/locales/zh-HK.i18n.json index 1b4f84a32d60..2f416acc4f2f 100644 --- a/packages/i18n/src/locales/zh-HK.i18n.json +++ b/packages/i18n/src/locales/zh-HK.i18n.json @@ -335,6 +335,7 @@ "Apply": "应用", "Apply_and_refresh_all_clients": "申请并刷新所有客户", "Apps": "应用", + "Apps_context_premium": "企业", "Apps_Marketplace_Deactivate_App_Prompt": "是否確定要禁用此應用程序?", "Apps_Marketplace_Modify_App_Subscription": "修改訂閱", "Apps_Marketplace_Uninstall_App_Prompt": "是否確定要卸載此應用程序?", @@ -1119,6 +1120,7 @@ "FEDERATION_Domain": "域", "FEDERATION_Status": "状态", "Retry_Count": "重试计数", + "Federation_Matrix_enabled": "启用", "Field": "领域", "Field_removed": "现场删除", "Field_required": "必填字段", @@ -1477,6 +1479,10 @@ "Layout_Sidenav_Footer_Dark_description": "页脚大小是260 x 70px", "Layout_Terms_of_Service": "服务条款", "LDAP": "LDAP", + "LDAP_Connection_Encryption": "加密", + "LDAP_DataSync_BackgroundSync": "后台同步", + "LDAP_Server_Type": "服务器类型", + "LDAP_Server_Type_Other": "其他", "LDAP_Authentication": "启用", "LDAP_Authentication_Password": "密码", "LDAP_Authentication_UserDN": "用户DN", @@ -2011,6 +2017,7 @@ "Reactions": "反应", "Read_by": "阅读", "Read_only": "只读", + "This_room_is_read_only": "这个房间是只读的", "Read_only_changed_successfully": "只读更改成功", "Read_only_channel": "只读频道", "Read_only_group": "只读组", @@ -2511,6 +2518,7 @@ "Unarchive": "取消封存", "unarchive-room": "unarchive房间", "unarchive-room_description": "允许非存档渠道", + "unauthorized": "未经授权", "Unblock_User": "取消阻止用户", "Unignore": "屏蔽", "Uninstall": "卸载", @@ -2680,6 +2688,8 @@ "Visitor_Navigation": "访客导航", "Visitor_page_URL": "访客页面网址", "Visitor_time_on_site": "访客在现场的时间", + "VoIP_Management_Server_Username": "用户名", + "VoIP_Management_Server_Password": "密码", "Wait_activation_warning": "您的帐户必须由管理员手工启动后才能登录。", "Warning": "警告", "Warnings": "警告", @@ -2755,6 +2765,7 @@ "Your_push_was_sent_to_s_devices": "您的推送已发送到%s设备", "Your_server_link": "您的服务器链接", "Your_workspace_is_ready": "您的工作区已准备好使用🎉", + "registration.page.login.errors.wrongCredentials": "用户未找到或密码不正确", "registration.page.registration.waitActivationWarning": "您的帐户必须由管理员手工启动后才能登录。", "registration.page.resetPassword.sent": "如果此电子邮件已注册,我们将发送有关如何重置密码的说明。如果您很短时间内没有收到电子邮件,请返回并重试。", "registration.component.login": "登录", @@ -2762,6 +2773,7 @@ "registration.component.resetPassword": "重设密码", "registration.component.form.username": "用户名", "registration.component.form.name": "姓名", + "registration.component.form.userAlreadyExist": "此用户名已存在。请尝试其他用户名。", "registration.component.form.emailAlreadyExists": "邮件已存在", "registration.component.form.usernameAlreadyExists": "此用户名已存在。请尝试其他用户名。", "registration.component.form.invalidEmail": "无效的电子邮件", diff --git a/packages/i18n/src/locales/zh-TW.i18n.json b/packages/i18n/src/locales/zh-TW.i18n.json index daab1ff46180..1b9cbed4a5ee 100644 --- a/packages/i18n/src/locales/zh-TW.i18n.json +++ b/packages/i18n/src/locales/zh-TW.i18n.json @@ -448,6 +448,7 @@ "Apply_and_refresh_all_clients": "套用並重新整理所有客戶端", "Apps": "應用程式", "Apps_context_installed": "已安裝", + "Apps_context_premium": "企業", "Apps_Engine_Version": "應用程式引擎版本", "Apps_Essential_Alert": "對於以下事件,此應用程式必不可少:", "Apps_Essential_Disclaimer": "如果停用此應用程式,則上面列出的事件將被中斷。如果您希望 Rocket.Chat 在沒有此應用程式功能的情況下正常工作,則需要將其反安裝", @@ -1360,6 +1361,7 @@ "Desktop_Notifications_Not_Enabled": "桌面通知未啟用", "Details": "詳細", "line": "行", + "Device_Management_IP": "IP", "Different_Style_For_User_Mentions": "使用者提到的不同風格", "Livechat_Facebook_API_Key": "Omni Channel API 金鑰", "Livechat_Facebook_API_Secret": "Omni Channel API 加密", @@ -1828,6 +1830,7 @@ "FEDERATION_Test_Setup_Error": "無法用您的設定找到您的伺服器,請確認您的設定。", "FEDERATION_Test_Setup_Success": "您的聯合設定正在執行且其他伺服器都可以找到您!", "Retry_Count": "重試計數", + "Federation_Matrix_enabled": "已啟用", "Field": "欄位", "Field_removed": "欄位已移除", "Field_required": "必填字段", @@ -3237,6 +3240,7 @@ "Privacy": "隱私條款", "Privacy_Policy": "隱私政策", "Private": "私人", + "Private_channels": "私人Channel s", "Private_Channel": "私人Channel", "Private_Channels": "私人Channel s", "Private_Chats": "私人聊天", @@ -3311,6 +3315,7 @@ "Reactions": "反應", "Read_by": "閱讀", "Read_only": "唯讀", + "This_room_is_read_only": "這是個唯讀頻道", "Read_only_changed_successfully": "唯讀更改成功", "Read_only_channel": "唯讀Channel", "Read_only_group": "唯讀群組", @@ -3455,6 +3460,7 @@ "Unsafe_Url": "不安全的網址", "Rocket_Chat_Alert": "Rocket.Chat 警報", "Role": "角色", + "Roles": "角色", "Role_Editing": "角色編輯", "Role_Mapping": "對應身份", "Role_removed": "角色已刪除", @@ -4492,6 +4498,7 @@ "registration.component.resetPassword": "重設密碼", "registration.component.form.username": "使用者名稱", "registration.component.form.name": "姓名", + "registration.component.form.userAlreadyExist": "此使用者名稱已存在。請嘗試其他使用者名稱。", "registration.component.form.emailAlreadyExists": "電子郵件已存在", "registration.component.form.usernameAlreadyExists": "此使用者名稱已存在。請嘗試其他使用者名稱。", "registration.component.form.invalidEmail": "電子郵件無效", diff --git a/packages/i18n/src/locales/zh.i18n.json b/packages/i18n/src/locales/zh.i18n.json index de49283fd471..6929e3eeab8d 100644 --- a/packages/i18n/src/locales/zh.i18n.json +++ b/packages/i18n/src/locales/zh.i18n.json @@ -422,6 +422,7 @@ "Apply_and_refresh_all_clients": "应用并刷新所有客户端", "Apps": "应用", "Apps_context_installed": "已安装", + "Apps_context_premium": "企业", "Apps_Engine_Version": "应用程序引擎版本", "Apps_Essential_Alert": "此应用对以下事件为必须:", "Apps_Essential_Disclaimer": "此应用禁用时上方列出的事件将被影响。如果想让 Rocket.Chat 在没有此应用的情况下运行,您需要卸载它。", @@ -806,6 +807,7 @@ "Confirm_password": "确认密码", "Confirm_your_password": "确认密码", "Connect": "连接", + "Connected": "已连接", "Connect_SSL_TLS": "使用 SSL/TLS 连接", "Connection_Closed": "连接关闭", "Connection_Reset": "连接重置", @@ -1660,6 +1662,7 @@ "FEDERATION_Test_Setup_Error": "无法通过您的配置找到服务器,请检查您的设定。", "FEDERATION_Test_Setup_Success": "您的联邦配置生效并且可被其他服务器找到!", "Retry_Count": "重试计数", + "Federation_Matrix_enabled": "已启用", "Field": "字段", "Field_removed": "已移除字段", "Field_required": "字段必须填写", @@ -2150,7 +2153,15 @@ "Layout_Terms_of_Service": "服务条款", "LDAP": "LDAP", "LDAP_Description": "LDAP(轻量目录访问协议)是一种层次数据库,常被企业用于提供单点登录机制——该机制允许用户使用同一套帐号密码登录多个网站或服务。想要了解LDAP认证的设置及示例,可参考我们的wiki页: https://rocket.chat/docs/administrator-guides/authentication/ldap/", + "LDAP_Connection_Encryption": "加密", "LDAP_Connection_successful": "LDAP 连接成功", + "LDAP_Connection_Timeouts": "超时", + "LDAP_UserSearch": "用户搜索", + "LDAP_DataSync_DataMap": "映射", + "LDAP_DataSync_Advanced": "高级同步", + "LDAP_DataSync_BackgroundSync": "后台同步", + "LDAP_Server_Type": "服务器类型", + "LDAP_Server_Type_Other": "其他", "LDAP_Advanced_Sync": "高级同步", "LDAP_Authentication": "启用", "LDAP_Authentication_Password": "密码", @@ -2919,6 +2930,7 @@ "Privacy": "隐私条款", "Privacy_Policy": "隐私政策", "Private": "私人", + "Private_channels": "私人频道", "Private_Channel": "私人频道", "Private_Channels": "私人频道", "Private_Chats": "私人聊天", @@ -3120,6 +3132,7 @@ "Robot_Instructions_File_Content": "Robots.txt 文件内容", "Rocket_Chat_Alert": "Rocket.Chat 提醒", "Role": "角色", + "Roles": "角色", "Role_Editing": "编辑角色", "Role_Mapping": "角色映射", "Role_removed": "已移除角色", @@ -3987,6 +4000,8 @@ "Visitor_Navigation": "游客导航", "Visitor_page_URL": "访客页面地址", "Visitor_time_on_site": "访客网站停留时间", + "VoIP_Management_Server_Username": "用户名", + "VoIP_Management_Server_Password": "密码", "Wait_activation_warning": "您的帐户必须由管理员手工激活后才能登录。", "Waiting_queue": "等待队列", "Waiting_queue_message": "等待队列消息", @@ -4117,6 +4132,7 @@ "registration.component.form.emailOrUsername": "电子邮件或用户名", "registration.component.form.username": "用户名", "registration.component.form.name": "姓名", + "registration.component.form.userAlreadyExist": "此用户名已存在。请尝试其他用户名。", "registration.component.form.emailAlreadyExists": "邮箱已存在", "registration.component.form.usernameAlreadyExists": "此用户名已存在。请尝试其他用户名。", "registration.component.form.invalidEmail": "输入的电子邮件地址无效", From 0f68bdb63ddd302eca576e5e5a30e9065f55035d Mon Sep 17 00:00:00 2001 From: "lingohub[bot]" <69908207+lingohub[bot]@users.noreply.github.com> Date: Wed, 28 Feb 2024 21:58:59 -0500 Subject: [PATCH 074/207] =?UTF-8?q?i18n:=20Rocket.Chat.Livechat=20language?= =?UTF-8?q?=20update=20from=20LingoHub=20=F0=9F=A4=96=20on=202024-02-28Z?= =?UTF-8?q?=20(#31855)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/livechat/src/i18n/af.json | 26 ++-- packages/livechat/src/i18n/ar.json | 26 ++-- packages/livechat/src/i18n/az.json | 26 ++-- packages/livechat/src/i18n/be-BY.json | 24 +-- packages/livechat/src/i18n/bg.json | 26 ++-- packages/livechat/src/i18n/bs.json | 8 +- packages/livechat/src/i18n/ca.json | 24 +-- packages/livechat/src/i18n/cs.json | 152 +++++++++---------- packages/livechat/src/i18n/cy.json | 26 ++-- packages/livechat/src/i18n/da.json | 26 ++-- packages/livechat/src/i18n/de-AT.json | 150 +++++++++---------- packages/livechat/src/i18n/de.json | 152 +++++++++---------- packages/livechat/src/i18n/el.json | 26 ++-- packages/livechat/src/i18n/en.json | 208 +++++++++++++------------- packages/livechat/src/i18n/eo.json | 26 ++-- packages/livechat/src/i18n/es.json | 152 +++++++++---------- packages/livechat/src/i18n/et.json | 24 +-- packages/livechat/src/i18n/eu.json | 24 +-- packages/livechat/src/i18n/fa.json | 200 ++++++++++++------------- packages/livechat/src/i18n/fi.json | 26 ++-- packages/livechat/src/i18n/fr.json | 152 +++++++++---------- packages/livechat/src/i18n/he.json | 134 ++++++++--------- packages/livechat/src/i18n/hr.json | 28 ++-- packages/livechat/src/i18n/hu.json | 26 ++-- packages/livechat/src/i18n/id.json | 26 ++-- packages/livechat/src/i18n/it.json | 140 ++++++++--------- packages/livechat/src/i18n/ja.json | 154 +++++++++---------- packages/livechat/src/i18n/km.json | 28 ++-- packages/livechat/src/i18n/ko.json | 28 ++-- packages/livechat/src/i18n/ku.json | 26 ++-- packages/livechat/src/i18n/lo.json | 24 +-- packages/livechat/src/i18n/lt.json | 26 ++-- packages/livechat/src/i18n/lv.json | 26 ++-- packages/livechat/src/i18n/mn.json | 26 ++-- packages/livechat/src/i18n/ms-MY.json | 24 +-- packages/livechat/src/i18n/nl.json | 160 ++++++++++---------- packages/livechat/src/i18n/no.json | 26 ++-- packages/livechat/src/i18n/pl.json | 144 +++++++++--------- packages/livechat/src/i18n/pt-BR.json | 168 ++++++++++----------- packages/livechat/src/i18n/pt.json | 36 ++--- packages/livechat/src/i18n/ro.json | 34 ++--- packages/livechat/src/i18n/ru.json | 152 +++++++++---------- packages/livechat/src/i18n/sk-SK.json | 24 +-- packages/livechat/src/i18n/sl-SI.json | 24 +-- packages/livechat/src/i18n/sq.json | 26 ++-- packages/livechat/src/i18n/sr.json | 148 +++++++++--------- packages/livechat/src/i18n/sv.json | 200 ++++++++++++------------- packages/livechat/src/i18n/ta-IN.json | 24 +-- packages/livechat/src/i18n/th-TH.json | 24 +-- packages/livechat/src/i18n/tr.json | 28 ++-- packages/livechat/src/i18n/ug.json | 14 +- packages/livechat/src/i18n/uk.json | 26 ++-- packages/livechat/src/i18n/vi-VN.json | 24 +-- packages/livechat/src/i18n/zh-HK.json | 24 +-- packages/livechat/src/i18n/zh-TW.json | 24 +-- packages/livechat/src/i18n/zh.json | 154 +++++++++---------- 56 files changed, 1852 insertions(+), 1852 deletions(-) diff --git a/packages/livechat/src/i18n/af.json b/packages/livechat/src/i18n/af.json index 70eee6311b8c..5a16326ec323 100644 --- a/packages/livechat/src/i18n/af.json +++ b/packages/livechat/src/i18n/af.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "kanselleer", - "conversation_finished": "Gesprek afgehandel", - "department_switched": "Departement aangeskakel", - "no": "Geen", - "options": "opsies", - "send": "stuur", - "user_joined": "Gebruiker aangesluit", - "user_left": "Gebruiker oor", - "we_are_not_online_right_now_please_leave_a_message": "Ons is nie aanlyn nie. Asseblief, laat 'n boodskap.", - "yes": "Ja" - } -} + "translation": { + "cancel": "kanselleer", + "conversation_finished": "Gesprek afgehandel", + "department_switched": "Departement aangeskakel", + "no": "Geen", + "options": "opsies", + "send": "stuur", + "user_joined": "Gebruiker aangesluit", + "user_left": "Gebruiker oor", + "we_are_not_online_right_now_please_leave_a_message": "Ons is nie aanlyn nie. Asseblief, laat 'n boodskap.", + "yes": "Ja" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/ar.json b/packages/livechat/src/i18n/ar.json index 6d412e97aa65..6c12b245ca9d 100644 --- a/packages/livechat/src/i18n/ar.json +++ b/packages/livechat/src/i18n/ar.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "إلغاء", - "conversation_finished": "تم إنهاء المحادثة", - "department_switched": "تم تغيير الفرع", - "no": "لا", - "options": "خيارات", - "send": "إرسال", - "user_joined": "العضو انضم", - "user_left": "اليسار المستخدم", - "we_are_not_online_right_now_please_leave_a_message": "نحن لسنا على الانترنت الآن. يرجى ترك رسالة.", - "yes": "نعم" - } -} + "translation": { + "cancel": "إلغاء", + "conversation_finished": "تم إنهاء المحادثة", + "department_switched": "تم تغيير الفرع", + "no": "لا", + "options": "خيارات", + "send": "إرسال", + "user_joined": "العضو انضم", + "user_left": "اليسار المستخدم", + "we_are_not_online_right_now_please_leave_a_message": "نحن لسنا على الانترنت الآن. يرجى ترك رسالة.", + "yes": "نعم" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/az.json b/packages/livechat/src/i18n/az.json index aecf5849101a..d53fd2232bd0 100644 --- a/packages/livechat/src/i18n/az.json +++ b/packages/livechat/src/i18n/az.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Ləğv et", - "conversation_finished": "Söhbət başa çatdı", - "department_switched": "Şöbə keçdi", - "no": "Yox", - "options": "Seçimlər", - "send": "Göndər", - "user_joined": "İstifadəçi qatıldı", - "user_left": "İstifadəçi buraxdı", - "we_are_not_online_right_now_please_leave_a_message": "İndi online deyil. Xahiş edirik bir mesaj buraxın.", - "yes": "Bəli" - } -} + "translation": { + "cancel": "Ləğv et", + "conversation_finished": "Söhbət başa çatdı", + "department_switched": "Şöbə keçdi", + "no": "Yox", + "options": "Seçimlər", + "send": "Göndər", + "user_joined": "İstifadəçi qatıldı", + "user_left": "İstifadəçi buraxdı", + "we_are_not_online_right_now_please_leave_a_message": "İndi online deyil. Xahiş edirik bir mesaj buraxın.", + "yes": "Bəli" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/be-BY.json b/packages/livechat/src/i18n/be-BY.json index 60148939c787..7123d8067ea6 100644 --- a/packages/livechat/src/i18n/be-BY.json +++ b/packages/livechat/src/i18n/be-BY.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Адмяніць", - "conversation_finished": "Размова скончана", - "department_switched": "Аддзел пераключыўся", - "no": "Няма", - "options": "Опцыі", - "send": "Паслаць", - "user_joined": "Карыстальнік далучыўся", - "user_left": "Карыстальнік выйшаў", - "we_are_not_online_right_now_please_leave_a_message": "Мы зараз не ў сеткі. Калі ласка, пакіньце паведамленне.", - "yes": "Да" - } + "translation": { + "cancel": "Адмяніць", + "conversation_finished": "Размова скончана", + "department_switched": "Аддзел пераключыўся", + "no": "Няма", + "options": "Опцыі", + "send": "Паслаць", + "user_joined": "Карыстальнік далучыўся", + "user_left": "Карыстальнік выйшаў", + "we_are_not_online_right_now_please_leave_a_message": "Мы зараз не ў сеткі. Калі ласка, пакіньце паведамленне.", + "yes": "Да" + } } \ No newline at end of file diff --git a/packages/livechat/src/i18n/bg.json b/packages/livechat/src/i18n/bg.json index 0f38e4b93866..8046760ebc5e 100644 --- a/packages/livechat/src/i18n/bg.json +++ b/packages/livechat/src/i18n/bg.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Отказ", - "conversation_finished": "Разговорът завърши", - "department_switched": "Отделът е включен", - "no": "Не", - "options": "Настроики", - "send": "Изпрати", - "user_joined": "Потребителят се присъедини", - "user_left": "Потребителят остана", - "we_are_not_online_right_now_please_leave_a_message": "В момента не сме онлайн. Моля, оставете съобщение.", - "yes": "Да" - } -} + "translation": { + "cancel": "Отказ", + "conversation_finished": "Разговорът завърши", + "department_switched": "Отделът е включен", + "no": "Не", + "options": "Настроики", + "send": "Изпрати", + "user_joined": "Потребителят се присъедини", + "user_left": "Потребителят остана", + "we_are_not_online_right_now_please_leave_a_message": "В момента не сме онлайн. Моля, оставете съобщение.", + "yes": "Да" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/bs.json b/packages/livechat/src/i18n/bs.json index 268699ce2c1a..60fd1779c2aa 100644 --- a/packages/livechat/src/i18n/bs.json +++ b/packages/livechat/src/i18n/bs.json @@ -1,5 +1,5 @@ { - "translation": { - "are_you_sure_you_want_to_switch_the_department": "Jeste li sigurni da želite prebaciti odjel?" - } -} + "translation": { + "are_you_sure_you_want_to_switch_the_department": "Jeste li sigurni da želite prebaciti odjel?" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/ca.json b/packages/livechat/src/i18n/ca.json index f97136b84dfa..1aedaf79a343 100644 --- a/packages/livechat/src/i18n/ca.json +++ b/packages/livechat/src/i18n/ca.json @@ -1,13 +1,13 @@ { - "translation": { - "cancel": "Cancel·la", - "conversation_finished": "Conversa acabada", - "department_switched": "Departament canviat", - "options": "Opcions", - "send": "Envia", - "user_joined": "usuari unit", - "user_left": "L'usuari ha abandonat la sala", - "we_are_not_online_right_now_please_leave_a_message": "No estem en línia ara mateix. Sisplau, deixa un missatge.", - "yes": "Sí" - } -} + "translation": { + "cancel": "Cancel·la", + "conversation_finished": "Conversa acabada", + "department_switched": "Departament canviat", + "options": "Opcions", + "send": "Envia", + "user_joined": "usuari unit", + "user_left": "L'usuari ha abandonat la sala", + "we_are_not_online_right_now_please_leave_a_message": "No estem en línia ara mateix. Sisplau, deixa un missatge.", + "yes": "Sí" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/cs.json b/packages/livechat/src/i18n/cs.json index ea56d7505473..f2c32fa7c56e 100644 --- a/packages/livechat/src/i18n/cs.json +++ b/packages/livechat/src/i18n/cs.json @@ -1,77 +1,77 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "Opravdu chcete ukončit chat?", - "are_you_sure_you_want_to_remove_all_of_your_person": "Opravdu chcete smazat všechny Vaše osobní údaje?", - "are_you_sure_you_want_to_switch_the_department": "Opravdu chcete změnit oddělení?", - "cancel": "Zrušit", - "change_department": "Změnit oddělení", - "change_department_1": "Změnit Oddělení", - "chat_finished": "Chat Ukončen", - "choose_a_department": "Vybrat oddělení...", - "choose_a_department_1": "Vybrat oddělení", - "choose_an_option": "Vybrat možnost...", - "conversation_finished": "Konverzace ukončena", - "count_new_messages_since_since_one": "Jedna nová zpráva od {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} nových zpráv od {{val, datetime}}", - "department_switched": "Oddělení změněno", - "departments": "Oddělení", - "disable_notifications": "Zakázat oznámení", - "dismiss_this_alert": "Zrušit toto upozornění", - "drop_here_to_upload_a_file": "Přetažením sem nahrajte soubor", - "email": "E-mail", - "enable_notifications": "Povolit oznámení", - "error_closing_chat": "Chyba při zavírání chatu.", - "error_removing_user_data": "Chyba při odstraňování uživatelských dat.", - "error_starting_a_new_conversation_reason": "Chyba při zahájení nové konverzace: {{reason}}", - "expand_chat": "Rozbalit chat", - "field_required": "Pole je povinné", - "file_exceeds_allowed_size_of_size": "Soubor překračuje povolenou velikost{{size}}.", - "fileupload_error": "Chyba při nahrávání souboru", - "finish_this_chat": "Ukončit tento chat", - "forget_remove_my_data": "Zapomenout/Odebrat mé údaje", - "go_to_menu_options_forget_remove_my_personal_data": "Přejít do <1>možnosti nabídky → Zapomenout/Odebrat mé osobní údaje vyžádat okamžité odstranění Vašich osobních údajů.", - "i_agree": "Souhlasím", - "i_need_help_with": "Potřebuji pomoct s...", - "if_you_have_any_other_questions_just_press_the_but": "Pokud máte jakékoli další otázky, stačí stisknout tlačítko níže a zahájit nový chat", - "insert_your_field_here": "Sem vložte {{field}} ...", - "invalid_email": "Neplatný e-mail", - "invalid_value": "Neplatná hodnota", - "leave_a_message": "Zanechte zprávu", - "livechat_connected": "Livechat připojen.", - "livechat_is_not_connected": "Livechat není připojen.", - "media_types_not_accepted": "Typy příloh nebyly přijaty.", - "message": "Zpráva", - "minimize_chat": "Minimalizovat chat", - "name": "Jméno", - "need_help": "Potřebujete pomoc?", - "new_chat": "Nová konverzace", - "no": "Ne", - "no_available_agents_to_transfer": "Nejsou k dispozici žádní dostupní operátoři pro přenos", - "options": "Volby", - "please_tell_us_some_information_to_start_the_chat": "Řekněte nám prosím nějaké informace pro zahájení chatu", - "please_wait_for_the_next_available_agent": "Počkejte prosím na dalšího dostupného operátora", - "restore_chat": "Obnovit konverzaci", - "room_name_changed": "Název okna změněn", - "send": "Poslat", - "sound_is_off": "Zvuk je vypnutý", - "sound_is_on": "Zvuk je zapnutý", - "start_chat": "Zahájit konverzaci", - "thanks_for_talking_with_us": "Děkujeme, že jste s námi hovořil", - "the_controller_of_your_personal_data_is_company_na": "Správcem vašich osobních údajů je [Název společnosti] se sídlem [Adresa společnosti]. Zahájením konverzace souhlaste s tím, že vaše osobní údaje budou zpracovávány a předávány v souladu s obecným nařízením o ochraně údajů (GDPR).", - "type_your_message_here": "Zde napište svou zprávu", - "unread_messages": "nepřečtené zprávy", - "user_added_by": "Uživatel přidán", - "user_joined": "Uživatel se připojil", - "user_left": "Uživatel odešel", - "user_removed_by": "Uživatel odstraněn", - "waiting_queue": "Čekající v pořadí...", - "we_are_not_online_right_now_please_leave_a_message": "Zrovna nejsme online. Zanechte nám prosím zprávu.", - "welcome": "Vítejte", - "write_your_message": "Napište svou zprávu...", - "yes": "Ano", - "you_browser_doesn_t_support_audio_element": "Váš prohlížeč nepodporuje zvukový prvek", - "you_browser_doesn_t_support_video_element": "Váš prohlížeč nepodporuje video prvek ", - "your_spot_is_spot": "Vaše místo je #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "Vaše místo je #{{spot}} (Předpokládaný čas čekání: {{estimatedWaitTime}})" - } -} + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "Opravdu chcete ukončit chat?", + "are_you_sure_you_want_to_remove_all_of_your_person": "Opravdu chcete smazat všechny Vaše osobní údaje?", + "are_you_sure_you_want_to_switch_the_department": "Opravdu chcete změnit oddělení?", + "cancel": "Zrušit", + "change_department": "Změnit oddělení", + "change_department_1": "Změnit Oddělení", + "chat_finished": "Chat Ukončen", + "choose_a_department": "Vybrat oddělení...", + "choose_a_department_1": "Vybrat oddělení", + "choose_an_option": "Vybrat možnost...", + "conversation_finished": "Konverzace ukončena", + "count_new_messages_since_since_one": "Jedna nová zpráva od {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} nových zpráv od {{val, datetime}}", + "department_switched": "Oddělení změněno", + "departments": "Oddělení", + "disable_notifications": "Zakázat oznámení", + "dismiss_this_alert": "Zrušit toto upozornění", + "drop_here_to_upload_a_file": "Přetažením sem nahrajte soubor", + "email": "E-mail", + "enable_notifications": "Povolit oznámení", + "error_closing_chat": "Chyba při zavírání chatu.", + "error_removing_user_data": "Chyba při odstraňování uživatelských dat.", + "error_starting_a_new_conversation_reason": "Chyba při zahájení nové konverzace: {{reason}}", + "expand_chat": "Rozbalit chat", + "field_required": "Pole je povinné", + "file_exceeds_allowed_size_of_size": "Soubor překračuje povolenou velikost{{size}}.", + "fileupload_error": "Chyba při nahrávání souboru", + "finish_this_chat": "Ukončit tento chat", + "forget_remove_my_data": "Zapomenout/Odebrat mé údaje", + "go_to_menu_options_forget_remove_my_personal_data": "Přejít do <1>možnosti nabídky → Zapomenout/Odebrat mé osobní údaje vyžádat okamžité odstranění Vašich osobních údajů.", + "i_agree": "Souhlasím", + "i_need_help_with": "Potřebuji pomoct s...", + "if_you_have_any_other_questions_just_press_the_but": "Pokud máte jakékoli další otázky, stačí stisknout tlačítko níže a zahájit nový chat", + "insert_your_field_here": "Sem vložte {{field}} ...", + "invalid_email": "Neplatný e-mail", + "invalid_value": "Neplatná hodnota", + "leave_a_message": "Zanechte zprávu", + "livechat_connected": "Livechat připojen.", + "livechat_is_not_connected": "Livechat není připojen.", + "media_types_not_accepted": "Typy příloh nebyly přijaty.", + "message": "Zpráva", + "minimize_chat": "Minimalizovat chat", + "name": "Jméno", + "need_help": "Potřebujete pomoc?", + "new_chat": "Nová konverzace", + "no": "Ne", + "no_available_agents_to_transfer": "Nejsou k dispozici žádní dostupní operátoři pro přenos", + "options": "Volby", + "please_tell_us_some_information_to_start_the_chat": "Řekněte nám prosím nějaké informace pro zahájení chatu", + "please_wait_for_the_next_available_agent": "Počkejte prosím na dalšího dostupného operátora", + "restore_chat": "Obnovit konverzaci", + "room_name_changed": "Název okna změněn", + "send": "Poslat", + "sound_is_off": "Zvuk je vypnutý", + "sound_is_on": "Zvuk je zapnutý", + "start_chat": "Zahájit konverzaci", + "thanks_for_talking_with_us": "Děkujeme, že jste s námi hovořil", + "the_controller_of_your_personal_data_is_company_na": "Správcem vašich osobních údajů je [Název společnosti] se sídlem [Adresa společnosti]. Zahájením konverzace souhlaste s tím, že vaše osobní údaje budou zpracovávány a předávány v souladu s obecným nařízením o ochraně údajů (GDPR).", + "type_your_message_here": "Zde napište svou zprávu", + "unread_messages": "nepřečtené zprávy", + "user_added_by": "Uživatel přidán", + "user_joined": "Uživatel se připojil", + "user_left": "Uživatel odešel", + "user_removed_by": "Uživatel odstraněn", + "waiting_queue": "Čekající v pořadí...", + "we_are_not_online_right_now_please_leave_a_message": "Zrovna nejsme online. Zanechte nám prosím zprávu.", + "welcome": "Vítejte", + "write_your_message": "Napište svou zprávu...", + "yes": "Ano", + "you_browser_doesn_t_support_audio_element": "Váš prohlížeč nepodporuje zvukový prvek", + "you_browser_doesn_t_support_video_element": "Váš prohlížeč nepodporuje video prvek ", + "your_spot_is_spot": "Vaše místo je #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "Vaše místo je #{{spot}} (Předpokládaný čas čekání: {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/cy.json b/packages/livechat/src/i18n/cy.json index 51006ddd71f0..bccee500c925 100644 --- a/packages/livechat/src/i18n/cy.json +++ b/packages/livechat/src/i18n/cy.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Diddymu", - "conversation_finished": "Mae'r sgwrs wedi gorffen", - "department_switched": "Adran wedi newid", - "no": "Na", - "options": "Dewisiadau", - "send": "Anfon", - "user_joined": "Ymunodd y defnyddiwr", - "user_left": "Defnyddiwr ar ôl", - "we_are_not_online_right_now_please_leave_a_message": "Nid ydym ar-lein ar hyn o bryd. Os gwelwch yn dda, gadewch neges.", - "yes": "Ydw" - } -} + "translation": { + "cancel": "Diddymu", + "conversation_finished": "Mae'r sgwrs wedi gorffen", + "department_switched": "Adran wedi newid", + "no": "Na", + "options": "Dewisiadau", + "send": "Anfon", + "user_joined": "Ymunodd y defnyddiwr", + "user_left": "Defnyddiwr ar ôl", + "we_are_not_online_right_now_please_leave_a_message": "Nid ydym ar-lein ar hyn o bryd. Os gwelwch yn dda, gadewch neges.", + "yes": "Ydw" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/da.json b/packages/livechat/src/i18n/da.json index 1255c18e9c2c..997347312987 100644 --- a/packages/livechat/src/i18n/da.json +++ b/packages/livechat/src/i18n/da.json @@ -1,14 +1,14 @@ { - "translation": { - "are_you_sure_you_want_to_switch_the_department": "Er du sikker på, at du vil skifte afdeling?", - "cancel": "Annuller", - "conversation_finished": "Samtalen er slut", - "department_switched": "Du har skiftet afdeling", - "no": "Nej", - "options": "Indstillinger", - "user_joined": "Brugeren sluttede sig til", - "user_left": "Brugeren tog sin afsked", - "we_are_not_online_right_now_please_leave_a_message": "Vi er ikke online lige nu. Efterlad en besked til os.", - "yes": "Ja" - } -} + "translation": { + "are_you_sure_you_want_to_switch_the_department": "Er du sikker på, at du vil skifte afdeling?", + "cancel": "Annuller", + "conversation_finished": "Samtalen er slut", + "department_switched": "Du har skiftet afdeling", + "no": "Nej", + "options": "Indstillinger", + "user_joined": "Brugeren sluttede sig til", + "user_left": "Brugeren tog sin afsked", + "we_are_not_online_right_now_please_leave_a_message": "Vi er ikke online lige nu. Efterlad en besked til os.", + "yes": "Ja" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/de-AT.json b/packages/livechat/src/i18n/de-AT.json index 870f2cdc7183..3bfe91cae32c 100644 --- a/packages/livechat/src/i18n/de-AT.json +++ b/packages/livechat/src/i18n/de-AT.json @@ -1,77 +1,77 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "Sind Sie sicher, dass Sie den Chat beenden möchten?", - "are_you_sure_you_want_to_remove_all_of_your_person": "Sind Sie sicher, dass Sie alle Ihre Daten löschen möchten?", - "are_you_sure_you_want_to_switch_the_department": "Sind Sie sicher, dass Sie die Abteilung wechseln möchten?", - "cancel": "Abbrechen", - "change_department": "Abteilung wechseln", - "change_department_1": "Abteilung wechseln", - "chat_finished": "Chat beendet", - "choose_a_department": "Wählen Sie eine Abteilung", - "choose_a_department_1": "Wählen Sie eine Abteilung", - "choose_an_option": "Wählen Sie eine Option", - "conversation_finished": "Gespräch beendet", - "count_new_messages_since_since_one": "Eine neue Nachricht seit {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} neue Nachrichten seit {{val, datetime}}", - "department_switched": "Abteilung wurde gewechselt", - "departments": "Abteilungen", - "disable_notifications": "Benachrichtigungen deaktivieren", - "dismiss_this_alert": "Diese Warnung schließen", - "drop_here_to_upload_a_file": "Datei zum Hochladen hierher ziehen", - "email": "E-Mail", - "enable_notifications": "Benachrichtigungen aktivieren", - "error_closing_chat": "Beim Schließen des Chats ist ein Fehler aufgetreten", - "error_removing_user_data": "Beim Löschen der Benutzerdaten ist ein Fehler aufgetreten", - "error_starting_a_new_conversation_reason": "Fehler beim Starten einer neuen Konversation: {{reason}}", - "expand_chat": "Chat vergrößern", - "field_required": "Feld erforderlich", - "file_exceeds_allowed_size_of_size": "Die Datei überschreitet die maximal erlaubte Größe von: {{size}}.", - "fileupload_error": "Datei-Upload Fehler", - "finish_this_chat": "Diesen Chat beenden", - "forget_remove_my_data": "Vergiss/Lösche meine Daten", - "gdpr": "DSGVO", - "go_to_menu_options_forget_remove_my_personal_data": "Gehen Sie zu <1>Menü Optionen → Vergiss/Lösche meine Daten, um die sofortige Löschung Ihrer Daten zu beantragen.", - "i_agree": "Ich stimme zu", - "i_need_help_with": "Ich benötige Hilfe mit ...", - "if_you_have_any_other_questions_just_press_the_but": "Falls Sie weitere Fragen haben, drücken Sie unten den Button, um einen neuen Chat zu beginnen.", - "insert_your_field_here": "Hier {{field}} eintragen ...", - "invalid_email": "Ungültige E-Mail-Adresse", - "invalid_value": "Ungültiger Wert", - "leave_a_message": "Hinterlassen Sie eine Nachricht", - "livechat_connected": "Livechat verbunden.", - "livechat_is_not_connected": "Livechat ist nicht verbunden.", - "media_types_not_accepted": "Der Medientyp wird nicht akzeptiert.", - "message": "Nachricht", - "minimize_chat": "Chat minimieren", - "need_help": "Benötigen Sie Hilfe?", - "new_chat": "Neuer Chat", - "no": "Nein", - "no_available_agents_to_transfer": "Keine verfügbaren Mitarbeiter/innen zum Übertragen", - "options": "Optionen", - "please_tell_us_some_information_to_start_the_chat": "Bitte nennen Sie uns ein paar Informationen, um den Chat zu beginnen", - "please_wait_for_the_next_available_agent": "Bitte haben Sie etwas Geduld. Wir sind in Kürze für Sie da ...", - "restore_chat": "Chat wiederaufnehmen", - "room_name_changed": "Raumname geändert", - "send": "Senden", - "sound_is_off": "Ton ist aus", - "sound_is_on": "Ton ist an", - "start_chat": "Chat beginnen", - "thanks_for_talking_with_us": "Vielen Dank für unser Gespräch", - "the_controller_of_your_personal_data_is_company_na": "Der Datenschutzbeauftragte Ihrer persönlichen Daten ist [Company Name], mit eingetragenem Firmensitz in [Company Address]. Indem Sie den Chat starten, erklären Sie sich damit einverstanden, dass Ihre persönlichen Daten im Einklang mit der Datenschutz-Grundverordnung (DSGVO) verarbeitet und weitergegeben werden.", - "type_your_message_here": "Geben Sie hier Ihre Nachricht ein", - "unread_messages": "ungelesene Nachrichten", - "user_added_by": "Benutzer/in hinzugefügt von", - "user_joined": "Benutzer/in ist dem Kanal beigetreten", - "user_left": "Benutzer/in hat den Kanal verlassen", - "user_removed_by": "Benutzer/in entfernt von", - "waiting_queue": "Warteschlange ...", - "we_are_not_online_right_now_please_leave_a_message": "Wir sind momentan nicht online. Bitte hinterlassen Sie uns eine Nachricht.", - "welcome": "Willkommen", - "write_your_message": "Schreiben Sie Ihre Nachricht ...", - "yes": "Ja", - "you_browser_doesn_t_support_audio_element": "Ihr Browser unterstützt keine Audio Elemente", - "you_browser_doesn_t_support_video_element": "Ihr Browser unterstützt keine Video Elemente", - "your_spot_is_spot": "Ihr Platz ist #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "Ihr Platz ist #{{spot}} (geschätzte Wartezeit: {{estimatedWaitTime}})" - } + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "Sind Sie sicher, dass Sie den Chat beenden möchten?", + "are_you_sure_you_want_to_remove_all_of_your_person": "Sind Sie sicher, dass Sie alle Ihre Daten löschen möchten?", + "are_you_sure_you_want_to_switch_the_department": "Sind Sie sicher, dass Sie die Abteilung wechseln möchten?", + "cancel": "Abbrechen", + "change_department": "Abteilung wechseln", + "change_department_1": "Abteilung wechseln", + "chat_finished": "Chat beendet", + "choose_a_department": "Wählen Sie eine Abteilung", + "choose_a_department_1": "Wählen Sie eine Abteilung", + "choose_an_option": "Wählen Sie eine Option", + "conversation_finished": "Gespräch beendet", + "count_new_messages_since_since_one": "Eine neue Nachricht seit {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} neue Nachrichten seit {{val, datetime}}", + "department_switched": "Abteilung wurde gewechselt", + "departments": "Abteilungen", + "disable_notifications": "Benachrichtigungen deaktivieren", + "dismiss_this_alert": "Diese Warnung schließen", + "drop_here_to_upload_a_file": "Datei zum Hochladen hierher ziehen", + "email": "E-Mail", + "enable_notifications": "Benachrichtigungen aktivieren", + "error_closing_chat": "Beim Schließen des Chats ist ein Fehler aufgetreten", + "error_removing_user_data": "Beim Löschen der Benutzerdaten ist ein Fehler aufgetreten", + "error_starting_a_new_conversation_reason": "Fehler beim Starten einer neuen Konversation: {{reason}}", + "expand_chat": "Chat vergrößern", + "field_required": "Feld erforderlich", + "file_exceeds_allowed_size_of_size": "Die Datei überschreitet die maximal erlaubte Größe von: {{size}}.", + "fileupload_error": "Datei-Upload Fehler", + "finish_this_chat": "Diesen Chat beenden", + "forget_remove_my_data": "Vergiss/Lösche meine Daten", + "gdpr": "DSGVO", + "go_to_menu_options_forget_remove_my_personal_data": "Gehen Sie zu <1>Menü Optionen → Vergiss/Lösche meine Daten, um die sofortige Löschung Ihrer Daten zu beantragen.", + "i_agree": "Ich stimme zu", + "i_need_help_with": "Ich benötige Hilfe mit ...", + "if_you_have_any_other_questions_just_press_the_but": "Falls Sie weitere Fragen haben, drücken Sie unten den Button, um einen neuen Chat zu beginnen.", + "insert_your_field_here": "Hier {{field}} eintragen ...", + "invalid_email": "Ungültige E-Mail-Adresse", + "invalid_value": "Ungültiger Wert", + "leave_a_message": "Hinterlassen Sie eine Nachricht", + "livechat_connected": "Livechat verbunden.", + "livechat_is_not_connected": "Livechat ist nicht verbunden.", + "media_types_not_accepted": "Der Medientyp wird nicht akzeptiert.", + "message": "Nachricht", + "minimize_chat": "Chat minimieren", + "need_help": "Benötigen Sie Hilfe?", + "new_chat": "Neuer Chat", + "no": "Nein", + "no_available_agents_to_transfer": "Keine verfügbaren Mitarbeiter/innen zum Übertragen", + "options": "Optionen", + "please_tell_us_some_information_to_start_the_chat": "Bitte nennen Sie uns ein paar Informationen, um den Chat zu beginnen", + "please_wait_for_the_next_available_agent": "Bitte haben Sie etwas Geduld. Wir sind in Kürze für Sie da ...", + "restore_chat": "Chat wiederaufnehmen", + "room_name_changed": "Raumname geändert", + "send": "Senden", + "sound_is_off": "Ton ist aus", + "sound_is_on": "Ton ist an", + "start_chat": "Chat beginnen", + "thanks_for_talking_with_us": "Vielen Dank für unser Gespräch", + "the_controller_of_your_personal_data_is_company_na": "Der Datenschutzbeauftragte Ihrer persönlichen Daten ist [Company Name], mit eingetragenem Firmensitz in [Company Address]. Indem Sie den Chat starten, erklären Sie sich damit einverstanden, dass Ihre persönlichen Daten im Einklang mit der Datenschutz-Grundverordnung (DSGVO) verarbeitet und weitergegeben werden.", + "type_your_message_here": "Geben Sie hier Ihre Nachricht ein", + "unread_messages": "ungelesene Nachrichten", + "user_added_by": "Benutzer/in hinzugefügt von", + "user_joined": "Benutzer/in ist dem Kanal beigetreten", + "user_left": "Benutzer/in hat den Kanal verlassen", + "user_removed_by": "Benutzer/in entfernt von", + "waiting_queue": "Warteschlange ...", + "we_are_not_online_right_now_please_leave_a_message": "Wir sind momentan nicht online. Bitte hinterlassen Sie uns eine Nachricht.", + "welcome": "Willkommen", + "write_your_message": "Schreiben Sie Ihre Nachricht ...", + "yes": "Ja", + "you_browser_doesn_t_support_audio_element": "Ihr Browser unterstützt keine Audio Elemente", + "you_browser_doesn_t_support_video_element": "Ihr Browser unterstützt keine Video Elemente", + "your_spot_is_spot": "Ihr Platz ist #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "Ihr Platz ist #{{spot}} (geschätzte Wartezeit: {{estimatedWaitTime}})" + } } \ No newline at end of file diff --git a/packages/livechat/src/i18n/de.json b/packages/livechat/src/i18n/de.json index 1c59667099ed..3bfe91cae32c 100644 --- a/packages/livechat/src/i18n/de.json +++ b/packages/livechat/src/i18n/de.json @@ -1,77 +1,77 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "Sind Sie sicher, dass Sie den Chat beenden möchten?", - "are_you_sure_you_want_to_remove_all_of_your_person": "Sind Sie sicher, dass Sie alle Ihre Daten löschen möchten?", - "are_you_sure_you_want_to_switch_the_department": "Sind Sie sicher, dass Sie die Abteilung wechseln möchten?", - "cancel": "Abbrechen", - "change_department": "Abteilung wechseln", - "change_department_1": "Abteilung wechseln", - "chat_finished": "Chat beendet", - "choose_a_department": "Wählen Sie eine Abteilung", - "choose_a_department_1": "Wählen Sie eine Abteilung", - "choose_an_option": "Wählen Sie eine Option", - "conversation_finished": "Gespräch beendet", - "count_new_messages_since_since_one": "Eine neue Nachricht seit {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} neue Nachrichten seit {{val, datetime}}", - "department_switched": "Abteilung wurde gewechselt", - "departments": "Abteilungen", - "disable_notifications": "Benachrichtigungen deaktivieren", - "dismiss_this_alert": "Diese Warnung schließen", - "drop_here_to_upload_a_file": "Datei zum Hochladen hierher ziehen", - "email": "E-Mail", - "enable_notifications": "Benachrichtigungen aktivieren", - "error_closing_chat": "Beim Schließen des Chats ist ein Fehler aufgetreten", - "error_removing_user_data": "Beim Löschen der Benutzerdaten ist ein Fehler aufgetreten", - "error_starting_a_new_conversation_reason": "Fehler beim Starten einer neuen Konversation: {{reason}}", - "expand_chat": "Chat vergrößern", - "field_required": "Feld erforderlich", - "file_exceeds_allowed_size_of_size": "Die Datei überschreitet die maximal erlaubte Größe von: {{size}}.", - "fileupload_error": "Datei-Upload Fehler", - "finish_this_chat": "Diesen Chat beenden", - "forget_remove_my_data": "Vergiss/Lösche meine Daten", - "gdpr": "DSGVO", - "go_to_menu_options_forget_remove_my_personal_data": "Gehen Sie zu <1>Menü Optionen → Vergiss/Lösche meine Daten, um die sofortige Löschung Ihrer Daten zu beantragen.", - "i_agree": "Ich stimme zu", - "i_need_help_with": "Ich benötige Hilfe mit ...", - "if_you_have_any_other_questions_just_press_the_but": "Falls Sie weitere Fragen haben, drücken Sie unten den Button, um einen neuen Chat zu beginnen.", - "insert_your_field_here": "Hier {{field}} eintragen ...", - "invalid_email": "Ungültige E-Mail-Adresse", - "invalid_value": "Ungültiger Wert", - "leave_a_message": "Hinterlassen Sie eine Nachricht", - "livechat_connected": "Livechat verbunden.", - "livechat_is_not_connected": "Livechat ist nicht verbunden.", - "media_types_not_accepted": "Der Medientyp wird nicht akzeptiert.", - "message": "Nachricht", - "minimize_chat": "Chat minimieren", - "need_help": "Benötigen Sie Hilfe?", - "new_chat": "Neuer Chat", - "no": "Nein", - "no_available_agents_to_transfer": "Keine verfügbaren Mitarbeiter/innen zum Übertragen", - "options": "Optionen", - "please_tell_us_some_information_to_start_the_chat": "Bitte nennen Sie uns ein paar Informationen, um den Chat zu beginnen", - "please_wait_for_the_next_available_agent": "Bitte haben Sie etwas Geduld. Wir sind in Kürze für Sie da ...", - "restore_chat": "Chat wiederaufnehmen", - "room_name_changed": "Raumname geändert", - "send": "Senden", - "sound_is_off": "Ton ist aus", - "sound_is_on": "Ton ist an", - "start_chat": "Chat beginnen", - "thanks_for_talking_with_us": "Vielen Dank für unser Gespräch", - "the_controller_of_your_personal_data_is_company_na": "Der Datenschutzbeauftragte Ihrer persönlichen Daten ist [Company Name], mit eingetragenem Firmensitz in [Company Address]. Indem Sie den Chat starten, erklären Sie sich damit einverstanden, dass Ihre persönlichen Daten im Einklang mit der Datenschutz-Grundverordnung (DSGVO) verarbeitet und weitergegeben werden.", - "type_your_message_here": "Geben Sie hier Ihre Nachricht ein", - "unread_messages": "ungelesene Nachrichten", - "user_added_by": "Benutzer/in hinzugefügt von", - "user_joined": "Benutzer/in ist dem Kanal beigetreten", - "user_left": "Benutzer/in hat den Kanal verlassen", - "user_removed_by": "Benutzer/in entfernt von", - "waiting_queue": "Warteschlange ...", - "we_are_not_online_right_now_please_leave_a_message": "Wir sind momentan nicht online. Bitte hinterlassen Sie uns eine Nachricht.", - "welcome": "Willkommen", - "write_your_message": "Schreiben Sie Ihre Nachricht ...", - "yes": "Ja", - "you_browser_doesn_t_support_audio_element": "Ihr Browser unterstützt keine Audio Elemente", - "you_browser_doesn_t_support_video_element": "Ihr Browser unterstützt keine Video Elemente", - "your_spot_is_spot": "Ihr Platz ist #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "Ihr Platz ist #{{spot}} (geschätzte Wartezeit: {{estimatedWaitTime}})" - } -} + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "Sind Sie sicher, dass Sie den Chat beenden möchten?", + "are_you_sure_you_want_to_remove_all_of_your_person": "Sind Sie sicher, dass Sie alle Ihre Daten löschen möchten?", + "are_you_sure_you_want_to_switch_the_department": "Sind Sie sicher, dass Sie die Abteilung wechseln möchten?", + "cancel": "Abbrechen", + "change_department": "Abteilung wechseln", + "change_department_1": "Abteilung wechseln", + "chat_finished": "Chat beendet", + "choose_a_department": "Wählen Sie eine Abteilung", + "choose_a_department_1": "Wählen Sie eine Abteilung", + "choose_an_option": "Wählen Sie eine Option", + "conversation_finished": "Gespräch beendet", + "count_new_messages_since_since_one": "Eine neue Nachricht seit {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} neue Nachrichten seit {{val, datetime}}", + "department_switched": "Abteilung wurde gewechselt", + "departments": "Abteilungen", + "disable_notifications": "Benachrichtigungen deaktivieren", + "dismiss_this_alert": "Diese Warnung schließen", + "drop_here_to_upload_a_file": "Datei zum Hochladen hierher ziehen", + "email": "E-Mail", + "enable_notifications": "Benachrichtigungen aktivieren", + "error_closing_chat": "Beim Schließen des Chats ist ein Fehler aufgetreten", + "error_removing_user_data": "Beim Löschen der Benutzerdaten ist ein Fehler aufgetreten", + "error_starting_a_new_conversation_reason": "Fehler beim Starten einer neuen Konversation: {{reason}}", + "expand_chat": "Chat vergrößern", + "field_required": "Feld erforderlich", + "file_exceeds_allowed_size_of_size": "Die Datei überschreitet die maximal erlaubte Größe von: {{size}}.", + "fileupload_error": "Datei-Upload Fehler", + "finish_this_chat": "Diesen Chat beenden", + "forget_remove_my_data": "Vergiss/Lösche meine Daten", + "gdpr": "DSGVO", + "go_to_menu_options_forget_remove_my_personal_data": "Gehen Sie zu <1>Menü Optionen → Vergiss/Lösche meine Daten, um die sofortige Löschung Ihrer Daten zu beantragen.", + "i_agree": "Ich stimme zu", + "i_need_help_with": "Ich benötige Hilfe mit ...", + "if_you_have_any_other_questions_just_press_the_but": "Falls Sie weitere Fragen haben, drücken Sie unten den Button, um einen neuen Chat zu beginnen.", + "insert_your_field_here": "Hier {{field}} eintragen ...", + "invalid_email": "Ungültige E-Mail-Adresse", + "invalid_value": "Ungültiger Wert", + "leave_a_message": "Hinterlassen Sie eine Nachricht", + "livechat_connected": "Livechat verbunden.", + "livechat_is_not_connected": "Livechat ist nicht verbunden.", + "media_types_not_accepted": "Der Medientyp wird nicht akzeptiert.", + "message": "Nachricht", + "minimize_chat": "Chat minimieren", + "need_help": "Benötigen Sie Hilfe?", + "new_chat": "Neuer Chat", + "no": "Nein", + "no_available_agents_to_transfer": "Keine verfügbaren Mitarbeiter/innen zum Übertragen", + "options": "Optionen", + "please_tell_us_some_information_to_start_the_chat": "Bitte nennen Sie uns ein paar Informationen, um den Chat zu beginnen", + "please_wait_for_the_next_available_agent": "Bitte haben Sie etwas Geduld. Wir sind in Kürze für Sie da ...", + "restore_chat": "Chat wiederaufnehmen", + "room_name_changed": "Raumname geändert", + "send": "Senden", + "sound_is_off": "Ton ist aus", + "sound_is_on": "Ton ist an", + "start_chat": "Chat beginnen", + "thanks_for_talking_with_us": "Vielen Dank für unser Gespräch", + "the_controller_of_your_personal_data_is_company_na": "Der Datenschutzbeauftragte Ihrer persönlichen Daten ist [Company Name], mit eingetragenem Firmensitz in [Company Address]. Indem Sie den Chat starten, erklären Sie sich damit einverstanden, dass Ihre persönlichen Daten im Einklang mit der Datenschutz-Grundverordnung (DSGVO) verarbeitet und weitergegeben werden.", + "type_your_message_here": "Geben Sie hier Ihre Nachricht ein", + "unread_messages": "ungelesene Nachrichten", + "user_added_by": "Benutzer/in hinzugefügt von", + "user_joined": "Benutzer/in ist dem Kanal beigetreten", + "user_left": "Benutzer/in hat den Kanal verlassen", + "user_removed_by": "Benutzer/in entfernt von", + "waiting_queue": "Warteschlange ...", + "we_are_not_online_right_now_please_leave_a_message": "Wir sind momentan nicht online. Bitte hinterlassen Sie uns eine Nachricht.", + "welcome": "Willkommen", + "write_your_message": "Schreiben Sie Ihre Nachricht ...", + "yes": "Ja", + "you_browser_doesn_t_support_audio_element": "Ihr Browser unterstützt keine Audio Elemente", + "you_browser_doesn_t_support_video_element": "Ihr Browser unterstützt keine Video Elemente", + "your_spot_is_spot": "Ihr Platz ist #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "Ihr Platz ist #{{spot}} (geschätzte Wartezeit: {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/el.json b/packages/livechat/src/i18n/el.json index 29751d5cac7a..eaf6ce80fc82 100644 --- a/packages/livechat/src/i18n/el.json +++ b/packages/livechat/src/i18n/el.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Ακύρωση", - "conversation_finished": "Η συνομιλία τελείωσε", - "department_switched": "Το τμήμα άλλαξε", - "no": "Όχι", - "options": "Επιλογές", - "send": "Αποστολή", - "user_joined": "Ο χρήστης συνδέθηκε", - "user_left": "Ο χρήστης έφυγε", - "we_are_not_online_right_now_please_leave_a_message": "Δεν είμαστε συνδεδεμένοι αυτήν τη στιγμή. Παρακαλώ αφήστε μήνυμα.", - "yes": "Ναι" - } -} + "translation": { + "cancel": "Ακύρωση", + "conversation_finished": "Η συνομιλία τελείωσε", + "department_switched": "Το τμήμα άλλαξε", + "no": "Όχι", + "options": "Επιλογές", + "send": "Αποστολή", + "user_joined": "Ο χρήστης συνδέθηκε", + "user_left": "Ο χρήστης έφυγε", + "we_are_not_online_right_now_please_leave_a_message": "Δεν είμαστε συνδεδεμένοι αυτήν τη στιγμή. Παρακαλώ αφήστε μήνυμα.", + "yes": "Ναι" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/en.json b/packages/livechat/src/i18n/en.json index 791e2262351f..620f53be45e7 100644 --- a/packages/livechat/src/i18n/en.json +++ b/packages/livechat/src/i18n/en.json @@ -1,105 +1,105 @@ { - "translation": { - "accept": "Accept", - "are_you_sure_you_want_to_finish_this_chat": "Are you sure you want to finish this chat?", - "are_you_sure_you_want_to_remove_all_of_your_person": "Are you sure you want to remove all of your personal data?", - "are_you_sure_you_want_to_switch_the_department": "Are you sure you want to switch the department?", - "call_end_time": "Call Ended at {{time, datetime}} - Lasted {{callDuration, datetime}}", - "cancel": "Cancel", - "change_department": "Change department", - "change_department_1": "Change Department", - "chat_finished": "Chat Finished", - "chat_now": "Chat now", - "chat_started": "Chat started", - "choose_a_department": "Choose a department...", - "choose_a_department_1": "Choose a department", - "choose_an_option": "Choose an option...", - "conversation_finished": "Conversation finished", - "count_new_messages_since_since_one": "One new message since {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} new messages since {{val, datetime}}", - "decline": "Decline", - "department_switched": "Department switched", - "departments": "Departments", - "disable_notifications": "Disable notifications", - "dismiss_this_alert": "Dismiss this alert", - "drop_here_to_upload_a_file": "Drop here to upload a file", - "email": "Email", - "enable_notifications": "Enable notifications", - "error_closing_chat": "Error closing chat.", - "error_getting_call_alert": "Error getting call alert.", - "error_removing_user_data": "Error removing user data.", - "error_starting_a_new_conversation_reason": "Error starting a new conversation: {{reason}}", - "expand_chat": "Expand chat", - "field_required": "Field required", - "file_exceeds_allowed_size_of_size": "File exceeds allowed size of {{size}}.", - "fileupload_error": "FileUpload Error", - "finish_this_chat": "Finish this chat", - "forget_remove_my_data": "Forget/Remove my data", - "from_returned_the_chat_to_the_queue": "{{from}} returned the chat to the queue", - "the_agent_transferred_the_chat_to_the_department_to": "The agent transferred the chat to the department {{to}}", - "from_transferred_the_chat_to_the_department_to": "{{from}} transferred the chat to the department {{to}}", - "from_transferred_the_chat_to_to": "{{from}} transferred the chat to {{to}}", - "gdpr": "GDPR", - "go_to_menu_options_forget_remove_my_personal_data": "Go to <1>menu options → Forget/Remove my personal data to request the immediate removal of your data.", - "hiddenelementscount_more": "+ {{hiddenElementsCount}} more", - "i_agree": "I Agree", - "i_need_help_with": "I need help with...", - "if_you_have_any_other_questions_just_press_the_but": "If you have any other questions, just press the button below to start a new chat.", - "incoming_video_call": "Incoming video Call", - "insert_your_field_here": "Insert your {{field}} here...", - "invalid_email": "Invalid email", - "invalid_value": "Invalid value", - "join_call": "Join Call", - "join_my_room_to_start_the_video_call": "Join my room to start the video call", - "leave_a_message": "Leave a message", - "livechat_connected": "Livechat connected.", - "livechat_is_not_connected": "Livechat is not connected.", - "media_types_not_accepted": "Media Types Not Accepted.", - "message": "Message", - "messages": "Messages", - "message_separator_date": "{{val, datetime}}", - "message_time": "{{val, datetime}}", - "minimize_chat": "Minimize chat", - "name": "Name", - "need_help": "Need help?", - "new_chat": "New Chat", - "no": "No", - "no_available_agents_to_transfer": "No available agents to transfer", - "ok": "OK", - "options": "Options", - "please_tell_us_some_information_to_start_the_chat": "Please, tell us some information to start the chat", - "please_wait_for_the_next_available_agent": "Please, wait for the next available agent..", - "powered_by_rocket_chat": "Powered by Rocket.Chat", - "restore_chat": "Restore chat", - "room_name_changed": "Room name changed", - "send": "Send", - "sound_is_off": "Sound is off", - "sound_is_on": "Sound is on", - "start_chat": "Start chat", - "thanks_for_talking_with_us": "Thanks for talking with us", - "the_chat_was_moved_back_to_queue": "The chat was moved back to queue", - "the_chat_was_moved_back_to_queue_due_to_unanswered": "The chat was moved back to queue since it was unanswered for {{duration}} seconds", - "the_chat_was_transferred_to_another_agent": "The chat was transferred to another agent", - "the_chat_was_transferred_to_another_agent_due_to_unanswered": "The chat was transferred to another agent since it was unanswered for {{duration}} seconds", - "the_controller_of_your_personal_data_is_company_na": "The controller of your personal data is [Company Name], with registered office at [Company Address]. To start the chat you agree that your personal data shall be processed and trasmitted in accordance with the General Data Protection Regulation (GDPR).", - "transcript_success": "Transcript sent", - "type_your_message_here": "Type your message here", - "unread_messages_count_one": "{{count}} unread message", - "unread_messages_count_other": "{{count}} unread messages", - "unread_messages": "unread messages", - "user_added_by": "User added by", - "user_joined": "User joined", - "user_left": "User left", - "user_removed_by": "User removed by", - "waiting_queue": "Waiting queue...", - "we_are_not_online_right_now_please_leave_a_message": "We are not online right now. Please, leave a message.", - "welcome": "Welcome", - "would_you_like_a_copy_of_this_chat_emailed":"Would you like a copy of this chat emailed?", - "write_your_message": "Write your message...", - "yes": "Yes", - "you_browser_doesn_t_support_audio_element": "You browser doesn't support audio element", - "you_browser_doesn_t_support_video_element": "You browser doesn't support video element", - "your_spot_is_spot": "Your spot is #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "Your spot is #{{spot}} (Estimated wait time: {{estimatedWaitTime}})" - } -} + "translation": { + "accept": "Accept", + "are_you_sure_you_want_to_finish_this_chat": "Are you sure you want to finish this chat?", + "are_you_sure_you_want_to_remove_all_of_your_person": "Are you sure you want to remove all of your personal data?", + "are_you_sure_you_want_to_switch_the_department": "Are you sure you want to switch the department?", + "call_end_time": "Call Ended at {{time, datetime}} - Lasted {{callDuration, datetime}}", + "cancel": "Cancel", + "change_department": "Change department", + "change_department_1": "Change Department", + "chat_finished": "Chat Finished", + "chat_now": "Chat now", + "chat_started": "Chat started", + "choose_a_department": "Choose a department...", + "choose_a_department_1": "Choose a department", + "choose_an_option": "Choose an option...", + "conversation_finished": "Conversation finished", + "count_new_messages_since_since_one": "One new message since {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} new messages since {{val, datetime}}", + "decline": "Decline", + "department_switched": "Department switched", + "departments": "Departments", + "disable_notifications": "Disable notifications", + "dismiss_this_alert": "Dismiss this alert", + "drop_here_to_upload_a_file": "Drop here to upload a file", + "email": "Email", + "enable_notifications": "Enable notifications", + "error_closing_chat": "Error closing chat.", + "error_getting_call_alert": "Error getting call alert.", + "error_removing_user_data": "Error removing user data.", + "error_starting_a_new_conversation_reason": "Error starting a new conversation: {{reason}}", + "expand_chat": "Expand chat", + "field_required": "Field required", + "file_exceeds_allowed_size_of_size": "File exceeds allowed size of {{size}}.", + "fileupload_error": "FileUpload Error", + "finish_this_chat": "Finish this chat", + "forget_remove_my_data": "Forget/Remove my data", + "from_returned_the_chat_to_the_queue": "{{from}} returned the chat to the queue", + "the_agent_transferred_the_chat_to_the_department_to": "The agent transferred the chat to the department {{to}}", + "from_transferred_the_chat_to_the_department_to": "{{from}} transferred the chat to the department {{to}}", + "from_transferred_the_chat_to_to": "{{from}} transferred the chat to {{to}}", + "gdpr": "GDPR", + "go_to_menu_options_forget_remove_my_personal_data": "Go to <1>menu options → Forget/Remove my personal data to request the immediate removal of your data.", + "hiddenelementscount_more": "+ {{hiddenElementsCount}} more", + "i_agree": "I Agree", + "i_need_help_with": "I need help with...", + "if_you_have_any_other_questions_just_press_the_but": "If you have any other questions, just press the button below to start a new chat.", + "incoming_video_call": "Incoming video Call", + "insert_your_field_here": "Insert your {{field}} here...", + "invalid_email": "Invalid email", + "invalid_value": "Invalid value", + "join_call": "Join Call", + "join_my_room_to_start_the_video_call": "Join my room to start the video call", + "leave_a_message": "Leave a message", + "livechat_connected": "Livechat connected.", + "livechat_is_not_connected": "Livechat is not connected.", + "media_types_not_accepted": "Media Types Not Accepted.", + "message": "Message", + "messages": "Messages", + "message_separator_date": "{{val, datetime}}", + "message_time": "{{val, datetime}}", + "minimize_chat": "Minimize chat", + "name": "Name", + "need_help": "Need help?", + "new_chat": "New Chat", + "no": "No", + "no_available_agents_to_transfer": "No available agents to transfer", + "ok": "OK", + "options": "Options", + "please_tell_us_some_information_to_start_the_chat": "Please, tell us some information to start the chat", + "please_wait_for_the_next_available_agent": "Please, wait for the next available agent..", + "powered_by_rocket_chat": "Powered by Rocket.Chat", + "restore_chat": "Restore chat", + "room_name_changed": "Room name changed", + "send": "Send", + "sound_is_off": "Sound is off", + "sound_is_on": "Sound is on", + "start_chat": "Start chat", + "thanks_for_talking_with_us": "Thanks for talking with us", + "the_chat_was_moved_back_to_queue": "The chat was moved back to queue", + "the_chat_was_moved_back_to_queue_due_to_unanswered": "The chat was moved back to queue since it was unanswered for {{duration}} seconds", + "the_chat_was_transferred_to_another_agent": "The chat was transferred to another agent", + "the_chat_was_transferred_to_another_agent_due_to_unanswered": "The chat was transferred to another agent since it was unanswered for {{duration}} seconds", + "the_controller_of_your_personal_data_is_company_na": "The controller of your personal data is [Company Name], with registered office at [Company Address]. To start the chat you agree that your personal data shall be processed and trasmitted in accordance with the General Data Protection Regulation (GDPR).", + "transcript_success": "Transcript sent", + "type_your_message_here": "Type your message here", + "unread_messages_count_one": "{{count}} unread message", + "unread_messages_count_other": "{{count}} unread messages", + "unread_messages": "unread messages", + "user_added_by": "User added by", + "user_joined": "User joined", + "user_left": "User left", + "user_removed_by": "User removed by", + "waiting_queue": "Waiting queue...", + "we_are_not_online_right_now_please_leave_a_message": "We are not online right now. Please, leave a message.", + "welcome": "Welcome", + "would_you_like_a_copy_of_this_chat_emailed": "Would you like a copy of this chat emailed?", + "write_your_message": "Write your message...", + "yes": "Yes", + "you_browser_doesn_t_support_audio_element": "You browser doesn't support audio element", + "you_browser_doesn_t_support_video_element": "You browser doesn't support video element", + "your_spot_is_spot": "Your spot is #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "Your spot is #{{spot}} (Estimated wait time: {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/eo.json b/packages/livechat/src/i18n/eo.json index 58a17723677d..8b3e97705281 100644 --- a/packages/livechat/src/i18n/eo.json +++ b/packages/livechat/src/i18n/eo.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Nuligi", - "conversation_finished": "Konversacio finis", - "department_switched": "Fako ŝaltis", - "no": "Ne", - "options": "Opcioj", - "send": "Sendu", - "user_joined": "Uzanto aliĝis", - "user_left": "Uzanto maldekstre", - "we_are_not_online_right_now_please_leave_a_message": "Ni ne estas interrete nun. Bonvolu lasi mesaĝon.", - "yes": "Jes" - } -} + "translation": { + "cancel": "Nuligi", + "conversation_finished": "Konversacio finis", + "department_switched": "Fako ŝaltis", + "no": "Ne", + "options": "Opcioj", + "send": "Sendu", + "user_joined": "Uzanto aliĝis", + "user_left": "Uzanto maldekstre", + "we_are_not_online_right_now_please_leave_a_message": "Ni ne estas interrete nun. Bonvolu lasi mesaĝon.", + "yes": "Jes" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/es.json b/packages/livechat/src/i18n/es.json index 832654c2c089..04bb8438d13f 100644 --- a/packages/livechat/src/i18n/es.json +++ b/packages/livechat/src/i18n/es.json @@ -1,77 +1,77 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "¿Estás seguro que quieres finalizar este chat?", - "are_you_sure_you_want_to_remove_all_of_your_person": "¿Estás seguro que quieres eliminar todos tus datos personales?", - "are_you_sure_you_want_to_switch_the_department": "¿Estás seguro que quieres cambiar el departamento?", - "cancel": "Cancelar", - "change_department": "Cambiar el departamento", - "change_department_1": "Cambiar el departamento", - "chat_finished": "Chat terminado", - "chat_now": "Chatear ahora", - "choose_a_department": "Seleccione un departamento", - "choose_a_department_1": "Seleccione un departamento", - "choose_an_option": "Seleccione una opción...", - "conversation_finished": "Conversación terminada", - "count_new_messages_since_since_one": "Un nuevo mensaje desde {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} nuevos mensajes desde {{val, datetime}}", - "department_switched": "Departamento cambiado", - "departments": "Departamentos", - "disable_notifications": "Desactivar notificaciones", - "dismiss_this_alert": "Descartar esta alerta", - "drop_here_to_upload_a_file": "Arrastre aquí para subir un archivo", - "enable_notifications": "Activar notificaciones", - "error_closing_chat": "Error cerrando el chat.", - "error_removing_user_data": "Error eliminando datos de usuario.", - "error_starting_a_new_conversation_reason": "Error al iniciar una nueva conversación: {{reason}}", - "expand_chat": "Expandir chat", - "field_required": "Archivo requerido", - "file_exceeds_allowed_size_of_size": "El archivo excede el tamaño permitido {{size}}.", - "fileupload_error": "Error en subida de archivo", - "finish_this_chat": "Finalizar este chat", - "forget_remove_my_data": "Olvidar/Eliminar mis datos", - "go_to_menu_options_forget_remove_my_personal_data": "Ir al <1>menu opciones → Olvidar/Eliminar mis datos para solicitar la inmediata remoción de tus datos.", - "hiddenelementscount_more": "+ {{hiddenElementsCount}} más", - "i_agree": "Acepto", - "i_need_help_with": "Necesito ayuda con...", - "if_you_have_any_other_questions_just_press_the_but": "Si tiene otra pregunta, simplemente presione el botón a continuación para iniciar un nuevo chat.", - "insert_your_field_here": "Ingrese su {{field}} aquí...", - "invalid_email": "Email no válido", - "invalid_value": "Valor no válido", - "leave_a_message": "Deje un mensaje", - "livechat_connected": "Livechat conectado.", - "livechat_is_not_connected": "Livechat no está conectado.", - "media_types_not_accepted": "Tipos de medios no aceptado.", - "message": "Mensaje", - "minimize_chat": "Minimizar chat", - "name": "Nombre", - "need_help": "¿Necesita ayuda?", - "new_chat": "Nuevo Chat", - "no_available_agents_to_transfer": "No hay agentes disponibles para transferir", - "options": "Opciones", - "please_tell_us_some_information_to_start_the_chat": "Por favor, ingrese alguna información para iniciar el chat", - "please_wait_for_the_next_available_agent": "Por favor, espere al próximo agente disponible.", - "restore_chat": "Restaurar chat", - "room_name_changed": "Nombre de sala cambiado", - "send": "Enviar", - "sound_is_off": "Sonido encendido", - "sound_is_on": "Sonido apagado", - "start_chat": "Comenzar chat", - "thanks_for_talking_with_us": "Gracias por hablar con nosotros", - "the_controller_of_your_personal_data_is_company_na": "El controlador de sus datos personales es [Company Name], con domicilio en [Company Address]. Para iniciar el chat, acepta que sus datos personales se procesen y transmitan de acuerdo con el Reglamento General de Protección de Datos (GDPR).", - "type_your_message_here": "Escribe tu mensaje aquí", - "unread_messages": "Mensajes no leídos", - "user_added_by": "Usuario agregado por", - "user_joined": "Usuario agregado", - "user_left": "El usuario ha abandonado la sala", - "user_removed_by": "Usuario eliminado por", - "waiting_queue": "Esperando cola...", - "we_are_not_online_right_now_please_leave_a_message": "No estamos en línea en este momento. Por favor, deje un mensaje.", - "welcome": "Bienvenido", - "write_your_message": "Escribe tu mensaje...", - "yes": "Si", - "you_browser_doesn_t_support_audio_element": "Tu navegador no soporta elementos de audio", - "you_browser_doesn_t_support_video_element": "Tu navegador no soporta elementos de video", - "your_spot_is_spot": "Tu lugar es #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "Tu lugar es #{{spot}} (Tiempo de espera estimado: {{estimatedWaitTime}})" - } -} + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "¿Estás seguro que quieres finalizar este chat?", + "are_you_sure_you_want_to_remove_all_of_your_person": "¿Estás seguro que quieres eliminar todos tus datos personales?", + "are_you_sure_you_want_to_switch_the_department": "¿Estás seguro que quieres cambiar el departamento?", + "cancel": "Cancelar", + "change_department": "Cambiar el departamento", + "change_department_1": "Cambiar el departamento", + "chat_finished": "Chat terminado", + "chat_now": "Chatear ahora", + "choose_a_department": "Seleccione un departamento", + "choose_a_department_1": "Seleccione un departamento", + "choose_an_option": "Seleccione una opción...", + "conversation_finished": "Conversación terminada", + "count_new_messages_since_since_one": "Un nuevo mensaje desde {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} nuevos mensajes desde {{val, datetime}}", + "department_switched": "Departamento cambiado", + "departments": "Departamentos", + "disable_notifications": "Desactivar notificaciones", + "dismiss_this_alert": "Descartar esta alerta", + "drop_here_to_upload_a_file": "Arrastre aquí para subir un archivo", + "enable_notifications": "Activar notificaciones", + "error_closing_chat": "Error cerrando el chat.", + "error_removing_user_data": "Error eliminando datos de usuario.", + "error_starting_a_new_conversation_reason": "Error al iniciar una nueva conversación: {{reason}}", + "expand_chat": "Expandir chat", + "field_required": "Archivo requerido", + "file_exceeds_allowed_size_of_size": "El archivo excede el tamaño permitido {{size}}.", + "fileupload_error": "Error en subida de archivo", + "finish_this_chat": "Finalizar este chat", + "forget_remove_my_data": "Olvidar/Eliminar mis datos", + "go_to_menu_options_forget_remove_my_personal_data": "Ir al <1>menu opciones → Olvidar/Eliminar mis datos para solicitar la inmediata remoción de tus datos.", + "hiddenelementscount_more": "+ {{hiddenElementsCount}} más", + "i_agree": "Acepto", + "i_need_help_with": "Necesito ayuda con...", + "if_you_have_any_other_questions_just_press_the_but": "Si tiene otra pregunta, simplemente presione el botón a continuación para iniciar un nuevo chat.", + "insert_your_field_here": "Ingrese su {{field}} aquí...", + "invalid_email": "Email no válido", + "invalid_value": "Valor no válido", + "leave_a_message": "Deje un mensaje", + "livechat_connected": "Livechat conectado.", + "livechat_is_not_connected": "Livechat no está conectado.", + "media_types_not_accepted": "Tipos de medios no aceptado.", + "message": "Mensaje", + "minimize_chat": "Minimizar chat", + "name": "Nombre", + "need_help": "¿Necesita ayuda?", + "new_chat": "Nuevo Chat", + "no_available_agents_to_transfer": "No hay agentes disponibles para transferir", + "options": "Opciones", + "please_tell_us_some_information_to_start_the_chat": "Por favor, ingrese alguna información para iniciar el chat", + "please_wait_for_the_next_available_agent": "Por favor, espere al próximo agente disponible.", + "restore_chat": "Restaurar chat", + "room_name_changed": "Nombre de sala cambiado", + "send": "Enviar", + "sound_is_off": "Sonido encendido", + "sound_is_on": "Sonido apagado", + "start_chat": "Comenzar chat", + "thanks_for_talking_with_us": "Gracias por hablar con nosotros", + "the_controller_of_your_personal_data_is_company_na": "El controlador de sus datos personales es [Company Name], con domicilio en [Company Address]. Para iniciar el chat, acepta que sus datos personales se procesen y transmitan de acuerdo con el Reglamento General de Protección de Datos (GDPR).", + "type_your_message_here": "Escribe tu mensaje aquí", + "unread_messages": "Mensajes no leídos", + "user_added_by": "Usuario agregado por", + "user_joined": "Usuario agregado", + "user_left": "El usuario ha abandonado la sala", + "user_removed_by": "Usuario eliminado por", + "waiting_queue": "Esperando cola...", + "we_are_not_online_right_now_please_leave_a_message": "No estamos en línea en este momento. Por favor, deje un mensaje.", + "welcome": "Bienvenido", + "write_your_message": "Escribe tu mensaje...", + "yes": "Si", + "you_browser_doesn_t_support_audio_element": "Tu navegador no soporta elementos de audio", + "you_browser_doesn_t_support_video_element": "Tu navegador no soporta elementos de video", + "your_spot_is_spot": "Tu lugar es #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "Tu lugar es #{{spot}} (Tiempo de espera estimado: {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/et.json b/packages/livechat/src/i18n/et.json index eb808a79f6db..364e82585912 100644 --- a/packages/livechat/src/i18n/et.json +++ b/packages/livechat/src/i18n/et.json @@ -1,13 +1,13 @@ { - "translation": { - "are_you_sure_you_want_to_switch_the_department": "Oled sa kindel, et soovid osakonda vahetada?", - "cancel": "Tühista", - "conversation_finished": "Vestlus on lõppenud", - "department_switched": "Osakond vahetatud", - "no": "Ei", - "options": "Valikud", - "send": "Saada", - "user_joined": "Kasutaja liitus", - "user_left": "Kasutaja lahkus" - } -} + "translation": { + "are_you_sure_you_want_to_switch_the_department": "Oled sa kindel, et soovid osakonda vahetada?", + "cancel": "Tühista", + "conversation_finished": "Vestlus on lõppenud", + "department_switched": "Osakond vahetatud", + "no": "Ei", + "options": "Valikud", + "send": "Saada", + "user_joined": "Kasutaja liitus", + "user_left": "Kasutaja lahkus" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/eu.json b/packages/livechat/src/i18n/eu.json index 5516b0b65fc6..f4c701fbc6a3 100644 --- a/packages/livechat/src/i18n/eu.json +++ b/packages/livechat/src/i18n/eu.json @@ -1,13 +1,13 @@ { - "translation": { - "are_you_sure_you_want_to_switch_the_department": "Ziur zaude saioa aldatu nahi duzula?", - "cancel": "Ezeztatu", - "conversation_finished": "Elkarrizketa amaitu da", - "no": "Ez", - "options": "Aukerak", - "send": "Bidali", - "user_joined": "Erabiltzailea sartu da", - "user_left": "Erabiltzailea irten da", - "yes": "Bai" - } -} + "translation": { + "are_you_sure_you_want_to_switch_the_department": "Ziur zaude saioa aldatu nahi duzula?", + "cancel": "Ezeztatu", + "conversation_finished": "Elkarrizketa amaitu da", + "no": "Ez", + "options": "Aukerak", + "send": "Bidali", + "user_joined": "Erabiltzailea sartu da", + "user_left": "Erabiltzailea irten da", + "yes": "Bai" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/fa.json b/packages/livechat/src/i18n/fa.json index 91fced7f0b60..283d32401d81 100644 --- a/packages/livechat/src/i18n/fa.json +++ b/packages/livechat/src/i18n/fa.json @@ -1,101 +1,101 @@ { - "translation": { - "accept": "پذیرفتن", - "are_you_sure_you_want_to_finish_this_chat": "آیا شما می خواید به این مکالمه پایان دهید؟", - "are_you_sure_you_want_to_remove_all_of_your_person": "آیا از پاک شدن تمام اطلاعات شخصی خود اطمینان دارید؟", - "are_you_sure_you_want_to_switch_the_department": "ایا مطمئن هستید که میخواهید بخش را عوض کنید؟", - "call_end_time": "تماس در زمان {{time, datetime}} پایان یافت - مدت {{callDuration, datetime}}", - "cancel": "لغو", - "change_department": "تغییر بخش", - "change_department_1": "تغییر بخش", - "chat_finished": "گفتگو پایان یافت", - "chat_now": "گفتگو کنید", - "chat_started": "گفتگو شروع شد", - "choose_a_department": "یک بخش را انتخاب کنید ...", - "choose_a_department_1": "یک بخش را انتخاب کنید", - "choose_an_option": "یک گزینه را انتخاب کنید", - "conversation_finished": "مکالمه به پایان رسید", - "count_new_messages_since_since_one": "یک پیام جدید از {{val, datetime}} قبل", - "count_new_messages_since_since_other": "{{count}} پیام جدید از {{val, datetime}} قبل", - "decline": "رد کردن", - "department_switched": "گروه عوض شد", - "departments": "بخش ها", - "disable_notifications": "غیرفعال کردن اعلان ها", - "dismiss_this_alert": "نادیده گرفتن این هشدار", - "drop_here_to_upload_a_file": "فایل را اینجا رها کنید", - "email": "ایمیل", - "enable_notifications": "فعال کردن اعلان ها", - "error_closing_chat": "خطا در بستن گفتگو.", - "error_getting_call_alert": "خطا در دریافت هشدار تماس.", - "error_removing_user_data": "خطا در حذف اطلاعات کاربر.", - "error_starting_a_new_conversation_reason": "خطا در ایجاد گفتگوی جدید: {{reason}}", - "expand_chat": "گسترش گفتگو", - "field_required": "فیلد اجباری است", - "file_exceeds_allowed_size_of_size": "حجم فایل بیشتر از حد مجاز ({{size}}) است.", - "fileupload_error": "خطا در آپلود فایل", - "finish_this_chat": "پایان این گفتگو", - "forget_remove_my_data": "حذف اطلاعات من", - "from_returned_the_chat_to_the_queue": "{{from}} گفتگو را به صف برگرداند", - "the_agent_transferred_the_chat_to_the_department_to": "پشتیبان چت را به بخش {{to}} منتقل کرد", - "from_transferred_the_chat_to_the_department_to": "{{from}} گفتگو را به بخش {{to}} منتقل کرد", - "from_transferred_the_chat_to_to": "{{from}} گفتگو را به {{to}} منتقل کرد", - "gdpr": "مقررات عمومی حفاظت از داده اتحادیه اروپا (GDPR)", - "go_to_menu_options_forget_remove_my_personal_data": "به <1>menu options → Forget/Remove my personal data بروید تا درخواست حذف بلافاصله ی اطلاعات خود را بدهید", - "hiddenelementscount_more": "+ {{hiddenElementsCount}} مورد دیگر", - "i_agree": "موافقم", - "i_need_help_with": "نیاز به کمک در بخش زیر را دارم", - "if_you_have_any_other_questions_just_press_the_but": "اگر هرگونه سوال دیگری دارید، بر روی دکمه ی زیر کلیک کنید تا یک گفتگو آغاز شود", - "incoming_video_call": "تماس تصویری دریافتی", - "insert_your_field_here": "{{field}} خود را اینجا وارد کنید ...", - "invalid_email": "ایمیل نامعتبر است", - "invalid_value": "مقدار نامعتبر است", - "join_call": "به تماس بپیوندید", - "join_my_room_to_start_the_video_call": "برای شروع تماس تصویری به اتاق من بپیوندید", - "leave_a_message": "یک پیام بگذارید", - "livechat_connected": "Livechat وصل شد.", - "livechat_is_not_connected": "Livechat وصل نشد.", - "media_types_not_accepted": "این نوع فایل قابل قبول نیست.", - "message": "پیام", - "messages": "پیام ها", - "minimize_chat": "کوچک کردن گفتگو", - "name": "نام", - "need_help": "نیاز به کمک دارید?", - "new_chat": "گفتگوی جدید", - "no": "خیر", - "no_available_agents_to_transfer": "هیچ پشتیبانی برای انتقال در دسترس نیست", - "ok": "باشد", - "options": "گزینه ها", - "please_tell_us_some_information_to_start_the_chat": "لطفاً برای شروع گفتگو اطلاعاتی را به ما بدهید", - "please_wait_for_the_next_available_agent": "لطفا تا در دسترس قرار گرفتن پشتیبان متنظر بمانید..", - "powered_by_rocket_chat": "قدرت گرفته از Rocket.Chat", - "restore_chat": "بازگردانی گفتگو", - "room_name_changed": "نام اتاق عوض شد", - "send": "ارسال", - "sound_is_off": "صدا قطع شد", - "sound_is_on": "صدا وصل شد", - "start_chat": "آغاز گفتگو", - "thanks_for_talking_with_us": "متشکر از تماس شما با ما", - "the_chat_was_moved_back_to_queue": "گفتگو به صف برگشت", - "the_chat_was_moved_back_to_queue_due_to_unanswered": "گفتگو از آنجایی که برای {{duration}} ثانیه بی پاسخ بود، به صف برگشت", - "the_chat_was_transferred_to_another_agent": "گفتگو به پشتیبان دیگری منتقل شد", - "the_chat_was_transferred_to_another_agent_due_to_unanswered": "گفتگو از آنجایی که برای {{duration}} ثانیه بی پاسخ بود به پشتیبان دیگری منتقل شد", - "the_controller_of_your_personal_data_is_company_na": "کنترل اطلاعات شما در دست شرکت [Company Name], به آدرس [Company Address] است. برای شروع گفتگو لازم از مقررات عمومی حفاظت از داده اتحادیه اروپا (GDPR) را تایید کنید.", - "transcript_success": "رونوشت ارسال شد", - "type_your_message_here": "پیام خود را اینجا تایپ کنید", - "unread_messages_count_one": "{{count}} پیام خوانده نشده", - "unread_messages": "پیام های خوانده نشده", - "user_added_by": "کاربر اضافه شد، توسط", - "user_joined": "کابر ملحق شد", - "user_left": "کاربر خارج شد", - "user_removed_by": "کاربر حذف شد، توسط", - "waiting_queue": "صف انتظار ...", - "we_are_not_online_right_now_please_leave_a_message": "ما در حال حاضر آنلاین نیستیم. لطفا پیام بگذارید", - "welcome": "خوش آمدید", - "write_your_message": "پیام خود را بنویسید ...", - "yes": "بله", - "you_browser_doesn_t_support_audio_element": "مرورگر شما صوت را پشتیبانی نمی کند", - "you_browser_doesn_t_support_video_element": "مرورگر شما ویدئو را پشتیبانی نمی کند", - "your_spot_is_spot": "نوبت شما #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "نوبت شما #{{spot}} است (مدت زمان تخمین زده شده: {{estimatedWaitTime}})" - } -} + "translation": { + "accept": "پذیرفتن", + "are_you_sure_you_want_to_finish_this_chat": "آیا شما می خواید به این مکالمه پایان دهید؟", + "are_you_sure_you_want_to_remove_all_of_your_person": "آیا از پاک شدن تمام اطلاعات شخصی خود اطمینان دارید؟", + "are_you_sure_you_want_to_switch_the_department": "ایا مطمئن هستید که میخواهید بخش را عوض کنید؟", + "call_end_time": "تماس در زمان {{time, datetime}} پایان یافت - مدت {{callDuration, datetime}}", + "cancel": "لغو", + "change_department": "تغییر بخش", + "change_department_1": "تغییر بخش", + "chat_finished": "گفتگو پایان یافت", + "chat_now": "گفتگو کنید", + "chat_started": "گفتگو شروع شد", + "choose_a_department": "یک بخش را انتخاب کنید ...", + "choose_a_department_1": "یک بخش را انتخاب کنید", + "choose_an_option": "یک گزینه را انتخاب کنید", + "conversation_finished": "مکالمه به پایان رسید", + "count_new_messages_since_since_one": "یک پیام جدید از {{val, datetime}} قبل", + "count_new_messages_since_since_other": "{{count}} پیام جدید از {{val, datetime}} قبل", + "decline": "رد کردن", + "department_switched": "گروه عوض شد", + "departments": "بخش ها", + "disable_notifications": "غیرفعال کردن اعلان ها", + "dismiss_this_alert": "نادیده گرفتن این هشدار", + "drop_here_to_upload_a_file": "فایل را اینجا رها کنید", + "email": "ایمیل", + "enable_notifications": "فعال کردن اعلان ها", + "error_closing_chat": "خطا در بستن گفتگو.", + "error_getting_call_alert": "خطا در دریافت هشدار تماس.", + "error_removing_user_data": "خطا در حذف اطلاعات کاربر.", + "error_starting_a_new_conversation_reason": "خطا در ایجاد گفتگوی جدید: {{reason}}", + "expand_chat": "گسترش گفتگو", + "field_required": "فیلد اجباری است", + "file_exceeds_allowed_size_of_size": "حجم فایل بیشتر از حد مجاز ({{size}}) است.", + "fileupload_error": "خطا در آپلود فایل", + "finish_this_chat": "پایان این گفتگو", + "forget_remove_my_data": "حذف اطلاعات من", + "from_returned_the_chat_to_the_queue": "{{from}} گفتگو را به صف برگرداند", + "the_agent_transferred_the_chat_to_the_department_to": "پشتیبان چت را به بخش {{to}} منتقل کرد", + "from_transferred_the_chat_to_the_department_to": "{{from}} گفتگو را به بخش {{to}} منتقل کرد", + "from_transferred_the_chat_to_to": "{{from}} گفتگو را به {{to}} منتقل کرد", + "gdpr": "مقررات عمومی حفاظت از داده اتحادیه اروپا (GDPR)", + "go_to_menu_options_forget_remove_my_personal_data": "به <1>menu options → Forget/Remove my personal data بروید تا درخواست حذف بلافاصله ی اطلاعات خود را بدهید", + "hiddenelementscount_more": "+ {{hiddenElementsCount}} مورد دیگر", + "i_agree": "موافقم", + "i_need_help_with": "نیاز به کمک در بخش زیر را دارم", + "if_you_have_any_other_questions_just_press_the_but": "اگر هرگونه سوال دیگری دارید، بر روی دکمه ی زیر کلیک کنید تا یک گفتگو آغاز شود", + "incoming_video_call": "تماس تصویری دریافتی", + "insert_your_field_here": "{{field}} خود را اینجا وارد کنید ...", + "invalid_email": "ایمیل نامعتبر است", + "invalid_value": "مقدار نامعتبر است", + "join_call": "به تماس بپیوندید", + "join_my_room_to_start_the_video_call": "برای شروع تماس تصویری به اتاق من بپیوندید", + "leave_a_message": "یک پیام بگذارید", + "livechat_connected": "Livechat وصل شد.", + "livechat_is_not_connected": "Livechat وصل نشد.", + "media_types_not_accepted": "این نوع فایل قابل قبول نیست.", + "message": "پیام", + "messages": "پیام ها", + "minimize_chat": "کوچک کردن گفتگو", + "name": "نام", + "need_help": "نیاز به کمک دارید?", + "new_chat": "گفتگوی جدید", + "no": "خیر", + "no_available_agents_to_transfer": "هیچ پشتیبانی برای انتقال در دسترس نیست", + "ok": "باشد", + "options": "گزینه ها", + "please_tell_us_some_information_to_start_the_chat": "لطفاً برای شروع گفتگو اطلاعاتی را به ما بدهید", + "please_wait_for_the_next_available_agent": "لطفا تا در دسترس قرار گرفتن پشتیبان متنظر بمانید..", + "powered_by_rocket_chat": "قدرت گرفته از Rocket.Chat", + "restore_chat": "بازگردانی گفتگو", + "room_name_changed": "نام اتاق عوض شد", + "send": "ارسال", + "sound_is_off": "صدا قطع شد", + "sound_is_on": "صدا وصل شد", + "start_chat": "آغاز گفتگو", + "thanks_for_talking_with_us": "متشکر از تماس شما با ما", + "the_chat_was_moved_back_to_queue": "گفتگو به صف برگشت", + "the_chat_was_moved_back_to_queue_due_to_unanswered": "گفتگو از آنجایی که برای {{duration}} ثانیه بی پاسخ بود، به صف برگشت", + "the_chat_was_transferred_to_another_agent": "گفتگو به پشتیبان دیگری منتقل شد", + "the_chat_was_transferred_to_another_agent_due_to_unanswered": "گفتگو از آنجایی که برای {{duration}} ثانیه بی پاسخ بود به پشتیبان دیگری منتقل شد", + "the_controller_of_your_personal_data_is_company_na": "کنترل اطلاعات شما در دست شرکت [Company Name], به آدرس [Company Address] است. برای شروع گفتگو لازم از مقررات عمومی حفاظت از داده اتحادیه اروپا (GDPR) را تایید کنید.", + "transcript_success": "رونوشت ارسال شد", + "type_your_message_here": "پیام خود را اینجا تایپ کنید", + "unread_messages_count_one": "{{count}} پیام خوانده نشده", + "unread_messages": "پیام های خوانده نشده", + "user_added_by": "کاربر اضافه شد، توسط", + "user_joined": "کابر ملحق شد", + "user_left": "کاربر خارج شد", + "user_removed_by": "کاربر حذف شد، توسط", + "waiting_queue": "صف انتظار ...", + "we_are_not_online_right_now_please_leave_a_message": "ما در حال حاضر آنلاین نیستیم. لطفا پیام بگذارید", + "welcome": "خوش آمدید", + "write_your_message": "پیام خود را بنویسید ...", + "yes": "بله", + "you_browser_doesn_t_support_audio_element": "مرورگر شما صوت را پشتیبانی نمی کند", + "you_browser_doesn_t_support_video_element": "مرورگر شما ویدئو را پشتیبانی نمی کند", + "your_spot_is_spot": "نوبت شما #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "نوبت شما #{{spot}} است (مدت زمان تخمین زده شده: {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/fi.json b/packages/livechat/src/i18n/fi.json index 002d5f5caa80..a909b0c69495 100644 --- a/packages/livechat/src/i18n/fi.json +++ b/packages/livechat/src/i18n/fi.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Peruuta", - "conversation_finished": "Keskustelu päättynyt", - "department_switched": "Osasto vaihdettu", - "no": "Ei", - "options": "Valinnat", - "send": "Lähetä", - "user_joined": "Käyttäjä liittyi", - "user_left": "Käyttäjä poistui", - "we_are_not_online_right_now_please_leave_a_message": "Emme ole verkossa juuri nyt. Jätä viesti.", - "yes": "Kyllä" - } -} + "translation": { + "cancel": "Peruuta", + "conversation_finished": "Keskustelu päättynyt", + "department_switched": "Osasto vaihdettu", + "no": "Ei", + "options": "Valinnat", + "send": "Lähetä", + "user_joined": "Käyttäjä liittyi", + "user_left": "Käyttäjä poistui", + "we_are_not_online_right_now_please_leave_a_message": "Emme ole verkossa juuri nyt. Jätä viesti.", + "yes": "Kyllä" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/fr.json b/packages/livechat/src/i18n/fr.json index 35a0555e0036..9d40b5eaf747 100644 --- a/packages/livechat/src/i18n/fr.json +++ b/packages/livechat/src/i18n/fr.json @@ -1,77 +1,77 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "Voulez-vous vraiment terminer cette conversation ?", - "are_you_sure_you_want_to_remove_all_of_your_person": "Voulez-vous vraiment supprimer toutes vos données personnelles ?", - "are_you_sure_you_want_to_switch_the_department": "Êtes-vous sûr de vouloir changer de département ?", - "cancel": "Annuler", - "change_department": "Changer de département", - "change_department_1": "Changer Département", - "chat_finished": "Chat Terminé", - "chat_started": "Chat démarré", - "choose_a_department": "Choisir un département...", - "choose_a_department_1": "Choisir un département", - "choose_an_option": "Choisir une option...", - "conversation_finished": "Conversation terminée", - "count_new_messages_since_since_one": "Un nouveau message depuis {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} nouveaux messages depuis {{val, datetime}}", - "department_switched": "Changement de département effectué", - "departments": "Départements", - "disable_notifications": "Désactiver les notifications", - "dismiss_this_alert": "Effacer cette alerte", - "drop_here_to_upload_a_file": "Déposer ici pour envoyer un fichier", - "enable_notifications": "Activer les notifications", - "error_closing_chat": "Erreur lors de la fermeture de la conversation.", - "error_removing_user_data": "Erreur lors de la suppression des données utilisateur.", - "error_starting_a_new_conversation_reason": "Erreur lors de la création d'une nouvelle conversation : {{reason}}", - "expand_chat": "Agrandir le chat", - "field_required": "Champ requis", - "file_exceeds_allowed_size_of_size": "Le fichier dépasse la taille autorisée de {{size}}.", - "fileupload_error": "Erreur d'envoi", - "finish_this_chat": "Terminer cette conversation", - "forget_remove_my_data": "Oublier/Supprimer mes données personnelles", - "gdpr": "RGPD", - "go_to_menu_options_forget_remove_my_personal_data": "Ouvrir <1>menu options → Oublier/Supprimer mes données personnelles pour demander la suppression immédiate de vos données personnelles.", - "i_agree": "J'accepte", - "i_need_help_with": "J'ai besoin d'aide pour...", - "if_you_have_any_other_questions_just_press_the_but": "Si vous avez une autre question, cliquez sur le bouton ci-dessous pour démarrer une nouvelle conversation.", - "insert_your_field_here": "Entrez votre {{field}} ici...", - "invalid_email": "Email invalide", - "invalid_value": "Valeur invalide", - "leave_a_message": "Laissez un message", - "livechat_connected": "Livechat connecté.", - "livechat_is_not_connected": "Le livechat n'est pas connecté.", - "media_types_not_accepted": "Type de fichier non autorisé.", - "minimize_chat": "Réduire le chat", - "name": "Nom", - "need_help": "Besoin d'aide ?", - "new_chat": "Nouvelle Conversation", - "no": "Non", - "no_available_agents_to_transfer": "Aucun agent disponible pour un transfert", - "please_tell_us_some_information_to_start_the_chat": "Veuillez nous donner quelques informations pour démarrer la conversation", - "please_wait_for_the_next_available_agent": "Veuillez patienter, attente d'un agent disponible...", - "powered_by_rocket_chat": "Propulsé par Rocket.Chat", - "restore_chat": "Restaurer conversation", - "room_name_changed": "Nom du salon modifié", - "send": "Envoyer", - "sound_is_off": "Le son est désactivé", - "sound_is_on": "Le son est activé", - "start_chat": "Démarrer le chat", - "thanks_for_talking_with_us": "Merci d'avoir conversé avec nous", - "the_controller_of_your_personal_data_is_company_na": "L'opérateur de vos données personnelles est [Company Name], dont l'adresse est [Company Address]. Pour débuter le chat vous acceptez que vos données personnelles soient utilisées et transmises conformément au règlement général sur la protection des données (RGPD).", - "type_your_message_here": "Tapez votre message ici", - "unread_messages": "messages non lus", - "user_added_by": "Utilisateur ajouté par", - "user_joined": "L'utilisateur a rejoint", - "user_left": "L'utilisateur est parti", - "user_removed_by": "Utilisateur supprimé par", - "waiting_queue": "File d'attente...", - "we_are_not_online_right_now_please_leave_a_message": "Nous ne sommes pas en ligne en ce moment. Merci de laisser un message.", - "welcome": "Bienvenue", - "write_your_message": "Ecrivez votre message...", - "yes": "Oui", - "you_browser_doesn_t_support_audio_element": "Votre navigateur ne supporte pas les éléments audio", - "you_browser_doesn_t_support_video_element": "Votre navigateur ne supporte pas les éléments vidéo", - "your_spot_is_spot": "Votre rang est #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "Votre rang est #{{spot}} (Temps d'attente estimé: {{estimatedWaitTime}})" - } -} + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "Voulez-vous vraiment terminer cette conversation ?", + "are_you_sure_you_want_to_remove_all_of_your_person": "Voulez-vous vraiment supprimer toutes vos données personnelles ?", + "are_you_sure_you_want_to_switch_the_department": "Êtes-vous sûr de vouloir changer de département ?", + "cancel": "Annuler", + "change_department": "Changer de département", + "change_department_1": "Changer Département", + "chat_finished": "Chat Terminé", + "chat_started": "Chat démarré", + "choose_a_department": "Choisir un département...", + "choose_a_department_1": "Choisir un département", + "choose_an_option": "Choisir une option...", + "conversation_finished": "Conversation terminée", + "count_new_messages_since_since_one": "Un nouveau message depuis {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} nouveaux messages depuis {{val, datetime}}", + "department_switched": "Changement de département effectué", + "departments": "Départements", + "disable_notifications": "Désactiver les notifications", + "dismiss_this_alert": "Effacer cette alerte", + "drop_here_to_upload_a_file": "Déposer ici pour envoyer un fichier", + "enable_notifications": "Activer les notifications", + "error_closing_chat": "Erreur lors de la fermeture de la conversation.", + "error_removing_user_data": "Erreur lors de la suppression des données utilisateur.", + "error_starting_a_new_conversation_reason": "Erreur lors de la création d'une nouvelle conversation : {{reason}}", + "expand_chat": "Agrandir le chat", + "field_required": "Champ requis", + "file_exceeds_allowed_size_of_size": "Le fichier dépasse la taille autorisée de {{size}}.", + "fileupload_error": "Erreur d'envoi", + "finish_this_chat": "Terminer cette conversation", + "forget_remove_my_data": "Oublier/Supprimer mes données personnelles", + "gdpr": "RGPD", + "go_to_menu_options_forget_remove_my_personal_data": "Ouvrir <1>menu options → Oublier/Supprimer mes données personnelles pour demander la suppression immédiate de vos données personnelles.", + "i_agree": "J'accepte", + "i_need_help_with": "J'ai besoin d'aide pour...", + "if_you_have_any_other_questions_just_press_the_but": "Si vous avez une autre question, cliquez sur le bouton ci-dessous pour démarrer une nouvelle conversation.", + "insert_your_field_here": "Entrez votre {{field}} ici...", + "invalid_email": "Email invalide", + "invalid_value": "Valeur invalide", + "leave_a_message": "Laissez un message", + "livechat_connected": "Livechat connecté.", + "livechat_is_not_connected": "Le livechat n'est pas connecté.", + "media_types_not_accepted": "Type de fichier non autorisé.", + "minimize_chat": "Réduire le chat", + "name": "Nom", + "need_help": "Besoin d'aide ?", + "new_chat": "Nouvelle Conversation", + "no": "Non", + "no_available_agents_to_transfer": "Aucun agent disponible pour un transfert", + "please_tell_us_some_information_to_start_the_chat": "Veuillez nous donner quelques informations pour démarrer la conversation", + "please_wait_for_the_next_available_agent": "Veuillez patienter, attente d'un agent disponible...", + "powered_by_rocket_chat": "Propulsé par Rocket.Chat", + "restore_chat": "Restaurer conversation", + "room_name_changed": "Nom du salon modifié", + "send": "Envoyer", + "sound_is_off": "Le son est désactivé", + "sound_is_on": "Le son est activé", + "start_chat": "Démarrer le chat", + "thanks_for_talking_with_us": "Merci d'avoir conversé avec nous", + "the_controller_of_your_personal_data_is_company_na": "L'opérateur de vos données personnelles est [Company Name], dont l'adresse est [Company Address]. Pour débuter le chat vous acceptez que vos données personnelles soient utilisées et transmises conformément au règlement général sur la protection des données (RGPD).", + "type_your_message_here": "Tapez votre message ici", + "unread_messages": "messages non lus", + "user_added_by": "Utilisateur ajouté par", + "user_joined": "L'utilisateur a rejoint", + "user_left": "L'utilisateur est parti", + "user_removed_by": "Utilisateur supprimé par", + "waiting_queue": "File d'attente...", + "we_are_not_online_right_now_please_leave_a_message": "Nous ne sommes pas en ligne en ce moment. Merci de laisser un message.", + "welcome": "Bienvenue", + "write_your_message": "Ecrivez votre message...", + "yes": "Oui", + "you_browser_doesn_t_support_audio_element": "Votre navigateur ne supporte pas les éléments audio", + "you_browser_doesn_t_support_video_element": "Votre navigateur ne supporte pas les éléments vidéo", + "your_spot_is_spot": "Votre rang est #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "Votre rang est #{{spot}} (Temps d'attente estimé: {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/he.json b/packages/livechat/src/i18n/he.json index 60c6707a9260..2b71feb4b1ad 100644 --- a/packages/livechat/src/i18n/he.json +++ b/packages/livechat/src/i18n/he.json @@ -1,68 +1,68 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "אתה בטוח שברצונך לסיים את השיחה?", - "are_you_sure_you_want_to_remove_all_of_your_person": "אתה בטוח שברצונך להסיר את כל המידע האישי?", - "are_you_sure_you_want_to_switch_the_department": "האם אתה בטוח שברצונך להחליף מחלקה?", - "cancel": "ביטול", - "change_department": "החלף מחלקה", - "change_department_1": "החלף מחלקה", - "chat_finished": "הצ'אט הסתיים", - "choose_a_department": "בחר מחלקה...", - "choose_a_department_1": "בחר מחלקה", - "choose_an_option": "בחר אפשרות...", - "conversation_finished": "נגמרה השיחה", - "count_new_messages_since_since_one": "הודעה אחת חדשה מאז {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} הודעות חדשות מאז {{val, datetime}}", - "department_switched": "החלף מחלקה", - "departments": "מחלקות", - "disable_notifications": "בטל התראות", - "dismiss_this_alert": "הסר אזהרה", - "drop_here_to_upload_a_file": "זרוק כאן את הקובץ", - "enable_notifications": "הפעל התראות", - "error_closing_chat": "שגיאה בסגירת הצ'אט.", - "error_removing_user_data": "שגיאה בהסרת המידע.", - "error_starting_a_new_conversation_reason": "לא הצלחנו ליצור שיחה חדשה: {{reason}}", - "expand_chat": "הרחב צ'אט", - "file_exceeds_allowed_size_of_size": "הקובץ חורג מגודל {{size}}.", - "fileupload_error": "שגיאה בהעלאת הקובץ", - "finish_this_chat": "סיים צ'אט זה", - "i_agree": "אני מסכים", - "i_need_help_with": "אני צריך עזרה עם...", - "if_you_have_any_other_questions_just_press_the_but": "אם יש לך שאלות כלשהם, לחץ על הכפתור למטה בכדי להתחיל צ'אט.", - "leave_a_message": "השאר הודעה", - "livechat_connected": "צ'אט לייב מחובר.", - "livechat_is_not_connected": "צ'אט לייב לא מחובר.", - "media_types_not_accepted": "סוג המדיה אינו מאושר.", - "message": "הודעה", - "minimize_chat": "מזער צ'אט", - "name": "שם", - "need_help": "צריך עזרה?", - "new_chat": "צ'אט חדש", - "no": "לא", - "no_available_agents_to_transfer": "אין סוכנים פנויים בכדי להעביר", - "ok": "אוקיי", - "options": "אפשרויות", - "please_tell_us_some_information_to_start_the_chat": "אנא שלחו לנו מידע בכדי להתחיל בצ'אט.", - "please_wait_for_the_next_available_agent": "אנא המתן לסוכן הבא הפנוי.", - "restore_chat": "שחזר צ'אט", - "room_name_changed": "שם החדר השתנה", - "send": "שליחה", - "sound_is_off": "סאונד מושתק", - "sound_is_on": "סאונד דולק", - "start_chat": "התחל צ'אט", - "thanks_for_talking_with_us": "תודה שדיברתם איתנו", - "type_your_message_here": "כתוב את ההודעה כאן", - "unread_messages": "הודעות שלא נקראו", - "user_joined": "המשתמש הצטרף", - "user_left": "המשתמש עזב", - "waiting_queue": "ממתין בתור...", - "we_are_not_online_right_now_please_leave_a_message": "אנחנו לא מחוברים כרגע, אנא השאירו הודעה", - "welcome": "ברוכים הבאים", - "write_your_message": "כתוב את ההודעה שלך", - "yes": "כן", - "you_browser_doesn_t_support_audio_element": "הדפדפן שלך לא תומך בסאונד", - "you_browser_doesn_t_support_video_element": "הדפדפן שלך לא תומך בוידאו", - "your_spot_is_spot": "מיקומך הוא #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "מיקומך הוא #{{spot}} זמן מוערך {{estimatedWaitTime}})" - } -} + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "אתה בטוח שברצונך לסיים את השיחה?", + "are_you_sure_you_want_to_remove_all_of_your_person": "אתה בטוח שברצונך להסיר את כל המידע האישי?", + "are_you_sure_you_want_to_switch_the_department": "האם אתה בטוח שברצונך להחליף מחלקה?", + "cancel": "ביטול", + "change_department": "החלף מחלקה", + "change_department_1": "החלף מחלקה", + "chat_finished": "הצ'אט הסתיים", + "choose_a_department": "בחר מחלקה...", + "choose_a_department_1": "בחר מחלקה", + "choose_an_option": "בחר אפשרות...", + "conversation_finished": "נגמרה השיחה", + "count_new_messages_since_since_one": "הודעה אחת חדשה מאז {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} הודעות חדשות מאז {{val, datetime}}", + "department_switched": "החלף מחלקה", + "departments": "מחלקות", + "disable_notifications": "בטל התראות", + "dismiss_this_alert": "הסר אזהרה", + "drop_here_to_upload_a_file": "זרוק כאן את הקובץ", + "enable_notifications": "הפעל התראות", + "error_closing_chat": "שגיאה בסגירת הצ'אט.", + "error_removing_user_data": "שגיאה בהסרת המידע.", + "error_starting_a_new_conversation_reason": "לא הצלחנו ליצור שיחה חדשה: {{reason}}", + "expand_chat": "הרחב צ'אט", + "file_exceeds_allowed_size_of_size": "הקובץ חורג מגודל {{size}}.", + "fileupload_error": "שגיאה בהעלאת הקובץ", + "finish_this_chat": "סיים צ'אט זה", + "i_agree": "אני מסכים", + "i_need_help_with": "אני צריך עזרה עם...", + "if_you_have_any_other_questions_just_press_the_but": "אם יש לך שאלות כלשהם, לחץ על הכפתור למטה בכדי להתחיל צ'אט.", + "leave_a_message": "השאר הודעה", + "livechat_connected": "צ'אט לייב מחובר.", + "livechat_is_not_connected": "צ'אט לייב לא מחובר.", + "media_types_not_accepted": "סוג המדיה אינו מאושר.", + "message": "הודעה", + "minimize_chat": "מזער צ'אט", + "name": "שם", + "need_help": "צריך עזרה?", + "new_chat": "צ'אט חדש", + "no": "לא", + "no_available_agents_to_transfer": "אין סוכנים פנויים בכדי להעביר", + "ok": "אוקיי", + "options": "אפשרויות", + "please_tell_us_some_information_to_start_the_chat": "אנא שלחו לנו מידע בכדי להתחיל בצ'אט.", + "please_wait_for_the_next_available_agent": "אנא המתן לסוכן הבא הפנוי.", + "restore_chat": "שחזר צ'אט", + "room_name_changed": "שם החדר השתנה", + "send": "שליחה", + "sound_is_off": "סאונד מושתק", + "sound_is_on": "סאונד דולק", + "start_chat": "התחל צ'אט", + "thanks_for_talking_with_us": "תודה שדיברתם איתנו", + "type_your_message_here": "כתוב את ההודעה כאן", + "unread_messages": "הודעות שלא נקראו", + "user_joined": "המשתמש הצטרף", + "user_left": "המשתמש עזב", + "waiting_queue": "ממתין בתור...", + "we_are_not_online_right_now_please_leave_a_message": "אנחנו לא מחוברים כרגע, אנא השאירו הודעה", + "welcome": "ברוכים הבאים", + "write_your_message": "כתוב את ההודעה שלך", + "yes": "כן", + "you_browser_doesn_t_support_audio_element": "הדפדפן שלך לא תומך בסאונד", + "you_browser_doesn_t_support_video_element": "הדפדפן שלך לא תומך בוידאו", + "your_spot_is_spot": "מיקומך הוא #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "מיקומך הוא #{{spot}} זמן מוערך {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/hr.json b/packages/livechat/src/i18n/hr.json index ff11d1a4c5c2..e37b4d78e4d3 100644 --- a/packages/livechat/src/i18n/hr.json +++ b/packages/livechat/src/i18n/hr.json @@ -1,15 +1,15 @@ { - "translation": { - "are_you_sure_you_want_to_switch_the_department": "Jeste li sigurni da želite prebaciti odjel?", - "cancel": "Otkaži", - "conversation_finished": "Razgovor je završio", - "department_switched": "Odjel je prebacio", - "no": "Ne", - "options": "Opcije", - "send": "Pošalji", - "user_joined": "Korisnik se pridružio", - "user_left": "Korisnik napustio razgovor", - "we_are_not_online_right_now_please_leave_a_message": "Trenutačno nismo na mreži. Molim vas ostavite poruku.", - "yes": "Da" - } -} + "translation": { + "are_you_sure_you_want_to_switch_the_department": "Jeste li sigurni da želite prebaciti odjel?", + "cancel": "Otkaži", + "conversation_finished": "Razgovor je završio", + "department_switched": "Odjel je prebacio", + "no": "Ne", + "options": "Opcije", + "send": "Pošalji", + "user_joined": "Korisnik se pridružio", + "user_left": "Korisnik napustio razgovor", + "we_are_not_online_right_now_please_leave_a_message": "Trenutačno nismo na mreži. Molim vas ostavite poruku.", + "yes": "Da" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/hu.json b/packages/livechat/src/i18n/hu.json index f2a3adf52337..7a97b669f6f8 100644 --- a/packages/livechat/src/i18n/hu.json +++ b/packages/livechat/src/i18n/hu.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Mégse", - "conversation_finished": "Beszélgetés befejeződött", - "department_switched": "Részleg lecserélve", - "no": "Nem", - "options": "Beállítások", - "send": "Elküld", - "user_joined": "Felhasználó belépett", - "user_left": "Felhasználó kilépett", - "we_are_not_online_right_now_please_leave_a_message": "Jelenleg nem vagyunk online. Kérlek hagyj üzenetet.", - "yes": "Igen" - } -} + "translation": { + "cancel": "Mégse", + "conversation_finished": "Beszélgetés befejeződött", + "department_switched": "Részleg lecserélve", + "no": "Nem", + "options": "Beállítások", + "send": "Elküld", + "user_joined": "Felhasználó belépett", + "user_left": "Felhasználó kilépett", + "we_are_not_online_right_now_please_leave_a_message": "Jelenleg nem vagyunk online. Kérlek hagyj üzenetet.", + "yes": "Igen" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/id.json b/packages/livechat/src/i18n/id.json index 26edcbd57dc0..bc5705bf97f0 100644 --- a/packages/livechat/src/i18n/id.json +++ b/packages/livechat/src/i18n/id.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Membatalkan", - "conversation_finished": "percakapan selesai", - "department_switched": "Departemen beralih", - "no": "Tidak", - "options": "Pilihan", - "send": "Kirim", - "user_joined": "pengguna bergabung", - "user_left": "kiri pengguna", - "we_are_not_online_right_now_please_leave_a_message": "Kami sedang tidak online sekarang. Tolong, tinggalkan pesan.", - "yes": "iya nih" - } -} + "translation": { + "cancel": "Membatalkan", + "conversation_finished": "percakapan selesai", + "department_switched": "Departemen beralih", + "no": "Tidak", + "options": "Pilihan", + "send": "Kirim", + "user_joined": "pengguna bergabung", + "user_left": "kiri pengguna", + "we_are_not_online_right_now_please_leave_a_message": "Kami sedang tidak online sekarang. Tolong, tinggalkan pesan.", + "yes": "iya nih" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/it.json b/packages/livechat/src/i18n/it.json index aac08d2bfa9c..a85e64481b78 100644 --- a/packages/livechat/src/i18n/it.json +++ b/packages/livechat/src/i18n/it.json @@ -1,71 +1,71 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "Sei sicuro di voler terminare questa chat?", - "are_you_sure_you_want_to_remove_all_of_your_person": "Sei sicuro di voler rimuovere tutti i tuoi dati personali?", - "are_you_sure_you_want_to_switch_the_department": "Sei sicuro di voler cambiare dipartimento?", - "cancel": "Annulla", - "change_department": "Cambia dipartimento", - "change_department_1": "Cambia Dipartimento", - "chat_finished": "Chat Conclusa", - "choose_a_department": "Scegli un dipartimento...", - "choose_a_department_1": "Scegli un dipartimento", - "choose_an_option": "Scegli un opzione...", - "conversation_finished": "Conversazione terminata", - "count_new_messages_since_since_one": "Un nuovo messaggio da {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} nuovi messaggi da {{val, datetime}}", - "department_switched": "Dipartimento cambiato", - "departments": "Dipartimenti", - "disable_notifications": "Disabilita notifiche", - "dismiss_this_alert": "Rimuovi questo avviso", - "drop_here_to_upload_a_file": "Rilascia qui per caricare un file", - "enable_notifications": "Abilita notifiche", - "error_closing_chat": "Errore durante la chiusura della chat.", - "error_removing_user_data": "Errore durante la rimozione dei dati personali.", - "error_starting_a_new_conversation_reason": "Errore durante la creazione di una nuova conversazione: {{reason}}", - "expand_chat": "Espandi chat", - "file_exceeds_allowed_size_of_size": "Il file supera la dimensione massima consentita di {{size}}.", - "fileupload_error": "FileUpload Errore", - "finish_this_chat": "Termina questa chat", - "forget_remove_my_data": "Dimentica/Rimuovi i miei dati", - "go_to_menu_options_forget_remove_my_personal_data": "Vai su <1>menu opzioni → Dimentica/Rimuovi i miei dati personali per richiedere l'immediata cancellazione dei tuoi dati.", - "i_agree": "Sono Daccordo", - "i_need_help_with": "Ho bisogno di aiuto con...", - "if_you_have_any_other_questions_just_press_the_but": "Se hai altre domande ti basta premere il tasto sotto per iniziare una nuova chat.", - "leave_a_message": "Lascia un messaggio", - "livechat_connected": "Livechat connesso.", - "livechat_is_not_connected": "Livechat non è connesso.", - "media_types_not_accepted": "Formato Media Non Accettato.", - "message": "Messaggio", - "minimize_chat": "Rimpicciolisci chat", - "name": "Nome", - "need_help": "Hai bisogno di aiuto?", - "new_chat": "Nuova Chat", - "no_available_agents_to_transfer": "Nessun agente disponibile per il trasferimento", - "options": "Opzioni", - "please_tell_us_some_information_to_start_the_chat": "Per favore, forniscici alcune informazioni per cominciare la chat", - "please_wait_for_the_next_available_agent": "Per favore, attendi il prossimo agente disponibile..", - "restore_chat": "Ripristina chat", - "room_name_changed": "Nome della stanza cambiato", - "send": "Invia", - "sound_is_off": "Il suono è spento", - "sound_is_on": "Il suono è acceso", - "start_chat": "Comincia la chat", - "thanks_for_talking_with_us": "Grazie per aver parlato con noi", - "the_controller_of_your_personal_data_is_company_na": "Il titolare del trattamento dei tuoi dati personali è [Nome Compagnia], con sede in [Indirizzo Compagnia]. Per cominciare la chat è necessario che tu accetta che i tuoi dati personali vengano processati e/o trasmessi in accordo con il General Data Protection Regulation (GDPR).", - "type_your_message_here": "Scrivi il tuo messaggio qui", - "unread_messages": "messaggi non letti", - "user_added_by": "Utente aggiunto da", - "user_joined": "L'utente si è iscritto", - "user_left": "L'utente ha abbandonato", - "user_removed_by": "Utente rimosso da", - "waiting_queue": "Coda di attesa...", - "we_are_not_online_right_now_please_leave_a_message": "Non siamo online al momento. Lasciare un messaggio.", - "welcome": "Benvenuto", - "write_your_message": "Scrivi il tuo messaggio...", - "yes": "Sì", - "you_browser_doesn_t_support_audio_element": "Il tuo browser non supporta l'elemento audio", - "you_browser_doesn_t_support_video_element": "Il tuo browser non supporta l'elemento video", - "your_spot_is_spot": "La tua posizione è #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "La tua posizione è #{{spot}} (Tempo di attesa stimato: {{estimatedWaitTime}})" - } -} + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "Sei sicuro di voler terminare questa chat?", + "are_you_sure_you_want_to_remove_all_of_your_person": "Sei sicuro di voler rimuovere tutti i tuoi dati personali?", + "are_you_sure_you_want_to_switch_the_department": "Sei sicuro di voler cambiare dipartimento?", + "cancel": "Annulla", + "change_department": "Cambia dipartimento", + "change_department_1": "Cambia Dipartimento", + "chat_finished": "Chat Conclusa", + "choose_a_department": "Scegli un dipartimento...", + "choose_a_department_1": "Scegli un dipartimento", + "choose_an_option": "Scegli un opzione...", + "conversation_finished": "Conversazione terminata", + "count_new_messages_since_since_one": "Un nuovo messaggio da {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} nuovi messaggi da {{val, datetime}}", + "department_switched": "Dipartimento cambiato", + "departments": "Dipartimenti", + "disable_notifications": "Disabilita notifiche", + "dismiss_this_alert": "Rimuovi questo avviso", + "drop_here_to_upload_a_file": "Rilascia qui per caricare un file", + "enable_notifications": "Abilita notifiche", + "error_closing_chat": "Errore durante la chiusura della chat.", + "error_removing_user_data": "Errore durante la rimozione dei dati personali.", + "error_starting_a_new_conversation_reason": "Errore durante la creazione di una nuova conversazione: {{reason}}", + "expand_chat": "Espandi chat", + "file_exceeds_allowed_size_of_size": "Il file supera la dimensione massima consentita di {{size}}.", + "fileupload_error": "FileUpload Errore", + "finish_this_chat": "Termina questa chat", + "forget_remove_my_data": "Dimentica/Rimuovi i miei dati", + "go_to_menu_options_forget_remove_my_personal_data": "Vai su <1>menu opzioni → Dimentica/Rimuovi i miei dati personali per richiedere l'immediata cancellazione dei tuoi dati.", + "i_agree": "Sono Daccordo", + "i_need_help_with": "Ho bisogno di aiuto con...", + "if_you_have_any_other_questions_just_press_the_but": "Se hai altre domande ti basta premere il tasto sotto per iniziare una nuova chat.", + "leave_a_message": "Lascia un messaggio", + "livechat_connected": "Livechat connesso.", + "livechat_is_not_connected": "Livechat non è connesso.", + "media_types_not_accepted": "Formato Media Non Accettato.", + "message": "Messaggio", + "minimize_chat": "Rimpicciolisci chat", + "name": "Nome", + "need_help": "Hai bisogno di aiuto?", + "new_chat": "Nuova Chat", + "no_available_agents_to_transfer": "Nessun agente disponibile per il trasferimento", + "options": "Opzioni", + "please_tell_us_some_information_to_start_the_chat": "Per favore, forniscici alcune informazioni per cominciare la chat", + "please_wait_for_the_next_available_agent": "Per favore, attendi il prossimo agente disponibile..", + "restore_chat": "Ripristina chat", + "room_name_changed": "Nome della stanza cambiato", + "send": "Invia", + "sound_is_off": "Il suono è spento", + "sound_is_on": "Il suono è acceso", + "start_chat": "Comincia la chat", + "thanks_for_talking_with_us": "Grazie per aver parlato con noi", + "the_controller_of_your_personal_data_is_company_na": "Il titolare del trattamento dei tuoi dati personali è [Nome Compagnia], con sede in [Indirizzo Compagnia]. Per cominciare la chat è necessario che tu accetta che i tuoi dati personali vengano processati e/o trasmessi in accordo con il General Data Protection Regulation (GDPR).", + "type_your_message_here": "Scrivi il tuo messaggio qui", + "unread_messages": "messaggi non letti", + "user_added_by": "Utente aggiunto da", + "user_joined": "L'utente si è iscritto", + "user_left": "L'utente ha abbandonato", + "user_removed_by": "Utente rimosso da", + "waiting_queue": "Coda di attesa...", + "we_are_not_online_right_now_please_leave_a_message": "Non siamo online al momento. Lasciare un messaggio.", + "welcome": "Benvenuto", + "write_your_message": "Scrivi il tuo messaggio...", + "yes": "Sì", + "you_browser_doesn_t_support_audio_element": "Il tuo browser non supporta l'elemento audio", + "you_browser_doesn_t_support_video_element": "Il tuo browser non supporta l'elemento video", + "your_spot_is_spot": "La tua posizione è #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "La tua posizione è #{{spot}} (Tempo di attesa stimato: {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/ja.json b/packages/livechat/src/i18n/ja.json index c10bbc48f499..4bc3467f29c2 100644 --- a/packages/livechat/src/i18n/ja.json +++ b/packages/livechat/src/i18n/ja.json @@ -1,78 +1,78 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "このチャットを終了してもよろしいですか?", - "are_you_sure_you_want_to_remove_all_of_your_person": "個人データをすべて削除してもよろしいですか?", - "are_you_sure_you_want_to_switch_the_department": "部署を切り替えてもよろしいですか?", - "cancel": "キャンセル", - "change_department": "部署を変更", - "change_department_1": "部署を変更", - "chat_finished": "チャットが終了しました", - "choose_a_department": "選択してください", - "choose_a_department_1": "担当部署を選択してください", - "choose_an_option": "選択してください", - "conversation_finished": "会話が終了しました", - "count_new_messages_since_since_one": "{{val, datetime}}以降の1件の新着メッセージ", - "count_new_messages_since_since_other": "{{val, datetime}}以降の{{count}}件の新着メッセージ", - "department_switched": "部署切り替え", - "departments": "担当部署", - "disable_notifications": "通知を無効にする", - "dismiss_this_alert": "アラートを閉じる", - "drop_here_to_upload_a_file": "ここにドロップしてファイルをアップロードします", - "email": "メールアドレス", - "enable_notifications": "通知を有効にする", - "error_closing_chat": "チャット終了時にエラーが発生しました。", - "error_removing_user_data": "データ削除中にエラーが発生しました。", - "error_starting_a_new_conversation_reason": "チャット開始時にエラーが発生しました: {{reason}}", - "expand_chat": "チャットを最大化", - "field_required": "必須項目", - "file_exceeds_allowed_size_of_size": "ファイルの許容サイズ ({{size}}) を超えています。", - "fileupload_error": "ファイルのアップロードエラー", - "finish_this_chat": "チャットを終了", - "forget_remove_my_data": "データを削除", - "go_to_menu_options_forget_remove_my_personal_data": "<1>オプション → 個人データを削除 に移動してデータの即時削除をリクエストします。", - "i_agree": "同意する", - "i_need_help_with": "担当部署", - "if_you_have_any_other_questions_just_press_the_but": "ほかに質問がある場合は、下のボタンを押して新しいチャットを開始してください。", - "insert_your_field_here": "ここに{{field}}を入力", - "invalid_email": "無効なメールアドレス", - "invalid_value": "無効な値", - "leave_a_message": "メッセージを残す", - "livechat_connected": "ライブチャットに接続しました。", - "livechat_is_not_connected": "ライブチャットに接続していません。", - "media_types_not_accepted": "ファイルの種別が無効です。", - "message": "メッセージ", - "minimize_chat": "チャットを最小化", - "name": "お名前", - "need_help": "助けが必要ですか?", - "new_chat": "新しいチャット", - "no": "いいえ", - "no_available_agents_to_transfer": "対応できる担当者がいません", - "ok": "はい", - "options": "オプション", - "please_tell_us_some_information_to_start_the_chat": "チャットを開始するため次の情報を教えてください", - "please_wait_for_the_next_available_agent": "担当者におつなぎしています。しばらくお待ちください。", - "restore_chat": "チャットを復元", - "room_name_changed": "ルーム名を変更", - "send": "送信", - "sound_is_off": "音量をオフにする", - "sound_is_on": "音量をオンにする", - "start_chat": "チャットを開始", - "thanks_for_talking_with_us": "お問い合わせいただきありがとうございます", - "the_controller_of_your_personal_data_is_company_na": "あなたの個人データの管理者は[会社名]であり、[会社の住所]に登録事務所があります。チャットを開始するには、個人データが一般データ保護規則(GDPR)に従って処理および送信されることに同意するものとします。", - "type_your_message_here": "ここにメッセージを入力", - "unread_messages": "未読メッセージ", - "user_added_by": "により追加されたユーザー", - "user_joined": "ユーザー参加", - "user_left": "ユーザーは退出しました", - "user_removed_by": "により削除されたユーザー", - "waiting_queue": "待機中...", - "we_are_not_online_right_now_please_leave_a_message": "現在オンラインではありません。こちらにメッセージを残してください。", - "welcome": "ようこそ", - "write_your_message": "メッセージを入力してください", - "yes": "はい", - "you_browser_doesn_t_support_audio_element": "お使いのブラウザは audio 要素をサポートしていません", - "you_browser_doesn_t_support_video_element": "お使いのブラウザは video 要素をサポートしていません", - "your_spot_is_spot": "あなたの受付番号は #{{spot}} です", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "あなたの受付番号は #{{spot}} です (推定待ち時間: {{estimatedWaitTime}})" - } -} + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "このチャットを終了してもよろしいですか?", + "are_you_sure_you_want_to_remove_all_of_your_person": "個人データをすべて削除してもよろしいですか?", + "are_you_sure_you_want_to_switch_the_department": "部署を切り替えてもよろしいですか?", + "cancel": "キャンセル", + "change_department": "部署を変更", + "change_department_1": "部署を変更", + "chat_finished": "チャットが終了しました", + "choose_a_department": "選択してください", + "choose_a_department_1": "担当部署を選択してください", + "choose_an_option": "選択してください", + "conversation_finished": "会話が終了しました", + "count_new_messages_since_since_one": "{{val, datetime}}以降の1件の新着メッセージ", + "count_new_messages_since_since_other": "{{val, datetime}}以降の{{count}}件の新着メッセージ", + "department_switched": "部署切り替え", + "departments": "担当部署", + "disable_notifications": "通知を無効にする", + "dismiss_this_alert": "アラートを閉じる", + "drop_here_to_upload_a_file": "ここにドロップしてファイルをアップロードします", + "email": "メールアドレス", + "enable_notifications": "通知を有効にする", + "error_closing_chat": "チャット終了時にエラーが発生しました。", + "error_removing_user_data": "データ削除中にエラーが発生しました。", + "error_starting_a_new_conversation_reason": "チャット開始時にエラーが発生しました: {{reason}}", + "expand_chat": "チャットを最大化", + "field_required": "必須項目", + "file_exceeds_allowed_size_of_size": "ファイルの許容サイズ ({{size}}) を超えています。", + "fileupload_error": "ファイルのアップロードエラー", + "finish_this_chat": "チャットを終了", + "forget_remove_my_data": "データを削除", + "go_to_menu_options_forget_remove_my_personal_data": "<1>オプション → 個人データを削除 に移動してデータの即時削除をリクエストします。", + "i_agree": "同意する", + "i_need_help_with": "担当部署", + "if_you_have_any_other_questions_just_press_the_but": "ほかに質問がある場合は、下のボタンを押して新しいチャットを開始してください。", + "insert_your_field_here": "ここに{{field}}を入力", + "invalid_email": "無効なメールアドレス", + "invalid_value": "無効な値", + "leave_a_message": "メッセージを残す", + "livechat_connected": "ライブチャットに接続しました。", + "livechat_is_not_connected": "ライブチャットに接続していません。", + "media_types_not_accepted": "ファイルの種別が無効です。", + "message": "メッセージ", + "minimize_chat": "チャットを最小化", + "name": "お名前", + "need_help": "助けが必要ですか?", + "new_chat": "新しいチャット", + "no": "いいえ", + "no_available_agents_to_transfer": "対応できる担当者がいません", + "ok": "はい", + "options": "オプション", + "please_tell_us_some_information_to_start_the_chat": "チャットを開始するため次の情報を教えてください", + "please_wait_for_the_next_available_agent": "担当者におつなぎしています。しばらくお待ちください。", + "restore_chat": "チャットを復元", + "room_name_changed": "ルーム名を変更", + "send": "送信", + "sound_is_off": "音量をオフにする", + "sound_is_on": "音量をオンにする", + "start_chat": "チャットを開始", + "thanks_for_talking_with_us": "お問い合わせいただきありがとうございます", + "the_controller_of_your_personal_data_is_company_na": "あなたの個人データの管理者は[会社名]であり、[会社の住所]に登録事務所があります。チャットを開始するには、個人データが一般データ保護規則(GDPR)に従って処理および送信されることに同意するものとします。", + "type_your_message_here": "ここにメッセージを入力", + "unread_messages": "未読メッセージ", + "user_added_by": "により追加されたユーザー", + "user_joined": "ユーザー参加", + "user_left": "ユーザーは退出しました", + "user_removed_by": "により削除されたユーザー", + "waiting_queue": "待機中...", + "we_are_not_online_right_now_please_leave_a_message": "現在オンラインではありません。こちらにメッセージを残してください。", + "welcome": "ようこそ", + "write_your_message": "メッセージを入力してください", + "yes": "はい", + "you_browser_doesn_t_support_audio_element": "お使いのブラウザは audio 要素をサポートしていません", + "you_browser_doesn_t_support_video_element": "お使いのブラウザは video 要素をサポートしていません", + "your_spot_is_spot": "あなたの受付番号は #{{spot}} です", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "あなたの受付番号は #{{spot}} です (推定待ち時間: {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/km.json b/packages/livechat/src/i18n/km.json index 4d3a0b167c8c..98927df25da6 100644 --- a/packages/livechat/src/i18n/km.json +++ b/packages/livechat/src/i18n/km.json @@ -1,15 +1,15 @@ { - "translation": { - "are_you_sure_you_want_to_switch_the_department": "តើអ្នកពិតជាចង់ផ្លាស់ប្តូរនាយកដ្ឋានឬ?", - "cancel": "បោះបង់", - "conversation_finished": "សន្ទនាបានបញ្ចប់", - "department_switched": "នាយកដ្ឋានបានប្តូរ", - "no": "ទេ", - "options": "ជម្រើស", - "send": "ផ្ញើ", - "user_joined": "អ្នកប្រ", - "user_left": "អ្នកប្រើបានចាកចេញ", - "we_are_not_online_right_now_please_leave_a_message": "យើងមិននៅលើអ៊ីនធឺរណែតឥឡូវនេះទេ។ សូមទុកសារ។", - "yes": "បាទ" - } -} + "translation": { + "are_you_sure_you_want_to_switch_the_department": "តើអ្នកពិតជាចង់ផ្លាស់ប្តូរនាយកដ្ឋានឬ?", + "cancel": "បោះបង់", + "conversation_finished": "សន្ទនាបានបញ្ចប់", + "department_switched": "នាយកដ្ឋានបានប្តូរ", + "no": "ទេ", + "options": "ជម្រើស", + "send": "ផ្ញើ", + "user_joined": "អ្នកប្រ", + "user_left": "អ្នកប្រើបានចាកចេញ", + "we_are_not_online_right_now_please_leave_a_message": "យើងមិននៅលើអ៊ីនធឺរណែតឥឡូវនេះទេ។ សូមទុកសារ។", + "yes": "បាទ" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/ko.json b/packages/livechat/src/i18n/ko.json index c0b3e125e24a..5711d6552dc9 100644 --- a/packages/livechat/src/i18n/ko.json +++ b/packages/livechat/src/i18n/ko.json @@ -1,15 +1,15 @@ { - "translation": { - "are_you_sure_you_want_to_switch_the_department": "부서를 변경하시겠습니까?", - "cancel": "취소", - "conversation_finished": "대화 종료됨", - "department_switched": "부서가 변경되었습니다", - "no": "아니오", - "options": "옵션", - "send": "전송", - "user_joined": "사용자가 참여하였습니다", - "user_left": "사용자가 떠났습니다", - "we_are_not_online_right_now_please_leave_a_message": "온라인 상태가 아닙니다. 메시지를 남겨주세요.", - "yes": "예" - } -} + "translation": { + "are_you_sure_you_want_to_switch_the_department": "부서를 변경하시겠습니까?", + "cancel": "취소", + "conversation_finished": "대화 종료됨", + "department_switched": "부서가 변경되었습니다", + "no": "아니오", + "options": "옵션", + "send": "전송", + "user_joined": "사용자가 참여하였습니다", + "user_left": "사용자가 떠났습니다", + "we_are_not_online_right_now_please_leave_a_message": "온라인 상태가 아닙니다. 메시지를 남겨주세요.", + "yes": "예" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/ku.json b/packages/livechat/src/i18n/ku.json index 61452cbdf3e5..bbb1480195e4 100644 --- a/packages/livechat/src/i18n/ku.json +++ b/packages/livechat/src/i18n/ku.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Bişûndekirin", - "conversation_finished": "conversation qedand", - "department_switched": "Wezaretê veguherîn", - "no": "Na", - "options": "Vebijêrkên", - "send": "Şandin", - "user_joined": "Bikarhêner bûn", - "user_left": "çepê Bikarhêner", - "we_are_not_online_right_now_please_leave_a_message": "Em niha ne tenê ne. Ji kerema xwe, peyamek bistînin.", - "yes": "Erê" - } -} + "translation": { + "cancel": "Bişûndekirin", + "conversation_finished": "conversation qedand", + "department_switched": "Wezaretê veguherîn", + "no": "Na", + "options": "Vebijêrkên", + "send": "Şandin", + "user_joined": "Bikarhêner bûn", + "user_left": "çepê Bikarhêner", + "we_are_not_online_right_now_please_leave_a_message": "Em niha ne tenê ne. Ji kerema xwe, peyamek bistînin.", + "yes": "Erê" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/lo.json b/packages/livechat/src/i18n/lo.json index 55c8c67cdb96..2baa002106f9 100644 --- a/packages/livechat/src/i18n/lo.json +++ b/packages/livechat/src/i18n/lo.json @@ -1,13 +1,13 @@ { - "translation": { - "cancel": "ຍົກເລີກ", - "conversation_finished": "ການສົນທະນາໄດ້ສໍາເລັດ", - "department_switched": "ກົມປ່ຽນແປງ", - "options": "ຕົວເລືອກ", - "send": "ສົ່ງ", - "user_joined": "ຜູ້ໃຊ້ເຂົ້າຮ່ວມ", - "user_left": "ຊ້າຍ User", - "we_are_not_online_right_now_please_leave_a_message": "ພວກເຮົາບໍ່ໄດ້ອອນໄລນ໌ໃນປັດຈຸບັນ. ກະລຸນາອອກຈາກຂໍ້ຄວາມ.", - "yes": "ແມ່ນແລ້ວ" - } -} + "translation": { + "cancel": "ຍົກເລີກ", + "conversation_finished": "ການສົນທະນາໄດ້ສໍາເລັດ", + "department_switched": "ກົມປ່ຽນແປງ", + "options": "ຕົວເລືອກ", + "send": "ສົ່ງ", + "user_joined": "ຜູ້ໃຊ້ເຂົ້າຮ່ວມ", + "user_left": "ຊ້າຍ User", + "we_are_not_online_right_now_please_leave_a_message": "ພວກເຮົາບໍ່ໄດ້ອອນໄລນ໌ໃນປັດຈຸບັນ. ກະລຸນາອອກຈາກຂໍ້ຄວາມ.", + "yes": "ແມ່ນແລ້ວ" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/lt.json b/packages/livechat/src/i18n/lt.json index c545b9432135..b1693f4b729c 100644 --- a/packages/livechat/src/i18n/lt.json +++ b/packages/livechat/src/i18n/lt.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Atšaukti", - "conversation_finished": "Pokalbis baigtas", - "department_switched": "Skyrius perjungtas", - "no": "Nėra", - "options": "Galimybės", - "send": "Siųsti", - "user_joined": "Vartotojas prisijungė", - "user_left": "Vartotojas paliko", - "we_are_not_online_right_now_please_leave_a_message": "Šiuo metu mes nesame prisijungę. Prašome palikti pranešimą.", - "yes": "Ne" - } -} + "translation": { + "cancel": "Atšaukti", + "conversation_finished": "Pokalbis baigtas", + "department_switched": "Skyrius perjungtas", + "no": "Nėra", + "options": "Galimybės", + "send": "Siųsti", + "user_joined": "Vartotojas prisijungė", + "user_left": "Vartotojas paliko", + "we_are_not_online_right_now_please_leave_a_message": "Šiuo metu mes nesame prisijungę. Prašome palikti pranešimą.", + "yes": "Ne" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/lv.json b/packages/livechat/src/i18n/lv.json index 870cd66292dc..ae7185ced248 100644 --- a/packages/livechat/src/i18n/lv.json +++ b/packages/livechat/src/i18n/lv.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Atcelt", - "conversation_finished": "Saruna pabeigta", - "department_switched": "Departaments ir nomainīts", - "no": "Nr.", - "options": "Iespējas", - "send": "Sūtīt", - "user_joined": "Lietotājs pievienojās", - "user_left": "Lietotājs atvienojās", - "we_are_not_online_right_now_please_leave_a_message": "Mēs šobrīd neesam tiešsaistē. Lūdzu, atstāj ziņu.", - "yes": "jā" - } -} + "translation": { + "cancel": "Atcelt", + "conversation_finished": "Saruna pabeigta", + "department_switched": "Departaments ir nomainīts", + "no": "Nr.", + "options": "Iespējas", + "send": "Sūtīt", + "user_joined": "Lietotājs pievienojās", + "user_left": "Lietotājs atvienojās", + "we_are_not_online_right_now_please_leave_a_message": "Mēs šobrīd neesam tiešsaistē. Lūdzu, atstāj ziņu.", + "yes": "jā" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/mn.json b/packages/livechat/src/i18n/mn.json index 7178dff54d44..386778bfa224 100644 --- a/packages/livechat/src/i18n/mn.json +++ b/packages/livechat/src/i18n/mn.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Цуцлах", - "conversation_finished": "Харилцаа дууссан", - "department_switched": "Хэлтэс солигдсон", - "no": "Үгүй", - "options": "Сонголтууд", - "send": "Илгээх", - "user_joined": "Хэрэглэгчид нэгдсэн байна", - "user_left": "Хэрэглэгчид үлдсэн", - "we_are_not_online_right_now_please_leave_a_message": "Бид онлайнаар холбогдоогүй байна. Зурвасаа үлдээгээрэй.", - "yes": "Тиймээ" - } -} + "translation": { + "cancel": "Цуцлах", + "conversation_finished": "Харилцаа дууссан", + "department_switched": "Хэлтэс солигдсон", + "no": "Үгүй", + "options": "Сонголтууд", + "send": "Илгээх", + "user_joined": "Хэрэглэгчид нэгдсэн байна", + "user_left": "Хэрэглэгчид үлдсэн", + "we_are_not_online_right_now_please_leave_a_message": "Бид онлайнаар холбогдоогүй байна. Зурвасаа үлдээгээрэй.", + "yes": "Тиймээ" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/ms-MY.json b/packages/livechat/src/i18n/ms-MY.json index 18b06fd2ec93..8c6da897bcc4 100644 --- a/packages/livechat/src/i18n/ms-MY.json +++ b/packages/livechat/src/i18n/ms-MY.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Batal", - "conversation_finished": "perbualan selesai", - "department_switched": "Jabatan dihidupkan", - "no": "Tidak", - "options": "Pilihan", - "send": "menghantar", - "user_joined": "pengguna menyertai", - "user_left": "kiri pengguna", - "we_are_not_online_right_now_please_leave_a_message": "Kami tidak berada dalam talian sekarang. Tolong, tinggalkan mesej.", - "yes": "Ya" - } + "translation": { + "cancel": "Batal", + "conversation_finished": "perbualan selesai", + "department_switched": "Jabatan dihidupkan", + "no": "Tidak", + "options": "Pilihan", + "send": "menghantar", + "user_joined": "pengguna menyertai", + "user_left": "kiri pengguna", + "we_are_not_online_right_now_please_leave_a_message": "Kami tidak berada dalam talian sekarang. Tolong, tinggalkan mesej.", + "yes": "Ya" + } } \ No newline at end of file diff --git a/packages/livechat/src/i18n/nl.json b/packages/livechat/src/i18n/nl.json index 445eb05e90a0..d8daa6c0d54a 100644 --- a/packages/livechat/src/i18n/nl.json +++ b/packages/livechat/src/i18n/nl.json @@ -1,81 +1,81 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "Weet je zeker dat je deze chat wilt beëindigen?", - "are_you_sure_you_want_to_remove_all_of_your_person": "Weet je zeker dat je al je persoonlijke gegevens wilt verwijderen?", - "are_you_sure_you_want_to_switch_the_department": "Weet je zeker dat je wilt wisselen van afdeling?", - "cancel": "Annuleren", - "change_department": "Afdeling wijzigen", - "change_department_1": "Afdeling wijzigen", - "chat_finished": "Chat afgesloten", - "choose_a_department": "Kies een afdeling...", - "choose_a_department_1": "Kies een afdeling", - "choose_an_option": "Kies een optie...", - "conversation_finished": "Gesprek beëindigd", - "count_new_messages_since_since_one": "Eén nieuw bericht sinds {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} nieuwe berichten sinds {{val, datetime}}", - "department_switched": "Afdeling gewisseld", - "departments": "Afdelingen", - "disable_notifications": "Meldingen uitschakelen", - "dismiss_this_alert": "Deze waarschuwing negeren", - "drop_here_to_upload_a_file": "Sleep je bestand hier naartoe om deze te uploaden", - "email": "E-mailadres", - "enable_notifications": "Notificaties aanzetten", - "error_closing_chat": "Fout bij sluiten van chat.", - "error_removing_user_data": "Fout bij verwijderen van gebruikersgegevens.", - "error_starting_a_new_conversation_reason": "Fout bij het starten van een nieuw gesprek: {{reason}}", - "expand_chat": "Chat uitbreiden", - "field_required": "Dit veld is vereist", - "file_exceeds_allowed_size_of_size": "Het bestand overschrijdt de toegestane grootte van {{size}}.", - "fileupload_error": "Fout bij het uploaden", - "finish_this_chat": "Sluit deze chat af", - "forget_remove_my_data": "Vergeet/verwijder mijn gegevens", - "from_returned_the_chat_to_the_queue": "{{from}} heeft de chat opnieuw in de wachtrij gezet", - "from_transferred_the_chat_to_the_department_to": "{{from}} heeft de chat overgedragen aan afdeling {{to}}", - "from_transferred_the_chat_to_to": "{{from}} heeft de chat overgedragen aan {{to}}", - "gdpr": "AVG", - "go_to_menu_options_forget_remove_my_personal_data": "Ga naar <1>optie menu → Vergeet/verwijder mijn gegevens om een aanvraag te doen om je gegevens te verwijderen.", - "hiddenelementscount_more": "+ {{hiddenElementsCount}} meer", - "i_agree": "Ik ga akkoord", - "i_need_help_with": "Ik heb hulp nodig met...", - "if_you_have_any_other_questions_just_press_the_but": "Als je nog andere vragen hebt, druk dan op de onderstaande knop om een nieuwe chat te starten.", - "insert_your_field_here": "Vul hier je {{field}} in...", - "invalid_email": "Ongeldig E-mailadres", - "invalid_value": "Ongeldige waarde", - "leave_a_message": "Laat een bericht achter", - "livechat_connected": "Laat een bericht achter.", - "livechat_is_not_connected": "Livechat is niet verbonden.", - "media_types_not_accepted": "Mediatypen worden niet geaccepteerd.", - "message": "Bericht", - "minimize_chat": "Chat minimaliseren", - "name": "Naam", - "need_help": "Hulp nodig?", - "new_chat": "Nieuwe chat", - "no": "Nee", - "no_available_agents_to_transfer": "Er zijn geen beschikbare medewerkers om naar over te dragen", - "options": "Opties", - "please_tell_us_some_information_to_start_the_chat": "Vertel ons wat informatie om de chat te starten", - "please_wait_for_the_next_available_agent": "Wacht alstublieft op de volgende beschikbare medewerker..", - "restore_chat": "Chat herstellen", - "room_name_changed": "De naam van de chatroom werd aangepast", - "send": "Versturen", - "sound_is_off": "Geluid is uit", - "sound_is_on": "Geluid is aan", - "thanks_for_talking_with_us": "Bedankt voor je gesprek", - "the_controller_of_your_personal_data_is_company_na": "De beheerder van je persoonlijke gegevens is [Company Name], met maatschappelijke zetel op [Company Address]. Om de chat te starten, gaat je ermee akkoord dat je persoonlijke gegevens worden verwerkt en verzonden in overeenstemming met de Algemene Verordening Gegevensbescherming (AVG).", - "type_your_message_here": "Schrijf hier je bericht", - "unread_messages": "ongelezen berichten", - "user_added_by": "Gebruiker toegevoegd door", - "user_joined": "Gebruiker is toegetreden", - "user_left": "Gebruiker is weggegaan", - "user_removed_by": "Gebruiker is verwijderd door", - "waiting_queue": "Wachtrij...", - "we_are_not_online_right_now_please_leave_a_message": "We zijn nu niet online. Laat alsjeblieft een bericht achter.", - "welcome": "Welkom", - "write_your_message": "Schrijf je bericht...", - "yes": "Ja", - "you_browser_doesn_t_support_audio_element": "Je browser ondersteunt geen audio-element", - "you_browser_doesn_t_support_video_element": "Je browser ondersteunt geen video-element", - "your_spot_is_spot": "Je plaats is #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "Je plek is #{{spot}} (geschatte wachttijd: {{estimatedWaitTime}})" - } -} + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "Weet je zeker dat je deze chat wilt beëindigen?", + "are_you_sure_you_want_to_remove_all_of_your_person": "Weet je zeker dat je al je persoonlijke gegevens wilt verwijderen?", + "are_you_sure_you_want_to_switch_the_department": "Weet je zeker dat je wilt wisselen van afdeling?", + "cancel": "Annuleren", + "change_department": "Afdeling wijzigen", + "change_department_1": "Afdeling wijzigen", + "chat_finished": "Chat afgesloten", + "choose_a_department": "Kies een afdeling...", + "choose_a_department_1": "Kies een afdeling", + "choose_an_option": "Kies een optie...", + "conversation_finished": "Gesprek beëindigd", + "count_new_messages_since_since_one": "Eén nieuw bericht sinds {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} nieuwe berichten sinds {{val, datetime}}", + "department_switched": "Afdeling gewisseld", + "departments": "Afdelingen", + "disable_notifications": "Meldingen uitschakelen", + "dismiss_this_alert": "Deze waarschuwing negeren", + "drop_here_to_upload_a_file": "Sleep je bestand hier naartoe om deze te uploaden", + "email": "E-mailadres", + "enable_notifications": "Notificaties aanzetten", + "error_closing_chat": "Fout bij sluiten van chat.", + "error_removing_user_data": "Fout bij verwijderen van gebruikersgegevens.", + "error_starting_a_new_conversation_reason": "Fout bij het starten van een nieuw gesprek: {{reason}}", + "expand_chat": "Chat uitbreiden", + "field_required": "Dit veld is vereist", + "file_exceeds_allowed_size_of_size": "Het bestand overschrijdt de toegestane grootte van {{size}}.", + "fileupload_error": "Fout bij het uploaden", + "finish_this_chat": "Sluit deze chat af", + "forget_remove_my_data": "Vergeet/verwijder mijn gegevens", + "from_returned_the_chat_to_the_queue": "{{from}} heeft de chat opnieuw in de wachtrij gezet", + "from_transferred_the_chat_to_the_department_to": "{{from}} heeft de chat overgedragen aan afdeling {{to}}", + "from_transferred_the_chat_to_to": "{{from}} heeft de chat overgedragen aan {{to}}", + "gdpr": "AVG", + "go_to_menu_options_forget_remove_my_personal_data": "Ga naar <1>optie menu → Vergeet/verwijder mijn gegevens om een aanvraag te doen om je gegevens te verwijderen.", + "hiddenelementscount_more": "+ {{hiddenElementsCount}} meer", + "i_agree": "Ik ga akkoord", + "i_need_help_with": "Ik heb hulp nodig met...", + "if_you_have_any_other_questions_just_press_the_but": "Als je nog andere vragen hebt, druk dan op de onderstaande knop om een nieuwe chat te starten.", + "insert_your_field_here": "Vul hier je {{field}} in...", + "invalid_email": "Ongeldig E-mailadres", + "invalid_value": "Ongeldige waarde", + "leave_a_message": "Laat een bericht achter", + "livechat_connected": "Laat een bericht achter.", + "livechat_is_not_connected": "Livechat is niet verbonden.", + "media_types_not_accepted": "Mediatypen worden niet geaccepteerd.", + "message": "Bericht", + "minimize_chat": "Chat minimaliseren", + "name": "Naam", + "need_help": "Hulp nodig?", + "new_chat": "Nieuwe chat", + "no": "Nee", + "no_available_agents_to_transfer": "Er zijn geen beschikbare medewerkers om naar over te dragen", + "options": "Opties", + "please_tell_us_some_information_to_start_the_chat": "Vertel ons wat informatie om de chat te starten", + "please_wait_for_the_next_available_agent": "Wacht alstublieft op de volgende beschikbare medewerker..", + "restore_chat": "Chat herstellen", + "room_name_changed": "De naam van de chatroom werd aangepast", + "send": "Versturen", + "sound_is_off": "Geluid is uit", + "sound_is_on": "Geluid is aan", + "thanks_for_talking_with_us": "Bedankt voor je gesprek", + "the_controller_of_your_personal_data_is_company_na": "De beheerder van je persoonlijke gegevens is [Company Name], met maatschappelijke zetel op [Company Address]. Om de chat te starten, gaat je ermee akkoord dat je persoonlijke gegevens worden verwerkt en verzonden in overeenstemming met de Algemene Verordening Gegevensbescherming (AVG).", + "type_your_message_here": "Schrijf hier je bericht", + "unread_messages": "ongelezen berichten", + "user_added_by": "Gebruiker toegevoegd door", + "user_joined": "Gebruiker is toegetreden", + "user_left": "Gebruiker is weggegaan", + "user_removed_by": "Gebruiker is verwijderd door", + "waiting_queue": "Wachtrij...", + "we_are_not_online_right_now_please_leave_a_message": "We zijn nu niet online. Laat alsjeblieft een bericht achter.", + "welcome": "Welkom", + "write_your_message": "Schrijf je bericht...", + "yes": "Ja", + "you_browser_doesn_t_support_audio_element": "Je browser ondersteunt geen audio-element", + "you_browser_doesn_t_support_video_element": "Je browser ondersteunt geen video-element", + "your_spot_is_spot": "Je plaats is #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "Je plek is #{{spot}} (geschatte wachttijd: {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/no.json b/packages/livechat/src/i18n/no.json index 4aacd344322f..9264dd1b8d7d 100644 --- a/packages/livechat/src/i18n/no.json +++ b/packages/livechat/src/i18n/no.json @@ -1,14 +1,14 @@ { - "translation": { - "are_you_sure_you_want_to_switch_the_department": "Er du sikker på at du vil bytte avdeling?", - "cancel": "Avbryt", - "conversation_finished": "Samtalen er avsluttet", - "department_switched": "Avdeling skiftet", - "no": "Nei", - "options": "Egenskaper", - "user_joined": "Bruker ble med", - "user_left": "Bruker igjen", - "we_are_not_online_right_now_please_leave_a_message": "Vi er ikke online akkurat nå. Legg igjen en beskjed.", - "yes": "Ja" - } -} + "translation": { + "are_you_sure_you_want_to_switch_the_department": "Er du sikker på at du vil bytte avdeling?", + "cancel": "Avbryt", + "conversation_finished": "Samtalen er avsluttet", + "department_switched": "Avdeling skiftet", + "no": "Nei", + "options": "Egenskaper", + "user_joined": "Bruker ble med", + "user_left": "Bruker igjen", + "we_are_not_online_right_now_please_leave_a_message": "Vi er ikke online akkurat nå. Legg igjen en beskjed.", + "yes": "Ja" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/pl.json b/packages/livechat/src/i18n/pl.json index fb070be11ee3..cf9c32e2ba17 100644 --- a/packages/livechat/src/i18n/pl.json +++ b/packages/livechat/src/i18n/pl.json @@ -1,73 +1,73 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "Czy na pewno chcesz zakończyć rozmowę?", - "are_you_sure_you_want_to_remove_all_of_your_person": "Czy na pewno chcesz usunąć wszystkie dane dotyczące rozmowy?", - "are_you_sure_you_want_to_switch_the_department": "Czy na pewno chcesz zmienić dział?", - "cancel": "Anuluj", - "change_department": "Zmień dział", - "change_department_1": "Zmień Dział", - "chat_finished": "Rozmowa Zakończona", - "choose_a_department": "Wybierz dział...", - "choose_a_department_1": "Wybierz dział", - "choose_an_option": "Wybierz opcję...", - "conversation_finished": "Rozmowa zakończona", - "count_new_messages_since_since_one": "Jedna nowa wiadomość od {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} nowych wiadomości od {{val, datetime}}", - "department_switched": "Zmieniono dział", - "departments": "Działy", - "disable_notifications": "Wyłącz powiadomienia", - "dismiss_this_alert": "Odrzuć powiadomienie", - "drop_here_to_upload_a_file": "Upuść tutaj, aby przesłać plik", - "enable_notifications": "Włącz powiadomienia", - "error_closing_chat": "Wystąpił błąd podczas zamykania rozmowy.", - "error_removing_user_data": "Wystąpił błąd podczas usuwania danych.", - "error_starting_a_new_conversation_reason": "Wystąpił błąd podczas tworzenia rozmowy: {{reason}}", - "expand_chat": "Otwórz w nowym oknie", - "file_exceeds_allowed_size_of_size": "Plik przekroczył dopuszczalny rozmiar {{size}}.", - "fileupload_error": "Błąd podczas wysyłania pliku", - "finish_this_chat": "Zakończ rozmowę", - "forget_remove_my_data": "Zapomnij/Usuń moje dane", - "gdpr": "RODO", - "go_to_menu_options_forget_remove_my_personal_data": "Idź do <1>Opcje → Zapomnij/Usuń moje dane, aby usunąć dane dotyczące rozmowy.", - "i_agree": "Zgadzam się", - "i_need_help_with": "Potrzebuję pomocy...", - "if_you_have_any_other_questions_just_press_the_but": "Jeśli masz jeszcze jakieś pytania, naciśnij poniższy przycisk, aby rozpocząć rozmowę.", - "leave_a_message": "Zostaw wiadomość", - "livechat_connected": "Połączono.", - "livechat_is_not_connected": "Nie połączono.", - "media_types_not_accepted": "Format pliku niedozwolony.", - "message": "Wiadomość", - "minimize_chat": "Minimalizuj okno", - "name": "Imię", - "need_help": "Potrzebujesz pomocy?", - "new_chat": "Nowa rozmowa", - "no": "Nie", - "no_available_agents_to_transfer": "Żaden konsultant nie jest dostępny", - "options": "Opcje", - "please_tell_us_some_information_to_start_the_chat": "Wyślij wiadomość, aby rozpocząć rozmowę", - "please_wait_for_the_next_available_agent": "Proszę zaczekać na konsultanta..", - "restore_chat": "Przywróć rozmowę", - "room_name_changed": "Nazwa rozmowy została zmieniona", - "send": "Wyślij", - "sound_is_off": "Wyłącz dźwięk", - "sound_is_on": "Włącz dźwiek", - "start_chat": "Rozpocznij rozmowę", - "thanks_for_talking_with_us": "Dziękujemy za kontakt", - "the_controller_of_your_personal_data_is_company_na": "Administratorem twoich danych osobowych jest [Company Name], z siedzibą przy ulicy [Company Address]. Rozpoczynając rozmowę, wyrażasz zgodę na przetwarzanie i przesyłanie twoich danych osobowych zgodnie z ogólnym rozporządzeniem o ochronie danych (RODO).", - "type_your_message_here": "Wpisz tu swoją wiadomość", - "unread_messages": "nieprzeczytane wiadomości", - "user_added_by": "Użytkownik dodany przez", - "user_joined": "Użytkownik dołączył", - "user_left": "Użytkownik wyszedł", - "user_removed_by": "Użytkownik usunięty przez", - "waiting_queue": "Oczekiwanie w kolejce...", - "we_are_not_online_right_now_please_leave_a_message": "Nie jesteśmy teraz online. Proszę zostawić wiadomość.", - "welcome": "Witamy", - "write_your_message": "Wpisz swoją wiadomość...", - "yes": "Tak", - "you_browser_doesn_t_support_audio_element": "Twoja przeglądarka nie wspiera elementów audio", - "you_browser_doesn_t_support_video_element": "Twoja przeglądarka nie wspiera elementów video", - "your_spot_is_spot": "Twoje miejsce to #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "Twoje miejsce to #{{spot}} (Szacowany czas oczekiwania: {{estimatedWaitTime}})" - } -} + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "Czy na pewno chcesz zakończyć rozmowę?", + "are_you_sure_you_want_to_remove_all_of_your_person": "Czy na pewno chcesz usunąć wszystkie dane dotyczące rozmowy?", + "are_you_sure_you_want_to_switch_the_department": "Czy na pewno chcesz zmienić dział?", + "cancel": "Anuluj", + "change_department": "Zmień dział", + "change_department_1": "Zmień Dział", + "chat_finished": "Rozmowa Zakończona", + "choose_a_department": "Wybierz dział...", + "choose_a_department_1": "Wybierz dział", + "choose_an_option": "Wybierz opcję...", + "conversation_finished": "Rozmowa zakończona", + "count_new_messages_since_since_one": "Jedna nowa wiadomość od {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} nowych wiadomości od {{val, datetime}}", + "department_switched": "Zmieniono dział", + "departments": "Działy", + "disable_notifications": "Wyłącz powiadomienia", + "dismiss_this_alert": "Odrzuć powiadomienie", + "drop_here_to_upload_a_file": "Upuść tutaj, aby przesłać plik", + "enable_notifications": "Włącz powiadomienia", + "error_closing_chat": "Wystąpił błąd podczas zamykania rozmowy.", + "error_removing_user_data": "Wystąpił błąd podczas usuwania danych.", + "error_starting_a_new_conversation_reason": "Wystąpił błąd podczas tworzenia rozmowy: {{reason}}", + "expand_chat": "Otwórz w nowym oknie", + "file_exceeds_allowed_size_of_size": "Plik przekroczył dopuszczalny rozmiar {{size}}.", + "fileupload_error": "Błąd podczas wysyłania pliku", + "finish_this_chat": "Zakończ rozmowę", + "forget_remove_my_data": "Zapomnij/Usuń moje dane", + "gdpr": "RODO", + "go_to_menu_options_forget_remove_my_personal_data": "Idź do <1>Opcje → Zapomnij/Usuń moje dane, aby usunąć dane dotyczące rozmowy.", + "i_agree": "Zgadzam się", + "i_need_help_with": "Potrzebuję pomocy...", + "if_you_have_any_other_questions_just_press_the_but": "Jeśli masz jeszcze jakieś pytania, naciśnij poniższy przycisk, aby rozpocząć rozmowę.", + "leave_a_message": "Zostaw wiadomość", + "livechat_connected": "Połączono.", + "livechat_is_not_connected": "Nie połączono.", + "media_types_not_accepted": "Format pliku niedozwolony.", + "message": "Wiadomość", + "minimize_chat": "Minimalizuj okno", + "name": "Imię", + "need_help": "Potrzebujesz pomocy?", + "new_chat": "Nowa rozmowa", + "no": "Nie", + "no_available_agents_to_transfer": "Żaden konsultant nie jest dostępny", + "options": "Opcje", + "please_tell_us_some_information_to_start_the_chat": "Wyślij wiadomość, aby rozpocząć rozmowę", + "please_wait_for_the_next_available_agent": "Proszę zaczekać na konsultanta..", + "restore_chat": "Przywróć rozmowę", + "room_name_changed": "Nazwa rozmowy została zmieniona", + "send": "Wyślij", + "sound_is_off": "Wyłącz dźwięk", + "sound_is_on": "Włącz dźwiek", + "start_chat": "Rozpocznij rozmowę", + "thanks_for_talking_with_us": "Dziękujemy za kontakt", + "the_controller_of_your_personal_data_is_company_na": "Administratorem twoich danych osobowych jest [Company Name], z siedzibą przy ulicy [Company Address]. Rozpoczynając rozmowę, wyrażasz zgodę na przetwarzanie i przesyłanie twoich danych osobowych zgodnie z ogólnym rozporządzeniem o ochronie danych (RODO).", + "type_your_message_here": "Wpisz tu swoją wiadomość", + "unread_messages": "nieprzeczytane wiadomości", + "user_added_by": "Użytkownik dodany przez", + "user_joined": "Użytkownik dołączył", + "user_left": "Użytkownik wyszedł", + "user_removed_by": "Użytkownik usunięty przez", + "waiting_queue": "Oczekiwanie w kolejce...", + "we_are_not_online_right_now_please_leave_a_message": "Nie jesteśmy teraz online. Proszę zostawić wiadomość.", + "welcome": "Witamy", + "write_your_message": "Wpisz swoją wiadomość...", + "yes": "Tak", + "you_browser_doesn_t_support_audio_element": "Twoja przeglądarka nie wspiera elementów audio", + "you_browser_doesn_t_support_video_element": "Twoja przeglądarka nie wspiera elementów video", + "your_spot_is_spot": "Twoje miejsce to #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "Twoje miejsce to #{{spot}} (Szacowany czas oczekiwania: {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/pt-BR.json b/packages/livechat/src/i18n/pt-BR.json index 214d1865895f..d47eb054e79b 100644 --- a/packages/livechat/src/i18n/pt-BR.json +++ b/packages/livechat/src/i18n/pt-BR.json @@ -1,86 +1,86 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "Tem certeza de que deseja encerrar este chat?", - "are_you_sure_you_want_to_remove_all_of_your_person": "Tem certeza de que deseja remover todos os seus dados pessoais?", - "are_you_sure_you_want_to_switch_the_department": "Tem certeza de que deseja trocar de departamento?", - "cancel": "Cancelar", - "change_department": "Trocar departamento", - "change_department_1": "Trocar Departamento", - "chat_finished": "Chat Terminado", - "chat_now": "Converse agora", - "chat_started": "Bate-papo iniciado", - "choose_a_department": "Escolha um departamento...", - "choose_a_department_1": "Escolha um departamento", - "choose_an_option": "Escolha uma opção...", - "conversation_finished": "Conversa Encerrada", - "count_new_messages_since_since_one": "Uma nova mensagem desde {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} novas mensagens desde {{val, datetime}}", - "department_switched": "Departamento trocado", - "departments": "Departamentos", - "disable_notifications": "Disabilitar notificações", - "dismiss_this_alert": "Ignorar este alerta", - "drop_here_to_upload_a_file": "Arraste e solte um aquivo para fazer upload", - "email": "E-mail", - "enable_notifications": "Habilitar notificações", - "error_closing_chat": "Erro ao fechar o chat.", - "error_removing_user_data": "Erro ao remover os dados do usuário.", - "error_starting_a_new_conversation_reason": "Erro ao iniciar nova conversa: {{reason}}", - "expand_chat": "Expandir chat", - "field_required": "Campo obrigatório", - "file_exceeds_allowed_size_of_size": "Arquivo excede o tamanho permitido de {{size}}.", - "fileupload_error": "Erro no upload do arquivo", - "finish_this_chat": "Encerrar este chat", - "forget_remove_my_data": "Esquecer/remover meus dados pessoais", - "from_returned_the_chat_to_the_queue": "{{from}} retornou a conversa para a fila", - "from_transferred_the_chat_to_the_department_to": "{{from}} transferiu a conversa para o departamento {{to}}", - "from_transferred_the_chat_to_to": "{{from}} transferiu a conversa para {{to}}", - "go_to_menu_options_forget_remove_my_personal_data": "Vá para o menu <1>opções → Esquecer/remover meus dados pessoais para solicitar a remoção imediata dos seus dados.", - "hiddenelementscount_more": "+ {{hiddenElementsCount}} mais", - "i_agree": "Concordo", - "i_need_help_with": "Preciso de ajuda com...", - "if_you_have_any_other_questions_just_press_the_but": "Se você tiver qualquer outras dúvidas, basta pressionar o botão abaixo para iniciar um novo chat.", - "insert_your_field_here": "Entre com o seu {{field}} aqui...", - "invalid_email": "E-mail inválido", - "invalid_value": "Valor inválido", - "leave_a_message": "Deixe uma mensagem", - "livechat_connected": "Livechat conectado.", - "livechat_is_not_connected": "Livechat não está conectado ao servidor.", - "media_types_not_accepted": "Tipo de mídia não aceito.", - "message": "Mensagem", - "minimize_chat": "Minimizar chat", - "name": "Nome", - "need_help": "Precisa de ajuda?", - "new_chat": "Novo Chat", - "no": "Não", - "no_available_agents_to_transfer": "Não há agentes disponíveis para transferência", - "options": "Opções", - "please_tell_us_some_information_to_start_the_chat": "Por favor, nos passe algumas informações antes de iniciar o chat", - "please_wait_for_the_next_available_agent": "Por favor, aguarde o próximo agente disponível..", - "powered_by_rocket_chat": "Desenvolvido por Rocket.Chat", - "restore_chat": "Restaurar o chat", - "room_name_changed": "Nome da sala alterado", - "send": "Enviar", - "sound_is_off": "O som está desligado", - "sound_is_on": "O som está ligado", - "start_chat": "Iniciar chat", - "thanks_for_talking_with_us": "Obrigado por falar conosco", - "the_controller_of_your_personal_data_is_company_na": "O controlador dos seus dados pessoais é [Nome da Empresa], com sede em [Company Address]. Para iniciar o chat, você concorda que seus dados pessoais serão processados e transmitidos de acordo com o Regulamento Geral de Proteção de Dados(GDPR).", - "transcript_success": "Transcrição enviada", - "type_your_message_here": "Digite sua mensagem aqui", - "unread_messages": "Mensagens não lidas", - "user_added_by": "Usuário {{user_added}} adicionado à conversa por {{user_by}}.", - "user_joined": "Usuário entrou", - "user_left": "Usuário saiu", - "user_removed_by": "Usuário removido por", - "waiting_queue": "Fila de espera...", - "we_are_not_online_right_now_please_leave_a_message": "Não estamos online agora. Por favor deixe uma mensagem.", - "welcome": "Bem vindo(a)", - "would_you_like_a_copy_of_this_chat_emailed": "Gostaria de uma cópia desta conversa por email?", - "write_your_message": "Escreva sua mensagem...", - "yes": "Sim", - "you_browser_doesn_t_support_audio_element": "Seu navegador não suporta elemento de áudio", - "you_browser_doesn_t_support_video_element": "Seu navegador não suporta elemento de vídeo", - "your_spot_is_spot": "Seu lugar é #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "Seu lugar é #{{spot}} (Tempo estimado: {{estimatedWaitTime}})" - } + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "Tem certeza de que deseja encerrar este chat?", + "are_you_sure_you_want_to_remove_all_of_your_person": "Tem certeza de que deseja remover todos os seus dados pessoais?", + "are_you_sure_you_want_to_switch_the_department": "Tem certeza de que deseja trocar de departamento?", + "cancel": "Cancelar", + "change_department": "Trocar departamento", + "change_department_1": "Trocar Departamento", + "chat_finished": "Chat Terminado", + "chat_now": "Converse agora", + "chat_started": "Bate-papo iniciado", + "choose_a_department": "Escolha um departamento...", + "choose_a_department_1": "Escolha um departamento", + "choose_an_option": "Escolha uma opção...", + "conversation_finished": "Conversa Encerrada", + "count_new_messages_since_since_one": "Uma nova mensagem desde {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} novas mensagens desde {{val, datetime}}", + "department_switched": "Departamento trocado", + "departments": "Departamentos", + "disable_notifications": "Disabilitar notificações", + "dismiss_this_alert": "Ignorar este alerta", + "drop_here_to_upload_a_file": "Arraste e solte um aquivo para fazer upload", + "email": "E-mail", + "enable_notifications": "Habilitar notificações", + "error_closing_chat": "Erro ao fechar o chat.", + "error_removing_user_data": "Erro ao remover os dados do usuário.", + "error_starting_a_new_conversation_reason": "Erro ao iniciar nova conversa: {{reason}}", + "expand_chat": "Expandir chat", + "field_required": "Campo obrigatório", + "file_exceeds_allowed_size_of_size": "Arquivo excede o tamanho permitido de {{size}}.", + "fileupload_error": "Erro no upload do arquivo", + "finish_this_chat": "Encerrar este chat", + "forget_remove_my_data": "Esquecer/remover meus dados pessoais", + "from_returned_the_chat_to_the_queue": "{{from}} retornou a conversa para a fila", + "from_transferred_the_chat_to_the_department_to": "{{from}} transferiu a conversa para o departamento {{to}}", + "from_transferred_the_chat_to_to": "{{from}} transferiu a conversa para {{to}}", + "go_to_menu_options_forget_remove_my_personal_data": "Vá para o menu <1>opções → Esquecer/remover meus dados pessoais para solicitar a remoção imediata dos seus dados.", + "hiddenelementscount_more": "+ {{hiddenElementsCount}} mais", + "i_agree": "Concordo", + "i_need_help_with": "Preciso de ajuda com...", + "if_you_have_any_other_questions_just_press_the_but": "Se você tiver qualquer outras dúvidas, basta pressionar o botão abaixo para iniciar um novo chat.", + "insert_your_field_here": "Entre com o seu {{field}} aqui...", + "invalid_email": "E-mail inválido", + "invalid_value": "Valor inválido", + "leave_a_message": "Deixe uma mensagem", + "livechat_connected": "Livechat conectado.", + "livechat_is_not_connected": "Livechat não está conectado ao servidor.", + "media_types_not_accepted": "Tipo de mídia não aceito.", + "message": "Mensagem", + "minimize_chat": "Minimizar chat", + "name": "Nome", + "need_help": "Precisa de ajuda?", + "new_chat": "Novo Chat", + "no": "Não", + "no_available_agents_to_transfer": "Não há agentes disponíveis para transferência", + "options": "Opções", + "please_tell_us_some_information_to_start_the_chat": "Por favor, nos passe algumas informações antes de iniciar o chat", + "please_wait_for_the_next_available_agent": "Por favor, aguarde o próximo agente disponível..", + "powered_by_rocket_chat": "Desenvolvido por Rocket.Chat", + "restore_chat": "Restaurar o chat", + "room_name_changed": "Nome da sala alterado", + "send": "Enviar", + "sound_is_off": "O som está desligado", + "sound_is_on": "O som está ligado", + "start_chat": "Iniciar chat", + "thanks_for_talking_with_us": "Obrigado por falar conosco", + "the_controller_of_your_personal_data_is_company_na": "O controlador dos seus dados pessoais é [Nome da Empresa], com sede em [Company Address]. Para iniciar o chat, você concorda que seus dados pessoais serão processados e transmitidos de acordo com o Regulamento Geral de Proteção de Dados(GDPR).", + "transcript_success": "Transcrição enviada", + "type_your_message_here": "Digite sua mensagem aqui", + "unread_messages": "Mensagens não lidas", + "user_added_by": "Usuário {{user_added}} adicionado à conversa por {{user_by}}.", + "user_joined": "Usuário entrou", + "user_left": "Usuário saiu", + "user_removed_by": "Usuário removido por", + "waiting_queue": "Fila de espera...", + "we_are_not_online_right_now_please_leave_a_message": "Não estamos online agora. Por favor deixe uma mensagem.", + "welcome": "Bem vindo(a)", + "would_you_like_a_copy_of_this_chat_emailed": "Gostaria de uma cópia desta conversa por email?", + "write_your_message": "Escreva sua mensagem...", + "yes": "Sim", + "you_browser_doesn_t_support_audio_element": "Seu navegador não suporta elemento de áudio", + "you_browser_doesn_t_support_video_element": "Seu navegador não suporta elemento de vídeo", + "your_spot_is_spot": "Seu lugar é #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "Seu lugar é #{{spot}} (Tempo estimado: {{estimatedWaitTime}})" + } } \ No newline at end of file diff --git a/packages/livechat/src/i18n/pt.json b/packages/livechat/src/i18n/pt.json index e6a6f553102c..6657e0efd093 100644 --- a/packages/livechat/src/i18n/pt.json +++ b/packages/livechat/src/i18n/pt.json @@ -1,19 +1,19 @@ { - "translation": { - "are_you_sure_you_want_to_switch_the_department": "Tem certeza que deseja trocar de departamento?", - "cancel": "Cancelar", - "conversation_finished": "Chat encerrado", - "count_new_messages_since_since_one": "Uma nova mensagem desde {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} novas mensagens desde {{val, datetime}}", - "department_switched": "Departamento comutado", - "no": "Não", - "options": "Opções", - "send": "Enviar", - "user_joined": "Utilizador entrou", - "user_left": "O utilizador saiu", - "we_are_not_online_right_now_please_leave_a_message": "Não estamos online agora. Por favor deixe uma mensagem.", - "yes": "Sim", - "your_spot_is_spot": "Seu lugar é #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "Seu lugar é #{{spot}} (Tempo estimado: {{estimatedWaitTime}})" - } -} + "translation": { + "are_you_sure_you_want_to_switch_the_department": "Tem certeza que deseja trocar de departamento?", + "cancel": "Cancelar", + "conversation_finished": "Chat encerrado", + "count_new_messages_since_since_one": "Uma nova mensagem desde {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} novas mensagens desde {{val, datetime}}", + "department_switched": "Departamento comutado", + "no": "Não", + "options": "Opções", + "send": "Enviar", + "user_joined": "Utilizador entrou", + "user_left": "O utilizador saiu", + "we_are_not_online_right_now_please_leave_a_message": "Não estamos online agora. Por favor deixe uma mensagem.", + "yes": "Sim", + "your_spot_is_spot": "Seu lugar é #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "Seu lugar é #{{spot}} (Tempo estimado: {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/ro.json b/packages/livechat/src/i18n/ro.json index e96059d8b6ef..d4c5bb8b510a 100644 --- a/packages/livechat/src/i18n/ro.json +++ b/packages/livechat/src/i18n/ro.json @@ -1,18 +1,18 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "Sigur doriți să încheiați acest chat?", - "cancel": "Anulează", - "chat_finished": "Chat-ul s-a terminat!", - "choose_a_department": "Alegeți un departament...", - "choose_a_department_1": "Alegeți un departament", - "conversation_finished": "conversație terminat", - "department_switched": "Departamentul a fost schimbat", - "no": "Nu", - "options": "Opțiuni", - "send": "Trimite", - "user_joined": "utilizator s-a alăturat", - "user_left": "stânga utilizator", - "we_are_not_online_right_now_please_leave_a_message": "Nu suntem online chiar acum. Te rog lasa un mesaj.", - "yes": "Da" - } -} + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "Sigur doriți să încheiați acest chat?", + "cancel": "Anulează", + "chat_finished": "Chat-ul s-a terminat!", + "choose_a_department": "Alegeți un departament...", + "choose_a_department_1": "Alegeți un departament", + "conversation_finished": "conversație terminat", + "department_switched": "Departamentul a fost schimbat", + "no": "Nu", + "options": "Opțiuni", + "send": "Trimite", + "user_joined": "utilizator s-a alăturat", + "user_left": "stânga utilizator", + "we_are_not_online_right_now_please_leave_a_message": "Nu suntem online chiar acum. Te rog lasa un mesaj.", + "yes": "Da" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/ru.json b/packages/livechat/src/i18n/ru.json index 5be1069f1396..de242247a7d1 100644 --- a/packages/livechat/src/i18n/ru.json +++ b/packages/livechat/src/i18n/ru.json @@ -1,77 +1,77 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "Вы уверены, что хотите закончить разговор?", - "are_you_sure_you_want_to_remove_all_of_your_person": "Вы уверены, что хотите удалить все ваши персональные данные?", - "are_you_sure_you_want_to_switch_the_department": "Вы уверены, что хотите сменить отдел?", - "cancel": "Отмена", - "change_department": "Сменить отдел", - "change_department_1": "Сменить Отдел", - "chat_finished": "Разговор Закончен", - "choose_a_department": "Выберите отдел...", - "choose_a_department_1": "Выберите отдел", - "choose_an_option": "Выберите опцию...", - "conversation_finished": "Разговор окончен", - "count_new_messages_since_since_one": "Одно новое сообщение с {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} новых сообщений с {{val, datetime}}", - "department_switched": "Отдел сменeн", - "departments": "Отделы", - "disable_notifications": "Выключить оповещение", - "dismiss_this_alert": "Пропустить данное предупреждение", - "drop_here_to_upload_a_file": "Бросить сюда файл для загрузки", - "enable_notifications": "Включить оповещения", - "error_closing_chat": "Ошибка закрытия чата.", - "error_removing_user_data": "Ошибка удаления пользовательских данных.", - "error_starting_a_new_conversation_reason": "Ошибка при запуске нового диалога: {{reason}}", - "expand_chat": "Развернуть окно чата", - "field_required": "Поле обязательно", - "file_exceeds_allowed_size_of_size": "Файл превышает разрешенный размер в {{size}}.", - "fileupload_error": "Ошибка загрузки файла", - "finish_this_chat": "Завершить этот чат", - "forget_remove_my_data": "Забыть/удалить мои данные", - "from_transferred_the_chat_to_to": "Перенаправлен от {{from}} на {{to}}", - "go_to_menu_options_forget_remove_my_personal_data": "Перейти <1>меню опции → Забыть/удалить мои персональные данные, чтобы запросить немедленное удаление ваших данных.", - "i_agree": "Я согласен", - "i_need_help_with": "Мне требуется помощь с...", - "if_you_have_any_other_questions_just_press_the_but": "Если у вас есть еще вопросы, нажмите на кнопку ниже, чтобы начать новый чат.", - "insert_your_field_here": "Введите {{field}} ...", - "invalid_email": "Неверный эмайл", - "invalid_value": "Неверное значение", - "leave_a_message": "Оставить сообщение", - "livechat_connected": "Livechat подключен.", - "livechat_is_not_connected": "Livechat не подключен.", - "media_types_not_accepted": "Данный тип медиа не поддерживается.", - "message": "Сообщение", - "minimize_chat": "Свернуть окно чата", - "name": "Имя", - "need_help": "Требуется помощь?", - "new_chat": "Новый чат", - "no": "Нет", - "no_available_agents_to_transfer": "Нет доступных операторов для передачи", - "options": "Параметры", - "please_tell_us_some_information_to_start_the_chat": "Пожалуйста, расскажите о себе прежде чем, начать чат", - "please_wait_for_the_next_available_agent": "Пожалуйста, подождите пока освободится следующий оператор...", - "powered_by_rocket_chat": "Работает на Rocket.Chat", - "restore_chat": "Восстановить чат", - "room_name_changed": "Имя комнаты изменено", - "send": "Отправить", - "sound_is_off": "Звук отключен", - "sound_is_on": "Звук включен", - "start_chat": "Начать чат", - "thanks_for_talking_with_us": "Спасибо, что обратились к нам", - "type_your_message_here": "Напечатайте ваше сообщение", - "unread_messages": "непрочитанные сообщения", - "user_added_by": "Пользователь добавлен пользователем", - "user_joined": "Пользователь присоединился", - "user_left": "Пользователь вышел", - "user_removed_by": "Пользователь удален пользователем", - "waiting_queue": "Ожидание очереди...", - "we_are_not_online_right_now_please_leave_a_message": "Мы сейчас не в сети. Пожалуйста, оставьте сообщение.", - "welcome": "Добро пожаловать", - "write_your_message": "Напишите ваше сообщение...", - "yes": "Да", - "you_browser_doesn_t_support_audio_element": "Ваш браузер не поддерживает аудио", - "you_browser_doesn_t_support_video_element": "Ваш браузер не поддерживает видео", - "your_spot_is_spot": "Ваше место в очереди: #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "Ваше место в очереди: #{{spot}} (Предполагаемое время ожидания: {{estimatedWaitTime}})" - } -} + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "Вы уверены, что хотите закончить разговор?", + "are_you_sure_you_want_to_remove_all_of_your_person": "Вы уверены, что хотите удалить все ваши персональные данные?", + "are_you_sure_you_want_to_switch_the_department": "Вы уверены, что хотите сменить отдел?", + "cancel": "Отмена", + "change_department": "Сменить отдел", + "change_department_1": "Сменить Отдел", + "chat_finished": "Разговор Закончен", + "choose_a_department": "Выберите отдел...", + "choose_a_department_1": "Выберите отдел", + "choose_an_option": "Выберите опцию...", + "conversation_finished": "Разговор окончен", + "count_new_messages_since_since_one": "Одно новое сообщение с {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} новых сообщений с {{val, datetime}}", + "department_switched": "Отдел сменeн", + "departments": "Отделы", + "disable_notifications": "Выключить оповещение", + "dismiss_this_alert": "Пропустить данное предупреждение", + "drop_here_to_upload_a_file": "Бросить сюда файл для загрузки", + "enable_notifications": "Включить оповещения", + "error_closing_chat": "Ошибка закрытия чата.", + "error_removing_user_data": "Ошибка удаления пользовательских данных.", + "error_starting_a_new_conversation_reason": "Ошибка при запуске нового диалога: {{reason}}", + "expand_chat": "Развернуть окно чата", + "field_required": "Поле обязательно", + "file_exceeds_allowed_size_of_size": "Файл превышает разрешенный размер в {{size}}.", + "fileupload_error": "Ошибка загрузки файла", + "finish_this_chat": "Завершить этот чат", + "forget_remove_my_data": "Забыть/удалить мои данные", + "from_transferred_the_chat_to_to": "Перенаправлен от {{from}} на {{to}}", + "go_to_menu_options_forget_remove_my_personal_data": "Перейти <1>меню опции → Забыть/удалить мои персональные данные, чтобы запросить немедленное удаление ваших данных.", + "i_agree": "Я согласен", + "i_need_help_with": "Мне требуется помощь с...", + "if_you_have_any_other_questions_just_press_the_but": "Если у вас есть еще вопросы, нажмите на кнопку ниже, чтобы начать новый чат.", + "insert_your_field_here": "Введите {{field}} ...", + "invalid_email": "Неверный эмайл", + "invalid_value": "Неверное значение", + "leave_a_message": "Оставить сообщение", + "livechat_connected": "Livechat подключен.", + "livechat_is_not_connected": "Livechat не подключен.", + "media_types_not_accepted": "Данный тип медиа не поддерживается.", + "message": "Сообщение", + "minimize_chat": "Свернуть окно чата", + "name": "Имя", + "need_help": "Требуется помощь?", + "new_chat": "Новый чат", + "no": "Нет", + "no_available_agents_to_transfer": "Нет доступных операторов для передачи", + "options": "Параметры", + "please_tell_us_some_information_to_start_the_chat": "Пожалуйста, расскажите о себе прежде чем, начать чат", + "please_wait_for_the_next_available_agent": "Пожалуйста, подождите пока освободится следующий оператор...", + "powered_by_rocket_chat": "Работает на Rocket.Chat", + "restore_chat": "Восстановить чат", + "room_name_changed": "Имя комнаты изменено", + "send": "Отправить", + "sound_is_off": "Звук отключен", + "sound_is_on": "Звук включен", + "start_chat": "Начать чат", + "thanks_for_talking_with_us": "Спасибо, что обратились к нам", + "type_your_message_here": "Напечатайте ваше сообщение", + "unread_messages": "непрочитанные сообщения", + "user_added_by": "Пользователь добавлен пользователем", + "user_joined": "Пользователь присоединился", + "user_left": "Пользователь вышел", + "user_removed_by": "Пользователь удален пользователем", + "waiting_queue": "Ожидание очереди...", + "we_are_not_online_right_now_please_leave_a_message": "Мы сейчас не в сети. Пожалуйста, оставьте сообщение.", + "welcome": "Добро пожаловать", + "write_your_message": "Напишите ваше сообщение...", + "yes": "Да", + "you_browser_doesn_t_support_audio_element": "Ваш браузер не поддерживает аудио", + "you_browser_doesn_t_support_video_element": "Ваш браузер не поддерживает видео", + "your_spot_is_spot": "Ваше место в очереди: #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "Ваше место в очереди: #{{spot}} (Предполагаемое время ожидания: {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/sk-SK.json b/packages/livechat/src/i18n/sk-SK.json index 133c03844b29..b0ae2f6bcd75 100644 --- a/packages/livechat/src/i18n/sk-SK.json +++ b/packages/livechat/src/i18n/sk-SK.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Zrušiť", - "conversation_finished": "Konverzácia bola ukončená", - "department_switched": "Oddelenie bolo prepnuté", - "no": "Žiadny", - "options": "Možnosti", - "send": "Odoslať", - "user_joined": "Používateľ sa pripojil", - "user_left": "Používateľ odišiel", - "we_are_not_online_right_now_please_leave_a_message": "Momentálne nie sme online. Prosíme, zanechajte správu.", - "yes": "Áno" - } + "translation": { + "cancel": "Zrušiť", + "conversation_finished": "Konverzácia bola ukončená", + "department_switched": "Oddelenie bolo prepnuté", + "no": "Žiadny", + "options": "Možnosti", + "send": "Odoslať", + "user_joined": "Používateľ sa pripojil", + "user_left": "Používateľ odišiel", + "we_are_not_online_right_now_please_leave_a_message": "Momentálne nie sme online. Prosíme, zanechajte správu.", + "yes": "Áno" + } } \ No newline at end of file diff --git a/packages/livechat/src/i18n/sl-SI.json b/packages/livechat/src/i18n/sl-SI.json index bb6735a6601d..172e77fafeaf 100644 --- a/packages/livechat/src/i18n/sl-SI.json +++ b/packages/livechat/src/i18n/sl-SI.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Prekliči", - "conversation_finished": "Pogovor končan", - "department_switched": "Oddelek zamenjan", - "no": "Ne", - "options": "Možnosti", - "send": "Pošlji", - "user_joined": "Uporabnik se je pridružil", - "user_left": "Uporabnik je odšel", - "we_are_not_online_right_now_please_leave_a_message": "Trenutno nismo na spletu. Prosim pustite sporočilo.", - "yes": "Da" - } + "translation": { + "cancel": "Prekliči", + "conversation_finished": "Pogovor končan", + "department_switched": "Oddelek zamenjan", + "no": "Ne", + "options": "Možnosti", + "send": "Pošlji", + "user_joined": "Uporabnik se je pridružil", + "user_left": "Uporabnik je odšel", + "we_are_not_online_right_now_please_leave_a_message": "Trenutno nismo na spletu. Prosim pustite sporočilo.", + "yes": "Da" + } } \ No newline at end of file diff --git a/packages/livechat/src/i18n/sq.json b/packages/livechat/src/i18n/sq.json index 05aa44a4d20d..a15c11592721 100644 --- a/packages/livechat/src/i18n/sq.json +++ b/packages/livechat/src/i18n/sq.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Anuloj", - "conversation_finished": "biseda përfunduar", - "department_switched": "Departamenti kaloi", - "no": "jo", - "options": "Opsione", - "send": "dërgoj", - "user_joined": "User bashkua", - "user_left": "majtë User", - "we_are_not_online_right_now_please_leave_a_message": "Ne nuk jemi në linjë tani. Lëreni një mesazh.", - "yes": "po" - } -} + "translation": { + "cancel": "Anuloj", + "conversation_finished": "biseda përfunduar", + "department_switched": "Departamenti kaloi", + "no": "jo", + "options": "Opsione", + "send": "dërgoj", + "user_joined": "User bashkua", + "user_left": "majtë User", + "we_are_not_online_right_now_please_leave_a_message": "Ne nuk jemi në linjë tani. Lëreni një mesazh.", + "yes": "po" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/sr.json b/packages/livechat/src/i18n/sr.json index ed23c3e4b9d1..f3683b89be76 100644 --- a/packages/livechat/src/i18n/sr.json +++ b/packages/livechat/src/i18n/sr.json @@ -1,75 +1,75 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "Желите ли да завршите овај разговор?", - "are_you_sure_you_want_to_remove_all_of_your_person": "Желите ли да уклонимо све Ваше податке?", - "are_you_sure_you_want_to_switch_the_department": "Желите ли да променити одељење?", - "cancel": "Отказати", - "change_department": "Промени одељење", - "change_department_1": "Промени одељење", - "chat_finished": "Разговор завршен", - "choose_a_department": "Изабери одељење...", - "choose_a_department_1": "Изабери одељење", - "choose_an_option": "Изабери опцију...", - "conversation_finished": "Разговор завршен", - "count_new_messages_since_since_one": "Једна нова порука од {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} порукe/a од {{val, datetime}}", - "department_switched": "Одељење је промењено", - "departments": "Одељења", - "disable_notifications": "Искључи обавештења", - "dismiss_this_alert": "Одбаци ово обавештење", - "drop_here_to_upload_a_file": "Превуци овде за слање датотеке", - "email": "E-пошта", - "enable_notifications": "Омогући обавештења", - "error_closing_chat": "Грешка приликом завршетка разговора.", - "error_removing_user_data": "Грешка приликом уклањања корисничких података.", - "error_starting_a_new_conversation_reason": "Грешка приликом започињања новог разговора: {{reason}}", - "expand_chat": "Прошири прозор разговора", - "file_exceeds_allowed_size_of_size": "Датотека превазилази дозвољену величину од {{size}}.", - "fileupload_error": "Греша пирликом слања датотеке", - "finish_this_chat": "Заврши овај разговор", - "forget_remove_my_data": "Уклони моје податке", - "go_to_menu_options_forget_remove_my_personal_data": "Идите на <1>menu options → Forget/Remove my personal data да захтевате уклањање Ваших података.", - "i_agree": "Слажем се", - "i_need_help_with": "Потребна ми је помоћ са...", - "if_you_have_any_other_questions_just_press_the_but": "Уколико имате других питања, само притисните дугме испод да започенете нови разговор.", - "leave_a_message": "Оставите поруку", - "livechat_connected": "Разговор уживо укљичен.", - "livechat_is_not_connected": "Разговор уживо није повезан.", - "media_types_not_accepted": "Тип датотеке није прихватљив.", - "message": "Порука", - "minimize_chat": "Смањи прозор за разговор", - "name": "Име", - "need_help": "Потребна Вам је помоћ?", - "new_chat": "Нови разговор", - "no": "Ne", - "no_available_agents_to_transfer": "Тренутно нема доступних оператера", - "ok": "У реду", - "options": "Опције", - "please_tell_us_some_information_to_start_the_chat": "Молим Вас, унесите следеће информације за почетак разговора", - "please_wait_for_the_next_available_agent": "Молим Вас, сачекајте првог слободног оператера...", - "powered_by_rocket_chat": "омогућио RocketChat", - "restore_chat": "Наставите разговор", - "room_name_changed": "Промена назива собе", - "send": "Послати", - "sound_is_off": "Звук је искључен", - "sound_is_on": "Звук је укљичен", - "start_chat": "Започни разговор", - "thanks_for_talking_with_us": "Захваљујемо на разговору!", - "the_controller_of_your_personal_data_is_company_na": "Вашим приватним подацима управља [Company Name], са седиштем на адреси [Company Address]. Када започнете разговор, прихватате да ће Ваши приватни подаци бити процесуирани и складиштени у складу са General Data Protection Regulation (GDPR).", - "type_your_message_here": "Укуцајте Вашу поруку овде", - "unread_messages": "Непрочитане поруке", - "user_added_by": "Корисник додат од стране", - "user_joined": "Корисник се придружио", - "user_left": "Корисник отишао", - "user_removed_by": "Корисник уклоњен од стране", - "waiting_queue": "Чекање у реду...", - "we_are_not_online_right_now_please_leave_a_message": "Тренутно нисмо на мрежи. Молимо оставите поруку.", - "welcome": "Добро дошли!", - "write_your_message": "Укуцајте Вашу поруку...", - "yes": "Да", - "you_browser_doesn_t_support_audio_element": "Ваш претраживач не подржава звучне елементе", - "you_browser_doesn_t_support_video_element": "Ваш претраживач не подржава видео елементе", - "your_spot_is_spot": "Ваше место је #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "Ваше место је #{{spot}} (Очекивано време чекања: {{estimatedWaitTime}})" - } -} + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "Желите ли да завршите овај разговор?", + "are_you_sure_you_want_to_remove_all_of_your_person": "Желите ли да уклонимо све Ваше податке?", + "are_you_sure_you_want_to_switch_the_department": "Желите ли да променити одељење?", + "cancel": "Отказати", + "change_department": "Промени одељење", + "change_department_1": "Промени одељење", + "chat_finished": "Разговор завршен", + "choose_a_department": "Изабери одељење...", + "choose_a_department_1": "Изабери одељење", + "choose_an_option": "Изабери опцију...", + "conversation_finished": "Разговор завршен", + "count_new_messages_since_since_one": "Једна нова порука од {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} порукe/a од {{val, datetime}}", + "department_switched": "Одељење је промењено", + "departments": "Одељења", + "disable_notifications": "Искључи обавештења", + "dismiss_this_alert": "Одбаци ово обавештење", + "drop_here_to_upload_a_file": "Превуци овде за слање датотеке", + "email": "E-пошта", + "enable_notifications": "Омогући обавештења", + "error_closing_chat": "Грешка приликом завршетка разговора.", + "error_removing_user_data": "Грешка приликом уклањања корисничких података.", + "error_starting_a_new_conversation_reason": "Грешка приликом започињања новог разговора: {{reason}}", + "expand_chat": "Прошири прозор разговора", + "file_exceeds_allowed_size_of_size": "Датотека превазилази дозвољену величину од {{size}}.", + "fileupload_error": "Греша пирликом слања датотеке", + "finish_this_chat": "Заврши овај разговор", + "forget_remove_my_data": "Уклони моје податке", + "go_to_menu_options_forget_remove_my_personal_data": "Идите на <1>menu options → Forget/Remove my personal data да захтевате уклањање Ваших података.", + "i_agree": "Слажем се", + "i_need_help_with": "Потребна ми је помоћ са...", + "if_you_have_any_other_questions_just_press_the_but": "Уколико имате других питања, само притисните дугме испод да започенете нови разговор.", + "leave_a_message": "Оставите поруку", + "livechat_connected": "Разговор уживо укљичен.", + "livechat_is_not_connected": "Разговор уживо није повезан.", + "media_types_not_accepted": "Тип датотеке није прихватљив.", + "message": "Порука", + "minimize_chat": "Смањи прозор за разговор", + "name": "Име", + "need_help": "Потребна Вам је помоћ?", + "new_chat": "Нови разговор", + "no": "Ne", + "no_available_agents_to_transfer": "Тренутно нема доступних оператера", + "ok": "У реду", + "options": "Опције", + "please_tell_us_some_information_to_start_the_chat": "Молим Вас, унесите следеће информације за почетак разговора", + "please_wait_for_the_next_available_agent": "Молим Вас, сачекајте првог слободног оператера...", + "powered_by_rocket_chat": "омогућио RocketChat", + "restore_chat": "Наставите разговор", + "room_name_changed": "Промена назива собе", + "send": "Послати", + "sound_is_off": "Звук је искључен", + "sound_is_on": "Звук је укљичен", + "start_chat": "Започни разговор", + "thanks_for_talking_with_us": "Захваљујемо на разговору!", + "the_controller_of_your_personal_data_is_company_na": "Вашим приватним подацима управља [Company Name], са седиштем на адреси [Company Address]. Када започнете разговор, прихватате да ће Ваши приватни подаци бити процесуирани и складиштени у складу са General Data Protection Regulation (GDPR).", + "type_your_message_here": "Укуцајте Вашу поруку овде", + "unread_messages": "Непрочитане поруке", + "user_added_by": "Корисник додат од стране", + "user_joined": "Корисник се придружио", + "user_left": "Корисник отишао", + "user_removed_by": "Корисник уклоњен од стране", + "waiting_queue": "Чекање у реду...", + "we_are_not_online_right_now_please_leave_a_message": "Тренутно нисмо на мрежи. Молимо оставите поруку.", + "welcome": "Добро дошли!", + "write_your_message": "Укуцајте Вашу поруку...", + "yes": "Да", + "you_browser_doesn_t_support_audio_element": "Ваш претраживач не подржава звучне елементе", + "you_browser_doesn_t_support_video_element": "Ваш претраживач не подржава видео елементе", + "your_spot_is_spot": "Ваше место је #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "Ваше место је #{{spot}} (Очекивано време чекања: {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/sv.json b/packages/livechat/src/i18n/sv.json index 3810b9a01071..d468b428f23c 100644 --- a/packages/livechat/src/i18n/sv.json +++ b/packages/livechat/src/i18n/sv.json @@ -1,101 +1,101 @@ { - "translation": { - "accept": "Acceptera", - "are_you_sure_you_want_to_finish_this_chat": "Är du säker på att du vill avsluta denna chatten?", - "are_you_sure_you_want_to_remove_all_of_your_person": "Är du säker på att du vill ta bort all din personliga data?", - "are_you_sure_you_want_to_switch_the_department": "Är du säker på att du vill byta avdelning?", - "call_end_time": "Samtal avslutades {{time, datetime}} - Varade {{callDuration, datetime}}", - "cancel": "Avbryt", - "change_department": "Byt avdelning", - "change_department_1": "Byt avdelning", - "chat_finished": "Chat avslutad", - "chat_now": "Chatta nu", - "chat_started": "Chat startad", - "choose_a_department": "Välj avdelning...", - "choose_a_department_1": "Välj avdelning", - "choose_an_option": "Välj ett alternativ", - "conversation_finished": "Konversation avslutad", - "count_new_messages_since_since_one": "Ett nytt meddelande sedan {{val, datetime}}", - "count_new_messages_since_since_other": "{{count}} nya meddelande sedan {{val, datetime}}", - "decline": "Neka", - "department_switched": "Avdelning bytt", - "departments": "Avdelningar", - "disable_notifications": "Inaktivera aviseringar", - "dismiss_this_alert": "Avvisa denna varning", - "drop_here_to_upload_a_file": "Släpp fil här för att ladda upp", - "email": "Email", - "enable_notifications": "Aktivera aviseringar", - "error_closing_chat": "Fel vid avslutande av chat.", - "error_getting_call_alert": "Fel vid samtalsavisering.", - "error_removing_user_data": "Fel vid borttagning av användardata.", - "error_starting_a_new_conversation_reason": "Fel vid start av konversation: {{reason}}", - "expand_chat": "Expandera chat", - "field_required": "Obligatoriskt fält", - "file_exceeds_allowed_size_of_size": "Filen överskrider tillåten storlek på {{size}}.", - "fileupload_error": "Fel vid uppladdning", - "finish_this_chat": "Avsluta chat", - "forget_remove_my_data": "Ta bort min data", - "from_returned_the_chat_to_the_queue": "{{from}} skickade tillbaka chatten till kön", - "the_agent_transferred_the_chat_to_the_department_to": "Agenten flyttade chatten till avdelning {{to}}", - "from_transferred_the_chat_to_the_department_to": "{{from}} flyttade chatten till avdelning {{to}}", - "from_transferred_the_chat_to_to": "{{from}} flyttade chatten till {{to}}", - "gdpr": "GDPR", - "go_to_menu_options_forget_remove_my_personal_data": "Gå till <1>meny inställningar → Ta bort min data för att begära omedelbar borttagning av din data.", - "hiddenelementscount_more": "+ {{hiddenElementsCount}} mer", - "i_agree": "Jag accepterar", - "i_need_help_with": "Jag behöver hjälp med...", - "if_you_have_any_other_questions_just_press_the_but": "Om du har fler frågor, tryck på knappen nedan för att starta en ny chat.", - "incoming_video_call": "Inkommande videosamtal", - "insert_your_field_here": "Infoga ditt {{field}} här...", - "invalid_email": "Ogiltig e-post", - "invalid_value": "Ogiltigt värde", - "join_call": "Anslut till samtal", - "join_my_room_to_start_the_video_call": "Anslut till mitt rum för att starta videosamtal.", - "leave_a_message": "Lämna ett meddelande", - "livechat_connected": "Livechat ansluten.", - "livechat_is_not_connected": "Livechat ej ansluten.", - "media_types_not_accepted": "Mediatypen accepteras inte.", - "message": "Meddelande", - "messages": "Meddelanden", - "message_separator_date": "{{val, datetime}}", - "message_time": "{{val, datetime}}", - "minimize_chat": "Minimera chat", - "name": "Namn", - "need_help": "Behöver du hjälp?", - "new_chat": "Ny chat", - "no": "Nej", - "no_available_agents_to_transfer": "Inga agenter tillgängliga för överföring.", - "ok": "OK", - "options": "Inställningar", - "please_tell_us_some_information_to_start_the_chat": "Vänligen ange information för att starta chat", - "please_wait_for_the_next_available_agent": "Vänligen vänta på nästa tillgängliga agent..", - "powered_by_rocket_chat": "Powered by Rocket.Chat", - "restore_chat": "Återställ chat", - "room_name_changed": "Rumsnamn ändrades", - "send": "Skicka", - "sound_is_off": "Ljud inaktiverat", - "sound_is_on": "Ljud aktiverat", - "start_chat": "Starta chat", - "thanks_for_talking_with_us": "Tack för att du kontaktar oss", - "the_chat_was_moved_back_to_queue": "Chatten flyttades tillbaka till kön", - "the_chat_was_transferred_to_another_agent": "Chatten flyttades till annan agent", - "the_controller_of_your_personal_data_is_company_na": "Den personuppgiftsansvarige för dina personuppgifter är [Företagsnamn], med säte på [Företagsadress]. För att starta chatten samtycker du till att dina personuppgifter behandlas och överförs i enlighet med den allmänna dataskyddsförordningen (GDPR).", - "transcript_success": "Transkription skickad", - "type_your_message_here": "Meddelande", - "unread_messages": "olästa meddelande", - "user_added_by": "Användare tillagd av", - "user_joined": "Användare ansluten", - "user_left": "Användare lämnade", - "user_removed_by": "Användare borttagen av", - "waiting_queue": "Kö...", - "we_are_not_online_right_now_please_leave_a_message": "Vi är inte online just nu. Lämna gärna ett medelande.", - "welcome": "Välkommen", - "would_you_like_a_copy_of_this_chat_emailed":"Vill du ha en kopia av chatten till din e-post?", - "write_your_message": "Skriv meddelande", - "yes": "Ja", - "you_browser_doesn_t_support_audio_element": "Din webbläsare stödjer inte ljudelement", - "you_browser_doesn_t_support_video_element": "Din webbläsare stödjer inte videoelement", - "your_spot_is_spot": "Din plats i kön är #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "Din plats i kön är #{{spot}} (Återstående kötid: {{estimatedWaitTime}})" - } -} + "translation": { + "accept": "Acceptera", + "are_you_sure_you_want_to_finish_this_chat": "Är du säker på att du vill avsluta denna chatten?", + "are_you_sure_you_want_to_remove_all_of_your_person": "Är du säker på att du vill ta bort all din personliga data?", + "are_you_sure_you_want_to_switch_the_department": "Är du säker på att du vill byta avdelning?", + "call_end_time": "Samtal avslutades {{time, datetime}} - Varade {{callDuration, datetime}}", + "cancel": "Avbryt", + "change_department": "Byt avdelning", + "change_department_1": "Byt avdelning", + "chat_finished": "Chat avslutad", + "chat_now": "Chatta nu", + "chat_started": "Chat startad", + "choose_a_department": "Välj avdelning...", + "choose_a_department_1": "Välj avdelning", + "choose_an_option": "Välj ett alternativ", + "conversation_finished": "Konversation avslutad", + "count_new_messages_since_since_one": "Ett nytt meddelande sedan {{val, datetime}}", + "count_new_messages_since_since_other": "{{count}} nya meddelande sedan {{val, datetime}}", + "decline": "Neka", + "department_switched": "Avdelning bytt", + "departments": "Avdelningar", + "disable_notifications": "Inaktivera aviseringar", + "dismiss_this_alert": "Avvisa denna varning", + "drop_here_to_upload_a_file": "Släpp fil här för att ladda upp", + "email": "Email", + "enable_notifications": "Aktivera aviseringar", + "error_closing_chat": "Fel vid avslutande av chat.", + "error_getting_call_alert": "Fel vid samtalsavisering.", + "error_removing_user_data": "Fel vid borttagning av användardata.", + "error_starting_a_new_conversation_reason": "Fel vid start av konversation: {{reason}}", + "expand_chat": "Expandera chat", + "field_required": "Obligatoriskt fält", + "file_exceeds_allowed_size_of_size": "Filen överskrider tillåten storlek på {{size}}.", + "fileupload_error": "Fel vid uppladdning", + "finish_this_chat": "Avsluta chat", + "forget_remove_my_data": "Ta bort min data", + "from_returned_the_chat_to_the_queue": "{{from}} skickade tillbaka chatten till kön", + "the_agent_transferred_the_chat_to_the_department_to": "Agenten flyttade chatten till avdelning {{to}}", + "from_transferred_the_chat_to_the_department_to": "{{from}} flyttade chatten till avdelning {{to}}", + "from_transferred_the_chat_to_to": "{{from}} flyttade chatten till {{to}}", + "gdpr": "GDPR", + "go_to_menu_options_forget_remove_my_personal_data": "Gå till <1>meny inställningar → Ta bort min data för att begära omedelbar borttagning av din data.", + "hiddenelementscount_more": "+ {{hiddenElementsCount}} mer", + "i_agree": "Jag accepterar", + "i_need_help_with": "Jag behöver hjälp med...", + "if_you_have_any_other_questions_just_press_the_but": "Om du har fler frågor, tryck på knappen nedan för att starta en ny chat.", + "incoming_video_call": "Inkommande videosamtal", + "insert_your_field_here": "Infoga ditt {{field}} här...", + "invalid_email": "Ogiltig e-post", + "invalid_value": "Ogiltigt värde", + "join_call": "Anslut till samtal", + "join_my_room_to_start_the_video_call": "Anslut till mitt rum för att starta videosamtal.", + "leave_a_message": "Lämna ett meddelande", + "livechat_connected": "Livechat ansluten.", + "livechat_is_not_connected": "Livechat ej ansluten.", + "media_types_not_accepted": "Mediatypen accepteras inte.", + "message": "Meddelande", + "messages": "Meddelanden", + "message_separator_date": "{{val, datetime}}", + "message_time": "{{val, datetime}}", + "minimize_chat": "Minimera chat", + "name": "Namn", + "need_help": "Behöver du hjälp?", + "new_chat": "Ny chat", + "no": "Nej", + "no_available_agents_to_transfer": "Inga agenter tillgängliga för överföring.", + "ok": "OK", + "options": "Inställningar", + "please_tell_us_some_information_to_start_the_chat": "Vänligen ange information för att starta chat", + "please_wait_for_the_next_available_agent": "Vänligen vänta på nästa tillgängliga agent..", + "powered_by_rocket_chat": "Powered by Rocket.Chat", + "restore_chat": "Återställ chat", + "room_name_changed": "Rumsnamn ändrades", + "send": "Skicka", + "sound_is_off": "Ljud inaktiverat", + "sound_is_on": "Ljud aktiverat", + "start_chat": "Starta chat", + "thanks_for_talking_with_us": "Tack för att du kontaktar oss", + "the_chat_was_moved_back_to_queue": "Chatten flyttades tillbaka till kön", + "the_chat_was_transferred_to_another_agent": "Chatten flyttades till annan agent", + "the_controller_of_your_personal_data_is_company_na": "Den personuppgiftsansvarige för dina personuppgifter är [Företagsnamn], med säte på [Företagsadress]. För att starta chatten samtycker du till att dina personuppgifter behandlas och överförs i enlighet med den allmänna dataskyddsförordningen (GDPR).", + "transcript_success": "Transkription skickad", + "type_your_message_here": "Meddelande", + "unread_messages": "olästa meddelande", + "user_added_by": "Användare tillagd av", + "user_joined": "Användare ansluten", + "user_left": "Användare lämnade", + "user_removed_by": "Användare borttagen av", + "waiting_queue": "Kö...", + "we_are_not_online_right_now_please_leave_a_message": "Vi är inte online just nu. Lämna gärna ett medelande.", + "welcome": "Välkommen", + "would_you_like_a_copy_of_this_chat_emailed": "Vill du ha en kopia av chatten till din e-post?", + "write_your_message": "Skriv meddelande", + "yes": "Ja", + "you_browser_doesn_t_support_audio_element": "Din webbläsare stödjer inte ljudelement", + "you_browser_doesn_t_support_video_element": "Din webbläsare stödjer inte videoelement", + "your_spot_is_spot": "Din plats i kön är #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "Din plats i kön är #{{spot}} (Återstående kötid: {{estimatedWaitTime}})" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/ta-IN.json b/packages/livechat/src/i18n/ta-IN.json index 0b76e70237bc..494101e3a9d0 100644 --- a/packages/livechat/src/i18n/ta-IN.json +++ b/packages/livechat/src/i18n/ta-IN.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "ரத்து", - "conversation_finished": "உரையாடலை முடித்தேன்", - "department_switched": "திணைக்களம் மாறியது", - "no": "இல்லை", - "options": "விருப்பங்கள்", - "send": "அனுப்பு", - "user_joined": "பயனர் சேர்ந்தார்", - "user_left": "பயனர் இடது", - "we_are_not_online_right_now_please_leave_a_message": "இப்போது நாங்கள் ஆன்லைனில் இல்லை. தயவுசெய்து, ஒரு செய்தியை விடு.", - "yes": "ஆமாம்" - } + "translation": { + "cancel": "ரத்து", + "conversation_finished": "உரையாடலை முடித்தேன்", + "department_switched": "திணைக்களம் மாறியது", + "no": "இல்லை", + "options": "விருப்பங்கள்", + "send": "அனுப்பு", + "user_joined": "பயனர் சேர்ந்தார்", + "user_left": "பயனர் இடது", + "we_are_not_online_right_now_please_leave_a_message": "இப்போது நாங்கள் ஆன்லைனில் இல்லை. தயவுசெய்து, ஒரு செய்தியை விடு.", + "yes": "ஆமாம்" + } } \ No newline at end of file diff --git a/packages/livechat/src/i18n/th-TH.json b/packages/livechat/src/i18n/th-TH.json index d7beceac1bc6..76860dbef9ab 100644 --- a/packages/livechat/src/i18n/th-TH.json +++ b/packages/livechat/src/i18n/th-TH.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "ยกเลิก", - "conversation_finished": "สนทนาเสร็จแล้ว", - "department_switched": "แผนกเปลี่ยนแล้ว", - "no": "ไม่", - "options": "ตัวเลือก", - "send": "ส่ง", - "user_joined": "ผู้ใช้เข้าร่วม", - "user_left": "เหลือผู้ใช้แล้ว", - "we_are_not_online_right_now_please_leave_a_message": "เรายังไม่ออนไลน์ในขณะนี้ กรุณาฝากข้อความไว้", - "yes": "ใช่" - } + "translation": { + "cancel": "ยกเลิก", + "conversation_finished": "สนทนาเสร็จแล้ว", + "department_switched": "แผนกเปลี่ยนแล้ว", + "no": "ไม่", + "options": "ตัวเลือก", + "send": "ส่ง", + "user_joined": "ผู้ใช้เข้าร่วม", + "user_left": "เหลือผู้ใช้แล้ว", + "we_are_not_online_right_now_please_leave_a_message": "เรายังไม่ออนไลน์ในขณะนี้ กรุณาฝากข้อความไว้", + "yes": "ใช่" + } } \ No newline at end of file diff --git a/packages/livechat/src/i18n/tr.json b/packages/livechat/src/i18n/tr.json index ac40533a2fc1..c7e786e18e67 100644 --- a/packages/livechat/src/i18n/tr.json +++ b/packages/livechat/src/i18n/tr.json @@ -1,15 +1,15 @@ { - "translation": { - "are_you_sure_you_want_to_switch_the_department": "Departmanı değiştirmek istediğinden emin misin?", - "cancel": "Vazgeç", - "conversation_finished": "Konuşma bitti", - "department_switched": "Bölüm değiştirildi", - "no": "Hayır", - "options": "Seçenekler", - "send": "Gönder", - "user_joined": "Kullanıcı katıldı", - "user_left": "Kullanıcı ayrıldı", - "we_are_not_online_right_now_please_leave_a_message": "Şu anda çevrimiçi değiliz. Lütfen mesaj bırakın.", - "yes": "Evet" - } -} + "translation": { + "are_you_sure_you_want_to_switch_the_department": "Departmanı değiştirmek istediğinden emin misin?", + "cancel": "Vazgeç", + "conversation_finished": "Konuşma bitti", + "department_switched": "Bölüm değiştirildi", + "no": "Hayır", + "options": "Seçenekler", + "send": "Gönder", + "user_joined": "Kullanıcı katıldı", + "user_left": "Kullanıcı ayrıldı", + "we_are_not_online_right_now_please_leave_a_message": "Şu anda çevrimiçi değiliz. Lütfen mesaj bırakın.", + "yes": "Evet" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/ug.json b/packages/livechat/src/i18n/ug.json index 73fe02da920d..839e7552ef6a 100644 --- a/packages/livechat/src/i18n/ug.json +++ b/packages/livechat/src/i18n/ug.json @@ -1,8 +1,8 @@ { - "translation": { - "conversation_finished": "سۆھبەتلىشىش ئاخىرلاشتى", - "send": "يوللاش", - "user_joined": "ئەزا ئاللىبۇرۇن قېتىلدى", - "user_left": "ئەزا ئاللىبۇرۇن ئايرىلدى" - } -} + "translation": { + "conversation_finished": "سۆھبەتلىشىش ئاخىرلاشتى", + "send": "يوللاش", + "user_joined": "ئەزا ئاللىبۇرۇن قېتىلدى", + "user_left": "ئەزا ئاللىبۇرۇن ئايرىلدى" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/uk.json b/packages/livechat/src/i18n/uk.json index 03e268cdd239..f78a67f57271 100644 --- a/packages/livechat/src/i18n/uk.json +++ b/packages/livechat/src/i18n/uk.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "Скасувати", - "conversation_finished": "Розмову закінчено", - "department_switched": "Відділ змінено", - "no": "Ні", - "options": "Параметри", - "send": "Надіслати", - "user_joined": "Користувач приєднався", - "user_left": "Користувач вийшов", - "we_are_not_online_right_now_please_leave_a_message": "Ми зараз не в мережі. Будь ласка, залиште повідомлення.", - "yes": "Так" - } -} + "translation": { + "cancel": "Скасувати", + "conversation_finished": "Розмову закінчено", + "department_switched": "Відділ змінено", + "no": "Ні", + "options": "Параметри", + "send": "Надіслати", + "user_joined": "Користувач приєднався", + "user_left": "Користувач вийшов", + "we_are_not_online_right_now_please_leave_a_message": "Ми зараз не в мережі. Будь ласка, залиште повідомлення.", + "yes": "Так" + } +} \ No newline at end of file diff --git a/packages/livechat/src/i18n/vi-VN.json b/packages/livechat/src/i18n/vi-VN.json index e14935eaa866..24d1b70da3f2 100644 --- a/packages/livechat/src/i18n/vi-VN.json +++ b/packages/livechat/src/i18n/vi-VN.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "hủy bỏ", - "conversation_finished": "Trò chuyện kết thúc", - "department_switched": "Sở chuyển", - "no": "Không", - "options": "Tùy chọn", - "send": "Gửi", - "user_joined": "Người dùng tham gia", - "user_left": "Người dùng còn lại", - "we_are_not_online_right_now_please_leave_a_message": "Chúng tôi không trực tuyến ngay bây giờ. Xin vui lòng, để lại tin nhắn.", - "yes": "Vâng" - } + "translation": { + "cancel": "hủy bỏ", + "conversation_finished": "Trò chuyện kết thúc", + "department_switched": "Sở chuyển", + "no": "Không", + "options": "Tùy chọn", + "send": "Gửi", + "user_joined": "Người dùng tham gia", + "user_left": "Người dùng còn lại", + "we_are_not_online_right_now_please_leave_a_message": "Chúng tôi không trực tuyến ngay bây giờ. Xin vui lòng, để lại tin nhắn.", + "yes": "Vâng" + } } \ No newline at end of file diff --git a/packages/livechat/src/i18n/zh-HK.json b/packages/livechat/src/i18n/zh-HK.json index 25bc37b1d786..1e632d6b1927 100644 --- a/packages/livechat/src/i18n/zh-HK.json +++ b/packages/livechat/src/i18n/zh-HK.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "取消", - "conversation_finished": "對話已結束", - "department_switched": "部门切换", - "no": "否", - "options": "選項", - "send": "傳送", - "user_joined": "用户加入", - "user_left": "用户离开了", - "we_are_not_online_right_now_please_leave_a_message": "我们现在不在线。请留言。", - "yes": "对" - } + "translation": { + "cancel": "取消", + "conversation_finished": "對話已結束", + "department_switched": "部门切换", + "no": "否", + "options": "選項", + "send": "傳送", + "user_joined": "用户加入", + "user_left": "用户离开了", + "we_are_not_online_right_now_please_leave_a_message": "我们现在不在线。请留言。", + "yes": "对" + } } \ No newline at end of file diff --git a/packages/livechat/src/i18n/zh-TW.json b/packages/livechat/src/i18n/zh-TW.json index 3d83df1253d4..368868a897e4 100644 --- a/packages/livechat/src/i18n/zh-TW.json +++ b/packages/livechat/src/i18n/zh-TW.json @@ -1,14 +1,14 @@ { - "translation": { - "cancel": "取消", - "conversation_finished": "對話已結束", - "department_switched": "部門已更換", - "no": "否", - "options": "選項", - "send": "傳送", - "user_joined": "使用者已加入", - "user_left": "使用者已離開", - "we_are_not_online_right_now_please_leave_a_message": "我們現在不在線。請留言。", - "yes": "是" - } + "translation": { + "cancel": "取消", + "conversation_finished": "對話已結束", + "department_switched": "部門已更換", + "no": "否", + "options": "選項", + "send": "傳送", + "user_joined": "使用者已加入", + "user_left": "使用者已離開", + "we_are_not_online_right_now_please_leave_a_message": "我們現在不在線。請留言。", + "yes": "是" + } } \ No newline at end of file diff --git a/packages/livechat/src/i18n/zh.json b/packages/livechat/src/i18n/zh.json index c7cec1304838..0ce60b9cfbbe 100644 --- a/packages/livechat/src/i18n/zh.json +++ b/packages/livechat/src/i18n/zh.json @@ -1,78 +1,78 @@ { - "translation": { - "are_you_sure_you_want_to_finish_this_chat": "您确定要离开聊天吗?", - "are_you_sure_you_want_to_remove_all_of_your_person": "您确定要删除所有个人资料吗?", - "are_you_sure_you_want_to_switch_the_department": "你确定要切换部门吗?", - "cancel": "取消", - "change_department": "变更部门", - "change_department_1": "变更部门", - "chat_finished": "聊天结束", - "choose_a_department": "选择部门...", - "choose_a_department_1": "选择部门", - "choose_an_option": "请选择...", - "conversation_finished": "会话已结束", - "count_new_messages_since_since_one": "有一个来自 {{val, datetime}} 的新信息 ", - "count_new_messages_since_since_other": "{{count}} 个来自 {{val, datetime}} 的新信息", - "department_switched": "部门切换", - "departments": "部门", - "disable_notifications": "关闭通知", - "dismiss_this_alert": "取消此提醒", - "drop_here_to_upload_a_file": "拽文件到这里上传", - "email": "电子邮件", - "enable_notifications": "开启通知", - "error_closing_chat": "关闭聊天窗口错误.", - "error_removing_user_data": "移除用户资料错误.", - "error_starting_a_new_conversation_reason": "开始新对话时错误: {{reason}}", - "expand_chat": "展开聊天窗口", - "field_required": "此字段必填", - "file_exceeds_allowed_size_of_size": "文件大小超过允许的限制 {{size}}.", - "fileupload_error": "文件上传错误", - "finish_this_chat": "结束聊天", - "forget_remove_my_data": "移除我的资料", - "go_to_menu_options_forget_remove_my_personal_data": "前往 <1>选项 → 移除我的资料 请求立即删除您的数据.", - "hiddenelementscount_more": "+ {{hiddenElementsCount}} 更多", - "i_agree": "我同意", - "i_need_help_with": "我需要帮助...", - "if_you_have_any_other_questions_just_press_the_but": "如有其他疑问,请按下面的按钮开始新的聊天.", - "insert_your_field_here": "请在此输入 {{field}} ...", - "invalid_email": "无效的电子邮件", - "invalid_value": "无效的内容", - "leave_a_message": "留言", - "livechat_connected": "聊天已连线", - "livechat_is_not_connected": "聊天未连线", - "media_types_not_accepted": "不允许的挡案类型", - "message": "信息", - "minimize_chat": "最小化聊天窗口", - "name": "名字", - "need_help": "需要帮忙吗?", - "new_chat": "开启新聊天", - "no": "否", - "no_available_agents_to_transfer": "目前没有其他客服在线上,请稍等", - "ok": "好", - "options": "选项", - "please_tell_us_some_information_to_start_the_chat": "请输入信息来开启聊天", - "please_wait_for_the_next_available_agent": "请稍等其他客服..", - "restore_chat": "恢复聊天", - "room_name_changed": "聊天房间名称改变", - "send": "发送", - "sound_is_off": "声音关闭", - "sound_is_on": "声音开启", - "start_chat": "开始聊天", - "thanks_for_talking_with_us": "谢谢与我们联系", - "the_controller_of_your_personal_data_is_company_na": "您的个人数据的由 [Company Name] 使用, 注册办公室位于 [Company Address]. 要开始聊天,您同意应按照通用数据保护条例(GDPR)处理和传输您的个人数据.", - "type_your_message_here": "请在此输入您的信息", - "unread_messages": "未读信息", - "user_joined": "用户已加入", - "user_left": "用户已离开", - "user_removed_by": "用户已被移除", - "waiting_queue": "等待中", - "we_are_not_online_right_now_please_leave_a_message": "我们现在不在线。请留言。", - "welcome": "欢迎", - "write_your_message": "请输入您的信息...", - "yes": "是", - "you_browser_doesn_t_support_audio_element": "您的浏览器不支援音频", - "you_browser_doesn_t_support_video_element": "您的浏览器不支援视频", - "your_spot_is_spot": "您的等待位置是 #{{spot}}", - "your_spot_is_spot_estimated_wait_time_estimatedwai": "您的等待位置是 #{{spot}} (预估等待时间: {{estimatedWaitTime}})" - } -} + "translation": { + "are_you_sure_you_want_to_finish_this_chat": "您确定要离开聊天吗?", + "are_you_sure_you_want_to_remove_all_of_your_person": "您确定要删除所有个人资料吗?", + "are_you_sure_you_want_to_switch_the_department": "你确定要切换部门吗?", + "cancel": "取消", + "change_department": "变更部门", + "change_department_1": "变更部门", + "chat_finished": "聊天结束", + "choose_a_department": "选择部门...", + "choose_a_department_1": "选择部门", + "choose_an_option": "请选择...", + "conversation_finished": "会话已结束", + "count_new_messages_since_since_one": "有一个来自 {{val, datetime}} 的新信息 ", + "count_new_messages_since_since_other": "{{count}} 个来自 {{val, datetime}} 的新信息", + "department_switched": "部门切换", + "departments": "部门", + "disable_notifications": "关闭通知", + "dismiss_this_alert": "取消此提醒", + "drop_here_to_upload_a_file": "拽文件到这里上传", + "email": "电子邮件", + "enable_notifications": "开启通知", + "error_closing_chat": "关闭聊天窗口错误.", + "error_removing_user_data": "移除用户资料错误.", + "error_starting_a_new_conversation_reason": "开始新对话时错误: {{reason}}", + "expand_chat": "展开聊天窗口", + "field_required": "此字段必填", + "file_exceeds_allowed_size_of_size": "文件大小超过允许的限制 {{size}}.", + "fileupload_error": "文件上传错误", + "finish_this_chat": "结束聊天", + "forget_remove_my_data": "移除我的资料", + "go_to_menu_options_forget_remove_my_personal_data": "前往 <1>选项 → 移除我的资料 请求立即删除您的数据.", + "hiddenelementscount_more": "+ {{hiddenElementsCount}} 更多", + "i_agree": "我同意", + "i_need_help_with": "我需要帮助...", + "if_you_have_any_other_questions_just_press_the_but": "如有其他疑问,请按下面的按钮开始新的聊天.", + "insert_your_field_here": "请在此输入 {{field}} ...", + "invalid_email": "无效的电子邮件", + "invalid_value": "无效的内容", + "leave_a_message": "留言", + "livechat_connected": "聊天已连线", + "livechat_is_not_connected": "聊天未连线", + "media_types_not_accepted": "不允许的挡案类型", + "message": "信息", + "minimize_chat": "最小化聊天窗口", + "name": "名字", + "need_help": "需要帮忙吗?", + "new_chat": "开启新聊天", + "no": "否", + "no_available_agents_to_transfer": "目前没有其他客服在线上,请稍等", + "ok": "好", + "options": "选项", + "please_tell_us_some_information_to_start_the_chat": "请输入信息来开启聊天", + "please_wait_for_the_next_available_agent": "请稍等其他客服..", + "restore_chat": "恢复聊天", + "room_name_changed": "聊天房间名称改变", + "send": "发送", + "sound_is_off": "声音关闭", + "sound_is_on": "声音开启", + "start_chat": "开始聊天", + "thanks_for_talking_with_us": "谢谢与我们联系", + "the_controller_of_your_personal_data_is_company_na": "您的个人数据的由 [Company Name] 使用, 注册办公室位于 [Company Address]. 要开始聊天,您同意应按照通用数据保护条例(GDPR)处理和传输您的个人数据.", + "type_your_message_here": "请在此输入您的信息", + "unread_messages": "未读信息", + "user_joined": "用户已加入", + "user_left": "用户已离开", + "user_removed_by": "用户已被移除", + "waiting_queue": "等待中", + "we_are_not_online_right_now_please_leave_a_message": "我们现在不在线。请留言。", + "welcome": "欢迎", + "write_your_message": "请输入您的信息...", + "yes": "是", + "you_browser_doesn_t_support_audio_element": "您的浏览器不支援音频", + "you_browser_doesn_t_support_video_element": "您的浏览器不支援视频", + "your_spot_is_spot": "您的等待位置是 #{{spot}}", + "your_spot_is_spot_estimated_wait_time_estimatedwai": "您的等待位置是 #{{spot}} (预估等待时间: {{estimatedWaitTime}})" + } +} \ No newline at end of file From d92c0c7fab70536e8d2af12cd0719e24db46478f Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Thu, 29 Feb 2024 10:13:13 -0300 Subject: [PATCH 075/207] chore: Refactor & Improve Livechat Widget API (#30924) --- .changeset/orange-dragons-fly.md | 6 + packages/livechat/.storybook/helpers.tsx | 5 +- packages/livechat/src/components/App/App.tsx | 128 +---- .../src/components/Screen/ScreenProvider.tsx | 142 +++++ .../livechat/src/components/Screen/index.js | 143 +++-- packages/livechat/src/definitions/agents.d.ts | 2 + packages/livechat/src/definitions/global.d.ts | 42 ++ packages/livechat/src/definitions/guest.d.ts | 6 + .../livechat/src/helpers/canRenderMessage.ts | 5 +- packages/livechat/src/helpers/formatAgent.ts | 32 ++ packages/livechat/src/helpers/isDefined.ts | 3 + .../livechat/src/lib/{hooks.js => hooks.ts} | 130 +++-- packages/livechat/src/lib/parentCall.ts | 6 +- packages/livechat/src/lib/room.js | 10 +- packages/livechat/src/lib/triggers.js | 2 +- .../livechat/src/routes/Chat/connector.tsx | 89 +-- .../livechat/src/routes/Chat/container.js | 119 +--- packages/livechat/src/routes/Chat/stories.tsx | 3 +- .../src/routes/ChatFinished/component.js | 38 -- .../src/routes/ChatFinished/component.tsx | 42 ++ .../src/routes/ChatFinished/container.js | 38 -- .../src/routes/ChatFinished/container.tsx | 24 + .../src/routes/ChatFinished/stories.tsx | 3 +- .../{component.js => component.tsx} | 26 +- .../src/routes/GDPRAgreement/container.js | 41 -- .../src/routes/GDPRAgreement/container.tsx | 21 + .../src/routes/GDPRAgreement/stories.tsx | 3 +- .../src/routes/LeaveMessage/index.tsx | 17 +- .../livechat/src/routes/Register/index.tsx | 29 +- .../livechat/src/routes/Register/stories.tsx | 5 +- .../src/routes/SwitchDepartment/index.tsx | 14 +- .../src/routes/SwitchDepartment/stories.tsx | 3 +- .../{component.js => component.tsx} | 32 +- .../src/routes/TriggerMessage/container.js | 67 --- .../src/routes/TriggerMessage/container.tsx | 39 ++ .../src/routes/TriggerMessage/index.ts | 2 +- .../src/routes/TriggerMessage/stories.tsx | 3 +- .../src/routes/TriggerMessage/styles.scss | 2 +- packages/livechat/src/store/index.tsx | 73 ++- .../livechat/src/{widget.js => widget.ts} | 526 +++++++++++------- packages/livechat/webpack.config.ts | 2 +- 41 files changed, 1016 insertions(+), 907 deletions(-) create mode 100644 .changeset/orange-dragons-fly.md create mode 100644 packages/livechat/src/components/Screen/ScreenProvider.tsx create mode 100644 packages/livechat/src/definitions/guest.d.ts create mode 100644 packages/livechat/src/helpers/formatAgent.ts create mode 100644 packages/livechat/src/helpers/isDefined.ts rename packages/livechat/src/lib/{hooks.js => hooks.ts} (56%) delete mode 100644 packages/livechat/src/routes/ChatFinished/component.js create mode 100644 packages/livechat/src/routes/ChatFinished/component.tsx delete mode 100644 packages/livechat/src/routes/ChatFinished/container.js create mode 100644 packages/livechat/src/routes/ChatFinished/container.tsx rename packages/livechat/src/routes/GDPRAgreement/{component.js => component.tsx} (80%) delete mode 100644 packages/livechat/src/routes/GDPRAgreement/container.js create mode 100644 packages/livechat/src/routes/GDPRAgreement/container.tsx rename packages/livechat/src/routes/TriggerMessage/{component.js => component.tsx} (55%) delete mode 100644 packages/livechat/src/routes/TriggerMessage/container.js create mode 100644 packages/livechat/src/routes/TriggerMessage/container.tsx rename packages/livechat/src/{widget.js => widget.ts} (53%) diff --git a/.changeset/orange-dragons-fly.md b/.changeset/orange-dragons-fly.md new file mode 100644 index 000000000000..a1ca0faa90b6 --- /dev/null +++ b/.changeset/orange-dragons-fly.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/livechat": minor +--- + +chore: Refactor & Improve Livechat Widget API +Refactors and adds better error handling to the widget's API calls diff --git a/packages/livechat/.storybook/helpers.tsx b/packages/livechat/.storybook/helpers.tsx index 12c896dccf55..82d6c5bf74fa 100644 --- a/packages/livechat/.storybook/helpers.tsx +++ b/packages/livechat/.storybook/helpers.tsx @@ -3,12 +3,15 @@ import { type DecoratorFunction } from '@storybook/csf'; import type { Args, PreactFramework } from '@storybook/preact'; import { loremIpsum as originalLoremIpsum } from 'lorem-ipsum'; +import { ScreenContext } from '../src/components/Screen/ScreenProvider'; import gazzoAvatar from './assets/gazzo.jpg'; import martinAvatar from './assets/martin.jpg'; import tassoAvatar from './assets/tasso.jpg'; export const screenDecorator: DecoratorFunction = (storyFn) => ( -
          {storyFn()}
          +
          + {storyFn()} +
          ); export const screenProps = () => ({ diff --git a/packages/livechat/src/components/App/App.tsx b/packages/livechat/src/components/App/App.tsx index cfaa52b94999..685b781cb3e3 100644 --- a/packages/livechat/src/components/App/App.tsx +++ b/packages/livechat/src/components/App/App.tsx @@ -2,12 +2,10 @@ import type { ILivechatTrigger } from '@rocket.chat/core-typings'; import i18next from 'i18next'; import { Component } from 'preact'; import Router, { route } from 'preact-router'; -import { parse } from 'query-string'; import { withTranslation } from 'react-i18next'; import type { Department } from '../../definitions/departments'; import { setInitCookies } from '../../helpers/cookies'; -import { isActiveSession } from '../../helpers/isActiveSession'; import { isRTL } from '../../helpers/isRTL'; import { visibility } from '../../helpers/visibility'; import history from '../../history'; @@ -25,7 +23,7 @@ import Register from '../../routes/Register'; import SwitchDepartment from '../../routes/SwitchDepartment'; import TriggerMessage from '../../routes/TriggerMessage'; import type { Dispatch } from '../../store'; -import store from '../../store'; +import { ScreenProvider } from '../Screen/ScreenProvider'; type AppProps = { config: { @@ -75,26 +73,6 @@ type AppState = { poppedOut: boolean; }; -export type ScreenPropsType = { - notificationsEnabled: boolean; - minimized: boolean; - expanded: boolean; - windowed: boolean; - sound: unknown; - alerts: unknown; - modal: unknown; - nameDefault: string; - emailDefault: string; - departmentDefault: string; - onEnableNotifications: () => unknown; - onDisableNotifications: () => unknown; - onMinimize: () => unknown; - onRestore: () => unknown; - onOpenWindow: () => unknown; - onDismissAlert: () => unknown; - dismissNotification: () => void; -}; - export class App extends Component { state = { initialized: false, @@ -150,49 +128,6 @@ export class App extends Component { Triggers.processTriggers(); } - protected handleEnableNotifications = () => { - const { dispatch, sound = {} } = this.props; - dispatch({ sound: { ...sound, enabled: true } }); - }; - - protected handleDisableNotifications = () => { - const { dispatch, sound = {} } = this.props; - dispatch({ sound: { ...sound, enabled: false } }); - }; - - protected handleMinimize = () => { - parentCall('minimizeWindow'); - const { dispatch } = this.props; - dispatch({ minimized: true }); - }; - - protected handleRestore = () => { - parentCall('restoreWindow'); - const { dispatch, undocked } = this.props; - const dispatchRestore = () => dispatch({ minimized: false, undocked: false }); - const dispatchEvent = () => { - dispatchRestore(); - store.off('storageSynced', dispatchEvent); - }; - if (undocked) { - store.on('storageSynced', dispatchEvent); - } else { - dispatchRestore(); - } - Triggers.callbacks?.emit('chat-opened-by-visitor'); - }; - - protected handleOpenWindow = () => { - parentCall('openPopout'); - const { dispatch } = this.props; - dispatch({ undocked: true, minimized: false }); - }; - - protected handleDismissAlert = (id: string) => { - const { dispatch, alerts = [] } = this.props; - dispatch({ alerts: alerts.filter((alert) => alert.id !== id) }); - }; - protected handleVisibilityChange = async () => { const { dispatch } = this.props; dispatch({ visible: !visibility.hidden }); @@ -202,19 +137,20 @@ export class App extends Component { this.forceUpdate(); }; - protected dismissNotification = () => !isActiveSession(); - protected initWidget() { const { minimized, iframe: { visible }, dispatch, } = this.props; + parentCall(minimized ? 'minimizeWindow' : 'restoreWindow'); parentCall(visible ? 'showWidget' : 'hideWidget'); visibility.addListener(this.handleVisibilityChange); + this.handleVisibilityChange(); + window.addEventListener('beforeunload', () => { visibility.removeListener(this.handleVisibilityChange); dispatch({ minimized: true, undocked: false }); @@ -223,16 +159,6 @@ export class App extends Component { i18next.on('languageChanged', this.handleLanguageChange); } - protected checkPoppedOutWindow() { - // Checking if the window is poppedOut and setting parent minimized if yes for the restore purpose - const { dispatch } = this.props; - const poppedOut = parse(window.location.search).mode === 'popout'; - this.setState({ poppedOut }); - if (poppedOut) { - dispatch({ minimized: false }); - } - } - protected async initialize() { // TODO: split these behaviors into composable components await Connection.init(); @@ -241,7 +167,6 @@ export class App extends Component { Hooks.init(); this.handleTriggers(); this.initWidget(); - this.checkPoppedOutWindow(); this.setState({ initialized: true }); parentCall('ready'); } @@ -268,44 +193,23 @@ export class App extends Component { } } - render = ({ sound, undocked, minimized, expanded, alerts, modal, iframe }: AppProps, { initialized, poppedOut }: AppState) => { + render = (_: AppProps, { initialized }: AppState) => { if (!initialized) { return null; } - const { department, name, email } = iframe.guest || {}; - - const screenProps = { - notificationsEnabled: sound?.enabled, - minimized: !poppedOut && (minimized || undocked), - expanded: !minimized && expanded, - windowed: !minimized && poppedOut, - sound, - alerts, - modal, - nameDefault: name, - emailDefault: email, - departmentDefault: department, - onEnableNotifications: this.handleEnableNotifications, - onDisableNotifications: this.handleDisableNotifications, - onMinimize: this.handleMinimize, - onRestore: this.handleRestore, - onOpenWindow: this.handleOpenWindow, - onDismissAlert: this.handleDismissAlert, - dismissNotification: this.dismissNotification, - }; - return ( - - - - - {/* TODO: Find a better way to avoid prop drilling with that amout of props (perhaps create a screen context/provider) */} - - - - - + + + + + + + + + + + ); }; } diff --git a/packages/livechat/src/components/Screen/ScreenProvider.tsx b/packages/livechat/src/components/Screen/ScreenProvider.tsx new file mode 100644 index 000000000000..8243879f2549 --- /dev/null +++ b/packages/livechat/src/components/Screen/ScreenProvider.tsx @@ -0,0 +1,142 @@ +import type { FunctionalComponent } from 'preact'; +import { createContext } from 'preact'; +import { useCallback, useContext, useEffect, useState } from 'preact/hooks'; +import { parse } from 'query-string'; + +import { isActiveSession } from '../../helpers/isActiveSession'; +import { parentCall } from '../../lib/parentCall'; +import Triggers from '../../lib/triggers'; +import store, { StoreContext } from '../../store'; + +export type ScreenContextValue = { + notificationsEnabled: boolean; + minimized: boolean; + expanded: boolean; + windowed: boolean; + sound: unknown; + alerts: unknown; + modal: unknown; + nameDefault: string; + emailDefault: string; + departmentDefault: string; + onEnableNotifications: () => unknown; + onDisableNotifications: () => unknown; + onMinimize: () => unknown; + onRestore: () => unknown; + onOpenWindow: () => unknown; + onDismissAlert: () => unknown; + dismissNotification: () => void; + theme?: { + color: string; + fontColor: string; + iconColor: string; + }; +}; + +export const ScreenContext = createContext({ + theme: { + color: '', + fontColor: '', + iconColor: '', + }, + notificationsEnabled: true, + minimized: true, + windowed: false, + onEnableNotifications: () => undefined, + onDisableNotifications: () => undefined, + onMinimize: () => undefined, + onRestore: () => undefined, + onOpenWindow: () => undefined, +} as ScreenContextValue); + +export const ScreenProvider: FunctionalComponent = ({ children }) => { + const { dispatch, config, sound, minimized = true, undocked, expanded = false, alerts, modal, iframe } = useContext(StoreContext); + const { department, name, email } = iframe.guest || {}; + const { color } = config.theme || {}; + const { color: customColor, fontColor: customFontColor, iconColor: customIconColor } = iframe.theme || {}; + + const [poppedOut, setPopedOut] = useState(false); + + const handleEnableNotifications = () => { + dispatch({ sound: { ...sound, enabled: true } }); + }; + + const handleDisableNotifications = () => { + dispatch({ sound: { ...sound, enabled: false } }); + }; + + const handleMinimize = () => { + parentCall('minimizeWindow'); + dispatch({ minimized: true }); + }; + + const handleRestore = () => { + parentCall('restoreWindow'); + const dispatchRestore = () => dispatch({ minimized: false, undocked: false }); + + const dispatchEvent = () => { + dispatchRestore(); + store.off('storageSynced', dispatchEvent); + }; + + if (undocked) { + store.on('storageSynced', dispatchEvent); + } else { + dispatchRestore(); + } + + Triggers.callbacks?.emit('chat-opened-by-visitor'); + }; + + const handleOpenWindow = () => { + parentCall('openPopout'); + dispatch({ undocked: true, minimized: false }); + }; + + const handleDismissAlert = (id: string) => { + dispatch({ alerts: alerts.filter((alert) => alert.id !== id) }); + }; + + const dismissNotification = () => !isActiveSession(); + + const checkPoppedOutWindow = useCallback(() => { + // Checking if the window is poppedOut and setting parent minimized if yes for the restore purpose + const poppedOut = parse(window.location.search).mode === 'popout'; + setPopedOut(poppedOut); + + if (poppedOut) { + dispatch({ minimized: false }); + } + }, [dispatch]); + + useEffect(() => { + checkPoppedOutWindow(); + }, [checkPoppedOutWindow]); + + const screenProps = { + theme: { + color: customColor || color, + fontColor: customFontColor, + iconColor: customIconColor, + }, + notificationsEnabled: sound?.enabled, + minimized: !poppedOut && (minimized || undocked), + expanded: !minimized && expanded, + windowed: !minimized && poppedOut, + sound, + alerts, + modal, + nameDefault: name, + emailDefault: email, + departmentDefault: department, + onEnableNotifications: handleEnableNotifications, + onDisableNotifications: handleDisableNotifications, + onMinimize: handleMinimize, + onRestore: handleRestore, + onOpenWindow: handleOpenWindow, + onDismissAlert: handleDismissAlert, + dismissNotification, + }; + + return {children}; +}; diff --git a/packages/livechat/src/components/Screen/index.js b/packages/livechat/src/components/Screen/index.js index e4f4aff1ea4c..aaf5c826f011 100644 --- a/packages/livechat/src/components/Screen/index.js +++ b/packages/livechat/src/components/Screen/index.js @@ -1,4 +1,4 @@ -import { useEffect } from 'preact/hooks'; +import { useContext, useEffect } from 'preact/hooks'; import { createClassName } from '../../helpers/createClassName'; import ChatIcon from '../../icons/chat.svg'; @@ -8,6 +8,7 @@ import { Footer, FooterContent, PoweredBy } from '../Footer'; import { PopoverContainer } from '../Popover'; import { Sound } from '../Sound'; import ScreenHeader from './Header'; +import { ScreenContext } from './ScreenProvider'; import styles from './styles.scss'; export const ScreenContent = ({ children, nopadding, triggered = false, full = false }) => ( @@ -74,81 +75,77 @@ const CssVar = ({ theme }) => { }; /** @type {{ (props: any) => JSX.Element; Content: (props: any) => JSX.Element; Footer: (props: any) => JSX.Element }} */ -export const Screen = ({ - theme = {}, - agent, - title, - notificationsEnabled, - minimized = false, - expanded = false, - windowed = false, - children, - className, - alerts, - modal, - unread, - sound, - onDismissAlert, - onEnableNotifications, - onDisableNotifications, - onMinimize, - onRestore, - onOpenWindow, - onSoundStop, - queueInfo, - dismissNotification, - triggered = false, -}) => ( -
          - - {triggered && ( - - )} -
          - - {!triggered && ( - - )} +export const Screen = ({ title, agent, children, className, unread, triggered = false, queueInfo, onSoundStop }) => { + const { + theme = {}, + notificationsEnabled, + minimized = false, + expanded = false, + windowed = false, + alerts, + modal, + sound, + onDismissAlert, + onEnableNotifications, + onDisableNotifications, + onMinimize, + onRestore, + onOpenWindow, + dismissNotification, + } = useContext(ScreenContext); - {modal} - {children} - -
          + return ( +
          + + {triggered && ( + + )} +
          + + {!triggered && ( + + )} - + {modal} + {children} + +
          - {sound && } -
          -); + + + {sound && } +
          + ); +}; Screen.Content = ScreenContent; Screen.Footer = ScreenFooter; diff --git a/packages/livechat/src/definitions/agents.d.ts b/packages/livechat/src/definitions/agents.d.ts index da1b81242574..fc8316bfa770 100644 --- a/packages/livechat/src/definitions/agents.d.ts +++ b/packages/livechat/src/definitions/agents.d.ts @@ -1,5 +1,7 @@ // TODO: Fully type agents in livechat export type Agent = { + _id: string; + username: string; name?: string; status?: string; email?: string; diff --git a/packages/livechat/src/definitions/global.d.ts b/packages/livechat/src/definitions/global.d.ts index d765a7018adb..f1663de17281 100644 --- a/packages/livechat/src/definitions/global.d.ts +++ b/packages/livechat/src/definitions/global.d.ts @@ -7,6 +7,48 @@ declare global { SERVER_URL: string; handleIframeClose?: () => void; expandCall?: () => void; + RocketChat: { + // TODO: Discover what the hell does "_" do + _: any; + url?: string; + // TODO: Type this + livechat: { + pageVisited; + setCustomField; + initialize; + setTheme; + setDepartment; + clearDepartment; + setGuestToken; + setGuestName; + setGuestEmail; + setAgent; + registerGuest; + setLanguage; + showWidget; + hideWidget; + maximizeWidget; + minimizeWidget; + setBusinessUnit; + clearBusinessUnit; + setParentUrl; + + // callbacks + onChatMaximized; + onChatMinimized; + onChatStarted; + onChatEnded; + onPrechatFormSubmit; + onOfflineFormSubmit; + onWidgetShown; + onWidgetHidden; + onAssignAgent; + onAgentStatusChange; + onQueuePositionChange; + onServiceOffline; + }; + }; + initRocket?: string[]; } interface Document { diff --git a/packages/livechat/src/definitions/guest.d.ts b/packages/livechat/src/definitions/guest.d.ts new file mode 100644 index 000000000000..afccca145e74 --- /dev/null +++ b/packages/livechat/src/definitions/guest.d.ts @@ -0,0 +1,6 @@ +export type Guest = { + token: string; + name?: string; + email?: string; + [key: string]: unknown; +}; diff --git a/packages/livechat/src/helpers/canRenderMessage.ts b/packages/livechat/src/helpers/canRenderMessage.ts index ea0940893694..75ab8604caf9 100644 --- a/packages/livechat/src/helpers/canRenderMessage.ts +++ b/packages/livechat/src/helpers/canRenderMessage.ts @@ -26,5 +26,6 @@ const msgTypesNotRendered = [ export const canRenderMessage = ({ t }: { t: string }) => !msgTypesNotRendered.includes(t); -export const canRenderTriggerMessage = (user: { token: string }) => (message: { trigger?: boolean; triggerAfterRegistration?: boolean }) => - !message.trigger || (!user && !message.triggerAfterRegistration) || (user && message.triggerAfterRegistration); +export const canRenderTriggerMessage = + (user: { token: string } | undefined) => (message: { trigger?: boolean; triggerAfterRegistration?: boolean }) => + !message.trigger || (!user && !message.triggerAfterRegistration) || (user && message.triggerAfterRegistration); diff --git a/packages/livechat/src/helpers/formatAgent.ts b/packages/livechat/src/helpers/formatAgent.ts new file mode 100644 index 000000000000..249733b12c64 --- /dev/null +++ b/packages/livechat/src/helpers/formatAgent.ts @@ -0,0 +1,32 @@ +import { getAvatarUrl } from './baseUrl'; + +type AgentType = { + _id: string; + name: string; + status: string; + emails: [{ address: string }]; + username: string; + phone: [{ phoneNumber: string }]; + customFields: { phone: string }; +}; + +export const formatAgent = (agent: AgentType) => { + if (!agent) { + return; + } + + return { + _id: agent._id, + name: agent.name, + status: agent.status, + email: agent.emails?.[0]?.address, + username: agent.username, + phone: agent.phone?.[0]?.phoneNumber || agent.customFields?.phone, + avatar: agent.username + ? { + description: agent.username, + src: getAvatarUrl(agent.username), + } + : undefined, + }; +}; diff --git a/packages/livechat/src/helpers/isDefined.ts b/packages/livechat/src/helpers/isDefined.ts new file mode 100644 index 000000000000..d4884b460e09 --- /dev/null +++ b/packages/livechat/src/helpers/isDefined.ts @@ -0,0 +1,3 @@ +export function isDefined(val: T | undefined | null): val is T { + return val !== undefined && val !== null; +} diff --git a/packages/livechat/src/lib/hooks.js b/packages/livechat/src/lib/hooks.ts similarity index 56% rename from packages/livechat/src/lib/hooks.js rename to packages/livechat/src/lib/hooks.ts index 311cf0b2a00b..163e319733c6 100644 --- a/packages/livechat/src/lib/hooks.js +++ b/packages/livechat/src/lib/hooks.ts @@ -1,7 +1,9 @@ import i18next from 'i18next'; import { Livechat } from '../api'; -import { store } from '../store'; +import type { StoreState } from '../store'; +import { initialState, store } from '../store'; +import type { LivechatMessageEventData } from '../widget'; import CustomFields from './customFields'; import { loadConfig, updateBusinessUnit } from './main'; import { parentCall } from './parentCall'; @@ -9,24 +11,34 @@ import { createToken } from './random'; import { loadMessages } from './room'; import Triggers from './triggers'; -const createOrUpdateGuest = async (guest) => { +const createOrUpdateGuest = async (guest: StoreState['guest']) => { + if (!guest) { + return; + } const { token } = guest; token && (await store.setState({ token })); const { visitor: user } = await Livechat.grantVisitor({ visitor: { ...guest } }); - store.setState({ user }); + + if (!user) { + return; + } + store.setState({ user } as Omit); await loadConfig(); }; -const updateIframeGuestData = (data) => { +const updateIframeGuestData = (data: Partial) => { const { iframe, iframe: { guest }, - user: _id, + user, token, } = store.state; - store.setState({ iframe: { ...iframe, guest: { ...guest, ...data } } }); - if (!_id) { + const iframeGuest = { ...guest, ...data } as StoreState['guest']; + + store.setState({ iframe: { ...iframe, guest: iframeGuest } }); + + if (!user) { return; } @@ -34,8 +46,10 @@ const updateIframeGuestData = (data) => { createOrUpdateGuest(guestData); }; +export type HooksWidgetAPI = typeof api; + const api = { - pageVisited(info) { + pageVisited: (info: { change: string; title: string; location: { href: string } }) => { if (info.change === 'url') { Triggers.processRequest(info); } @@ -52,31 +66,27 @@ const api = { Livechat.sendVisitorNavigation({ token, rid, pageInfo: { change, title, location: { href } } }); }, - setCustomField(key, value, overwrite = true) { + setCustomField: (key: string, value = '', overwrite = true) => { CustomFields.setCustomField(key, value, overwrite); }, - setTheme({ color, fontColor, iconColor, title, offlineTitle } = {}) { + setTheme: (theme: StoreState['iframe']['theme']) => { const { iframe, - iframe: { theme }, + iframe: { theme: currentTheme }, } = store.state; store.setState({ iframe: { ...iframe, theme: { + ...currentTheme, ...theme, - color, - fontColor, - iconColor, - title, - offlineTitle, }, }, }); }, - async setDepartment(value) { + setDepartment: async (value: string) => { const { user, config: { departments = [] }, @@ -90,7 +100,7 @@ const api = { updateIframeGuestData({ department }); if (defaultAgent && defaultAgent.department !== department) { - store.setState({ defaultAgent: null }); + store.setState({ defaultAgent: undefined }); } if (department !== existingDepartment) { @@ -99,8 +109,8 @@ const api = { } }, - async setBusinessUnit(newBusinessUnit) { - if (!newBusinessUnit || !newBusinessUnit.trim().length) { + setBusinessUnit: async (newBusinessUnit: string) => { + if (!newBusinessUnit?.trim().length) { throw new Error('Error! Invalid business ids'); } @@ -109,16 +119,27 @@ const api = { return existingBusinessUnit !== newBusinessUnit && updateBusinessUnit(newBusinessUnit); }, - async clearBusinessUnit() { + clearBusinessUnit: async () => { const { businessUnit } = store.state; return businessUnit && updateBusinessUnit(); }, - clearDepartment() { + clearDepartment: () => { updateIframeGuestData({ department: '' }); }, - setAgent({ _id, username, ...props } = {}) { + clearWidgetData: async () => { + const { minimized, visible, undocked, expanded, businessUnit, ...initial } = initialState(); + await store.setState(initial); + }, + + setAgent: (agent: StoreState['defaultAgent']) => { + if (!agent) { + return; + } + + const { _id, username, ...props } = agent; + if (!_id || !username) { return console.warn('The fields _id and username are mandatory.'); } @@ -133,11 +154,11 @@ const api = { }); }, - setExpanded(expanded) { + setExpanded: (expanded: StoreState['expanded']) => { store.setState({ expanded }); }, - async setGuestToken(token) { + setGuestToken: async (token: string) => { const { token: localToken } = store.state; if (token === localToken) { return; @@ -145,16 +166,16 @@ const api = { await createOrUpdateGuest({ token }); }, - setGuestName(name) { + setGuestName: (name: string) => { updateIframeGuestData({ name }); }, - setGuestEmail(email) { + setGuestEmail: (email: string) => { updateIframeGuestData({ email }); }, - async registerGuest(data) { - if (!data || typeof data !== 'object') { + registerGuest: async (data: StoreState['guest']) => { + if (typeof data !== 'object') { return; } @@ -171,59 +192,72 @@ const api = { await createOrUpdateGuest(data); }, - async setLanguage(language) { + setLanguage: async (language: StoreState['iframe']['language']) => { const { iframe } = store.state; await store.setState({ iframe: { ...iframe, language } }); i18next.changeLanguage(language); }, - showWidget() { + showWidget: () => { const { iframe } = store.state; store.setState({ iframe: { ...iframe, visible: true } }); parentCall('showWidget'); }, - hideWidget() { + hideWidget: () => { const { iframe } = store.state; store.setState({ iframe: { ...iframe, visible: false } }); parentCall('hideWidget'); }, - minimizeWidget() { + minimizeWidget: () => { store.setState({ minimized: true }); parentCall('closeWidget'); }, - maximizeWidget() { + maximizeWidget: () => { store.setState({ minimized: false }); parentCall('openWidget'); }, - setParentUrl(parentUrl) { + + setParentUrl: (parentUrl: StoreState['parentUrl']) => { store.setState({ parentUrl }); }, }; -const onNewMessage = (event) => { +function onNewMessageHandler(event: MessageEvent>) { if (event.source === event.target) { return; } - if (typeof event.data === 'object' && event.data.src !== undefined && event.data.src === 'rocketchat') { - if (api[event.data.fn] !== undefined && typeof api[event.data.fn] === 'function') { - const args = [].concat(event.data.args || []); - api[event.data.fn].apply(null, args); - } + if (!event.data || typeof event.data !== 'object') { + return; } -}; + + if (!event.data.src || event.data.src !== 'rocketchat') { + return; + } + + const { fn, args } = event.data; + + if (!api.hasOwnProperty(fn)) { + return; + } + + // There is an existing issue with overload resolution with type union arguments please see https://github.com/microsoft/TypeScript/issues/14107 + // @ts-expect-error: A spread argument must either have a tuple type or be passed to a rest parameter + api[fn](...args); +} class Hooks { + private _started: boolean; + constructor() { - if (!Hooks.instance) { - this._started = false; - Hooks.instance = this; + if (instance) { + throw new Error('Hooks already has an instance.'); } - return Hooks.instance; + this._started = false; } init() { @@ -232,12 +266,12 @@ class Hooks { } this._started = true; - window.addEventListener('message', onNewMessage, false); + window.addEventListener('message', onNewMessageHandler, false); } reset() { this._started = false; - window.removeEventListener('message', onNewMessage, false); + window.removeEventListener('message', onNewMessageHandler, false); } } diff --git a/packages/livechat/src/lib/parentCall.ts b/packages/livechat/src/lib/parentCall.ts index 5c2b63ac3487..4b03e22927a5 100644 --- a/packages/livechat/src/lib/parentCall.ts +++ b/packages/livechat/src/lib/parentCall.ts @@ -1,6 +1,6 @@ -import { validCallbacks } from '../widget'; +import { VALID_CALLBACKS } from '../widget'; -export const parentCall = (method: string, args: any = []) => { +export const parentCall = (method: string, ...args: any[]) => { const data = { src: 'rocketchat', fn: method, @@ -12,4 +12,4 @@ export const parentCall = (method: string, args: any = []) => { }; export const runCallbackEventEmitter = (callbackName: string, data: unknown) => - validCallbacks.includes(callbackName) && parentCall('callback', [callbackName, data]); + VALID_CALLBACKS.includes(callbackName) && parentCall('callback', callbackName, data); diff --git a/packages/livechat/src/lib/room.js b/packages/livechat/src/lib/room.js index 1e4f975f1886..af69cd8c4c18 100644 --- a/packages/livechat/src/lib/room.js +++ b/packages/livechat/src/lib/room.js @@ -141,28 +141,28 @@ export const initRoom = async () => { if (servedBy) { roomAgent = await Livechat.agent(rid); await store.setState({ agent: roomAgent, queueInfo: null }); - parentCall('callback', ['assign-agent', normalizeAgent(roomAgent)]); + parentCall('callback', 'assign-agent', normalizeAgent(roomAgent)); } } if (queueInfo) { - parentCall('callback', ['queue-position-change', queueInfo]); + parentCall('callback', 'queue-position-change', queueInfo); } Livechat.onAgentChange(rid, async (agent) => { await store.setState({ agent, queueInfo: null }); - parentCall('callback', ['assign-agent', normalizeAgent(agent)]); + parentCall('callback', 'assign-agent', normalizeAgent(agent)); }); Livechat.onAgentStatusChange(rid, (status) => { const { agent } = store.state; agent && store.setState({ agent: { ...agent, status } }); - parentCall('callback', ['agent-status-change', normalizeAgent(agent)]); + parentCall('callback', 'agent-status-change', normalizeAgent(agent)); }); Livechat.onQueuePositionChange(rid, async (queueInfo) => { await store.setState({ queueInfo }); - parentCall('callback', ['queue-position-change', queueInfo]); + parentCall('callback', 'queue-position-change', queueInfo); }); setCookies(rid, token); diff --git a/packages/livechat/src/lib/triggers.js b/packages/livechat/src/lib/triggers.js index efa9e9bdc65d..cf73a74f20be 100644 --- a/packages/livechat/src/lib/triggers.js +++ b/packages/livechat/src/lib/triggers.js @@ -157,7 +157,7 @@ class Triggers { if (agent && agent._id) { await store.setState({ agent }); - parentCall('callback', ['assign-agent', normalizeAgent(agent)]); + parentCall('callback', 'assign-agent', normalizeAgent(agent)); } const foundCondition = trigger.conditions.find((c) => ['chat-opened-by-visitor', 'after-guest-registration'].includes(c.name)); diff --git a/packages/livechat/src/routes/Chat/connector.tsx b/packages/livechat/src/routes/Chat/connector.tsx index 9493133f44b3..a0897964ddb7 100644 --- a/packages/livechat/src/routes/Chat/connector.tsx +++ b/packages/livechat/src/routes/Chat/connector.tsx @@ -1,47 +1,13 @@ -import type { ComponentProps } from 'preact'; +import type { TFunction } from 'i18next'; +import type { FunctionalComponent } from 'preact'; import { withTranslation } from 'react-i18next'; import { ChatContainer } from '.'; -import { getAvatarUrl } from '../../helpers/baseUrl'; -import { canRenderMessage } from '../../helpers/canRenderMessage'; +import { canRenderMessage, canRenderTriggerMessage } from '../../helpers/canRenderMessage'; +import { formatAgent } from '../../helpers/formatAgent'; import { Consumer } from '../../store'; -type ChatConnectorProps = Omit< - ComponentProps, - | 'theme' - | 'title' - | 'sound' - | 'token' - | 'user' - | 'agent' - | 'room' - | 'messages' - | 'noMoreMessages' - | 'emoji' - | 'uploads' - | 'typingUsernames' - | 'loading' - | 'showConnecting' - | 'connecting' - | 'dispatch' - | 'departments' - | 'allowSwitchingDepartments' - | 'conversationFinishedMessage' - | 'allowRemoveUserData' - | 'alerts' - | 'visible' - | 'unread' - | 'lastReadMessageId' - | 'guest' - | 'triggerAgent' - | 'queueInfo' - | 'registrationFormEnabled' - | 'nameFieldRegistrationForm' - | 'emailFieldRegistrationForm' - | 'limitTextLength' ->; - -const ChatConnector = ({ ref, ...props }: ChatConnectorProps) => ( +export const ChatConnector: FunctionalComponent<{ path: string; default: boolean; t: TFunction }> = ({ ref, t }) => ( {({ config: { @@ -56,13 +22,10 @@ const ChatConnector = ({ ref, ...props }: ChatConnectorProps) => ( limitTextLength, } = {}, messages: { conversationFinishedMessage } = {}, - theme: { color, title } = {}, + theme: { title } = {}, departments = {}, }, - iframe: { - theme: { color: customColor, fontColor: customFontColor, iconColor: customIconColor, title: customTitle } = {}, - guest, - } = {}, + iframe: { theme: { title: customTitle } = {}, guest } = {}, token, agent, sound, @@ -79,40 +42,19 @@ const ChatConnector = ({ ref, ...props }: ChatConnectorProps) => ( lastReadMessageId, triggerAgent, queueInfo, + incomingCallAlert, + ongoingCall, + messageListPosition, }) => ( ( dispatch={dispatch} departments={departments} allowSwitchingDepartments={allowSwitchingDepartments} - conversationFinishedMessage={conversationFinishedMessage || props.t('conversation_finished')} + conversationFinishedMessage={conversationFinishedMessage || t('conversation_finished')} allowRemoveUserData={allowRemoveUserData} alerts={alerts} visible={visible} @@ -144,6 +86,9 @@ const ChatConnector = ({ ref, ...props }: ChatConnectorProps) => ( nameFieldRegistrationForm={nameFieldRegistrationForm} emailFieldRegistrationForm={emailFieldRegistrationForm} limitTextLength={limitTextLength} + incomingCallAlert={incomingCallAlert} + ongoingCall={ongoingCall} + messageListPosition={messageListPosition} /> )} diff --git a/packages/livechat/src/routes/Chat/container.js b/packages/livechat/src/routes/Chat/container.js index 2d23645fb49e..a6a9ba8451c2 100644 --- a/packages/livechat/src/routes/Chat/container.js +++ b/packages/livechat/src/routes/Chat/container.js @@ -5,7 +5,7 @@ import { withTranslation } from 'react-i18next'; import { Livechat } from '../../api'; import { ModalManager } from '../../components/Modal'; import { getAvatarUrl } from '../../helpers/baseUrl'; -import { canRenderMessage, canRenderTriggerMessage } from '../../helpers/canRenderMessage'; +import { canRenderMessage } from '../../helpers/canRenderMessage'; import { debounce } from '../../helpers/debounce'; import { throttle } from '../../helpers/throttle'; import { upsert } from '../../helpers/upsert'; @@ -15,7 +15,6 @@ import { getLastReadMessage, loadConfig, processUnread, shouldMarkAsUnread } fro import { parentCall, runCallbackEventEmitter } from '../../lib/parentCall'; import { createToken } from '../../lib/random'; import { initRoom, closeChat, loadMessages, loadMoreMessages, defaultRoomParams, getGreetingMessages } from '../../lib/room'; -import { Consumer } from '../../store'; import Chat from './component'; class ChatContainer extends Component { @@ -373,118 +372,4 @@ class ChatContainer extends Component { ); } -export const ChatConnector = ({ ref, t, ...props }) => ( - - {({ - config: { - settings: { - fileUpload: uploads, - allowSwitchingDepartments, - forceAcceptDataProcessingConsent: allowRemoveUserData, - showConnecting, - registrationForm, - nameFieldRegistrationForm, - emailFieldRegistrationForm, - limitTextLength, - } = {}, - messages: { conversationFinishedMessage } = {}, - theme: { color, title } = {}, - departments = {}, - }, - iframe: { - theme: { color: customColor, fontColor: customFontColor, iconColor: customIconColor, title: customTitle } = {}, - guest, - } = {}, - token, - agent, - sound, - user, - room, - messages, - noMoreMessages, - typing, - loading, - dispatch, - alerts, - visible, - unread, - lastReadMessageId, - triggerAgent, - queueInfo, - incomingCallAlert, - ongoingCall, - messageListPosition, - }) => ( - - )} - -); - -export default withTranslation()(ChatConnector); +export default withTranslation()(ChatContainer); diff --git a/packages/livechat/src/routes/Chat/stories.tsx b/packages/livechat/src/routes/Chat/stories.tsx index 2c91e6b5c132..c2adcce878cd 100644 --- a/packages/livechat/src/routes/Chat/stories.tsx +++ b/packages/livechat/src/routes/Chat/stories.tsx @@ -2,7 +2,7 @@ import { action } from '@storybook/addon-actions'; import type { Meta, Story } from '@storybook/preact'; import type { ComponentProps } from 'preact'; -import { screenProps, avatarResolver, beepAudio, screenDecorator } from '../../../.storybook/helpers'; +import { avatarResolver, beepAudio, screenDecorator } from '../../../.storybook/helpers'; import Chat from './component'; const now = new Date(Date.parse('2021-01-01T00:00:00.000Z')); @@ -53,7 +53,6 @@ export default { onBottom: action('bottom'), onUpload: action('upload'), onSubmit: action('submit'), - ...screenProps(), }, decorators: [screenDecorator], parameters: { diff --git a/packages/livechat/src/routes/ChatFinished/component.js b/packages/livechat/src/routes/ChatFinished/component.js deleted file mode 100644 index 8873bd56fa0f..000000000000 --- a/packages/livechat/src/routes/ChatFinished/component.js +++ /dev/null @@ -1,38 +0,0 @@ -import { Component } from 'preact'; -import { withTranslation } from 'react-i18next'; - -import { Button } from '../../components/Button'; -import { ButtonGroup } from '../../components/ButtonGroup'; -import Screen from '../../components/Screen'; -import { createClassName } from '../../helpers/createClassName'; -import styles from './styles.scss'; - -class ChatFinished extends Component { - handleClick = () => { - const { onRedirectChat } = this.props; - onRedirectChat && onRedirectChat(); - }; - - render = ({ color, title, greeting, message, onRedirectChat, t, ...props }) => { - const defaultGreeting = t('thanks_for_talking_with_us'); - const defaultMessage = t('if_you_have_any_other_questions_just_press_the_but'); - - return ( - - -

          {greeting || defaultGreeting}

          -

          {message || defaultMessage}

          - - - - -
          - -
          - ); - }; -} - -export default withTranslation()(ChatFinished); diff --git a/packages/livechat/src/routes/ChatFinished/component.tsx b/packages/livechat/src/routes/ChatFinished/component.tsx new file mode 100644 index 000000000000..3b13f1407cd0 --- /dev/null +++ b/packages/livechat/src/routes/ChatFinished/component.tsx @@ -0,0 +1,42 @@ +import { withTranslation } from 'react-i18next'; + +import { Button } from '../../components/Button'; +import { ButtonGroup } from '../../components/ButtonGroup'; +import Screen from '../../components/Screen'; +import { createClassName } from '../../helpers/createClassName'; +import styles from './styles.scss'; + +type ChatFinishedProps = { + title: string; + greeting?: string; + message?: string; + onRedirectChat?: () => void; + t: (s: string) => string; +}; + +const ChatFinished = ({ title, greeting, message, onRedirectChat, t }: ChatFinishedProps) => { + const handleClick = () => { + onRedirectChat?.(); + }; + + const defaultGreeting = t('thanks_for_talking_with_us'); + const defaultMessage = t('if_you_have_any_other_questions_just_press_the_but'); + + return ( + + +

          {greeting || defaultGreeting}

          +

          {message || defaultMessage}

          + + + + +
          + +
          + ); +}; + +export default withTranslation()(ChatFinished); diff --git a/packages/livechat/src/routes/ChatFinished/container.js b/packages/livechat/src/routes/ChatFinished/container.js deleted file mode 100644 index 601bfe48e857..000000000000 --- a/packages/livechat/src/routes/ChatFinished/container.js +++ /dev/null @@ -1,38 +0,0 @@ -import { Component } from 'preact'; -import { route } from 'preact-router'; -import { withTranslation } from 'react-i18next'; - -import { Consumer } from '../../store'; -import ChatFinished from './component'; - -class ChatFinishedContainer extends Component { - handleRedirect = () => { - route('/'); - }; - - render = (props) => ; -} - -const ChatFinishedConnector = ({ ref, t, ...props }) => ( - - {({ - config: { messages: { conversationFinishedMessage: greeting, conversationFinishedText: message } = {}, theme: { color } = {} } = {}, - iframe: { theme: { color: customColor, fontColor: customFontColor, iconColor: customIconColor } = {} } = {}, - }) => ( - - )} - -); - -export default withTranslation()(ChatFinishedConnector); diff --git a/packages/livechat/src/routes/ChatFinished/container.tsx b/packages/livechat/src/routes/ChatFinished/container.tsx new file mode 100644 index 000000000000..b19481fb221d --- /dev/null +++ b/packages/livechat/src/routes/ChatFinished/container.tsx @@ -0,0 +1,24 @@ +import type { TFunction } from 'i18next'; +import type { FunctionalComponent } from 'preact'; +import { route } from 'preact-router'; +import { useContext } from 'preact/hooks'; +import { withTranslation } from 'react-i18next'; + +import { StoreContext } from '../../store'; +import ChatFinished from './component'; + +const ChatFinishedContainer: FunctionalComponent<{ path: string; t: TFunction }> = ({ ref, t }) => { + const { + config: { + messages: { conversationFinishedMessage: greeting, conversationFinishedText: message }, + }, + } = useContext(StoreContext); + + const handleRedirect = () => { + route('/'); + }; + + return ; +}; + +export default withTranslation()(ChatFinishedContainer); diff --git a/packages/livechat/src/routes/ChatFinished/stories.tsx b/packages/livechat/src/routes/ChatFinished/stories.tsx index 6a4cc0f2b259..18c4c88ab41c 100644 --- a/packages/livechat/src/routes/ChatFinished/stories.tsx +++ b/packages/livechat/src/routes/ChatFinished/stories.tsx @@ -2,7 +2,7 @@ import { action } from '@storybook/addon-actions'; import type { Meta, Story } from '@storybook/preact'; import type { ComponentProps } from 'preact'; -import { screenProps, loremIpsum, screenDecorator } from '../../../.storybook/helpers'; +import { loremIpsum, screenDecorator } from '../../../.storybook/helpers'; import ChatFinished from './component'; export default { @@ -13,7 +13,6 @@ export default { greeting: '', message: '', onRedirectChat: action('redirectChat'), - ...screenProps(), }, decorators: [screenDecorator], parameters: { diff --git a/packages/livechat/src/routes/GDPRAgreement/component.js b/packages/livechat/src/routes/GDPRAgreement/component.tsx similarity index 80% rename from packages/livechat/src/routes/GDPRAgreement/component.js rename to packages/livechat/src/routes/GDPRAgreement/component.tsx index b9333cc6bf5f..b18988baafd5 100644 --- a/packages/livechat/src/routes/GDPRAgreement/component.js +++ b/packages/livechat/src/routes/GDPRAgreement/component.tsx @@ -1,3 +1,4 @@ +import type { TFunction } from 'i18next'; import { Component } from 'preact'; import { Trans, withTranslation } from 'react-i18next'; @@ -8,23 +9,22 @@ import Screen from '../../components/Screen'; import { createClassName } from '../../helpers/createClassName'; import styles from './styles.scss'; -class GDPR extends Component { +type GDPRProps = { + title: string; + consentText: string; + instructions: string; + onAgree: () => void; + t: TFunction; +}; + +class GDPR extends Component { handleClick = () => { const { onAgree } = this.props; - onAgree && onAgree(); + onAgree?.(); }; - render = ({ - color, - title, - consentText, - instructions, - // eslint-disable-next-line no-unused-vars - onAgree, - t, - ...props - }) => ( - + render = ({ title, consentText, instructions, t }: GDPRProps) => ( + {consentText ? (

          diff --git a/packages/livechat/src/routes/GDPRAgreement/container.js b/packages/livechat/src/routes/GDPRAgreement/container.js deleted file mode 100644 index 82c290eb55e9..000000000000 --- a/packages/livechat/src/routes/GDPRAgreement/container.js +++ /dev/null @@ -1,41 +0,0 @@ -import { Component } from 'preact'; -import { route } from 'preact-router'; -import { withTranslation } from 'react-i18next'; - -import { Consumer } from '../../store'; -import GDPRAgreement from './component'; - -class GDPRContainer extends Component { - handleAgree = async () => { - const { dispatch } = this.props; - await dispatch({ gdpr: { accepted: true } }); - route('/'); - }; - - render = (props) => ; -} - -const GDPRConnector = ({ ref, t, ...props }) => ( - - {({ - config: { theme: { color } = {}, messages: { dataProcessingConsentText: consentText } = {} } = {}, - iframe: { theme: { color: customColor, fontColor: customFontColor, iconColor: customIconColor } = {} } = {}, - dispatch, - }) => ( - - )} - -); - -export default withTranslation()(GDPRConnector); diff --git a/packages/livechat/src/routes/GDPRAgreement/container.tsx b/packages/livechat/src/routes/GDPRAgreement/container.tsx new file mode 100644 index 000000000000..8dcbb83009b2 --- /dev/null +++ b/packages/livechat/src/routes/GDPRAgreement/container.tsx @@ -0,0 +1,21 @@ +import type { TFunction } from 'i18next'; +import type { FunctionalComponent } from 'preact'; +import { route } from 'preact-router'; +import { useContext } from 'preact/hooks'; +import { withTranslation } from 'react-i18next'; + +import { StoreContext } from '../../store'; +import GDPRAgreement from './component'; + +const GDPRContainer: FunctionalComponent<{ t: TFunction }> = ({ ref, t }) => { + const { config: { messages: { dataProcessingConsentText: consentText = '' } = {} } = {}, dispatch } = useContext(StoreContext); + + const handleAgree = async () => { + await dispatch({ gdpr: { accepted: true } }); + route('/'); + }; + + return ; +}; + +export default withTranslation()(GDPRContainer); diff --git a/packages/livechat/src/routes/GDPRAgreement/stories.tsx b/packages/livechat/src/routes/GDPRAgreement/stories.tsx index fb0139cd2e53..95de5492be73 100644 --- a/packages/livechat/src/routes/GDPRAgreement/stories.tsx +++ b/packages/livechat/src/routes/GDPRAgreement/stories.tsx @@ -2,7 +2,7 @@ import { action } from '@storybook/addon-actions'; import type { Meta, Story } from '@storybook/preact'; import type { ComponentProps } from 'preact'; -import { screenDecorator, screenProps } from '../../../.storybook/helpers'; +import { screenDecorator } from '../../../.storybook/helpers'; import GDPRAgreement from './component'; export default { @@ -13,7 +13,6 @@ export default { consentText: '', instructions: '', onAgree: action('agree'), - ...screenProps(), }, decorators: [screenDecorator], parameters: { diff --git a/packages/livechat/src/routes/LeaveMessage/index.tsx b/packages/livechat/src/routes/LeaveMessage/index.tsx index 8f209de029db..7a8cdab0d0e2 100644 --- a/packages/livechat/src/routes/LeaveMessage/index.tsx +++ b/packages/livechat/src/routes/LeaveMessage/index.tsx @@ -1,3 +1,4 @@ +import type { FunctionalComponent } from 'preact'; import { useContext, useRef } from 'preact/hooks'; import type { JSXInternal } from 'preact/src/jsx'; import type { FieldValues, SubmitHandler } from 'react-hook-form'; @@ -21,12 +22,12 @@ import { createToken } from '../../lib/random'; import { StoreContext } from '../../store'; import styles from './styles.scss'; -const LeaveMessage = ({ screenProps }: { screenProps: { [key: string]: unknown }; path: string }) => { +const LeaveMessage: FunctionalComponent<{ path: string }> = () => { const { config: { departments = [], messages: { offlineMessage, offlineSuccessMessage, offlineUnavailableMessage }, - theme: { offlineTitle: title, offlineColor: color }, + theme: { offlineTitle: title }, settings: { displayOfflineForm }, }, iframe, @@ -56,7 +57,9 @@ const LeaveMessage = ({ screenProps }: { screenProps: { [key: string]: unknown } ...(department && { department }), message, }; + await dispatch({ loading: true }); + try { // TODO: Remove intersection after ts refactor of parseOfflineMessage const payload = parseOfflineMessage(fields) as FormValues & { host: string }; @@ -64,7 +67,7 @@ const LeaveMessage = ({ screenProps }: { screenProps: { [key: string]: unknown } await ModalManager.alert({ text: offlineSuccessMessage || text, }); - parentCall('callback', ['offline-form-submit', fields]); + parentCall('callback', 'offline-form-submit', fields); return true; } catch (error: unknown) { const errorMessage = (error as { error: string })?.error; @@ -82,13 +85,7 @@ const LeaveMessage = ({ screenProps }: { screenProps: { [key: string]: unknown } const defaultUnavailableMessage = ''; // TODO return ( - +

          diff --git a/packages/livechat/src/routes/Register/index.tsx b/packages/livechat/src/routes/Register/index.tsx index d9df7a5a3d4a..8582bde03d04 100644 --- a/packages/livechat/src/routes/Register/index.tsx +++ b/packages/livechat/src/routes/Register/index.tsx @@ -1,3 +1,4 @@ +import type { FunctionalComponent } from 'preact'; import { route } from 'preact-router'; import { useContext, useEffect, useRef } from 'preact/hooks'; import type { JSXInternal } from 'preact/src/jsx'; @@ -16,12 +17,13 @@ import CustomFields from '../../lib/customFields'; import { validateEmail } from '../../lib/email'; import { parentCall } from '../../lib/parentCall'; import { StoreContext } from '../../store'; +import type { StoreState } from '../../store'; import styles from './styles.scss'; // Custom field as in the form payload type FormPayloadCustomField = { [key: string]: string }; -export const Register = ({ screenProps }: { screenProps: { [key: string]: unknown }; path: string }) => { +export const Register: FunctionalComponent<{ path: string }> = () => { const { t } = useTranslation(); const topRef = useRef(null); @@ -38,13 +40,10 @@ export const Register = ({ screenProps }: { screenProps: { [key: string]: unknow departments = [], messages: { registrationFormMessage: message }, settings: { nameFieldRegistrationForm: hasNameField, emailFieldRegistrationForm: hasEmailField }, - theme: { title, color }, + theme: { title }, customFields = [], }, - iframe: { - guest: { department: guestDepartment, name: guestName, email: guestEmail }, - theme: { color: customColor, fontColor: customFontColor, iconColor: customIconColor, title: customTitle }, - }, + iframe: { guest: { department: guestDepartment = undefined, name: guestName = undefined, email: guestEmail = undefined } = {} }, loading = false, token, dispatch, @@ -82,10 +81,12 @@ export const Register = ({ screenProps }: { screenProps: { [key: string]: unknow }; dispatch({ loading: true, department }); + try { const { visitor: user } = await Livechat.grantVisitor({ visitor: { ...fields, token } }); - dispatch({ user }); - parentCall('callback', ['pre-chat-form-submit', fields]); + await dispatch({ user } as Omit); + + parentCall('callback', 'pre-chat-form-submit', fields); registerCustomFields(customFields); } finally { dispatch({ loading: false }); @@ -107,17 +108,7 @@ export const Register = ({ screenProps }: { screenProps: { [key: string]: unknow }, [user?._id]); return ( - +
          { +const SwitchDepartment: FunctionalComponent<{ path: string }> = () => { const { t } = useTranslation(); const { config: { messages: { switchDepartmentMessage }, departments: deps = [], - theme: { color }, }, iframe: { guest }, iframe, @@ -60,7 +61,10 @@ const SwitchDepartment = ({ screenProps }: { screenProps: { [key: string]: unkno if (!room) { const { visitor: user } = await Livechat.grantVisitor({ visitor: { department, token } }); - await dispatch({ user, alerts: (alerts.push({ id: createToken(), children: t('department_switched'), success: true }), alerts) }); + await dispatch({ + user: user as StoreState['user'], + alerts: (alerts.push({ id: createToken(), children: t('department_switched'), success: true }), alerts), + }); return route('/'); } @@ -84,7 +88,7 @@ const SwitchDepartment = ({ screenProps }: { screenProps: { [key: string]: unkno throw t('no_available_agents_to_transfer'); } - await dispatch({ iframe: { ...iframe, guest: { ...guest, department } }, loading: false }); + await dispatch({ iframe: { ...iframe, guest: { ...guest, department } }, loading: false } as Pick); await loadConfig(); await ModalManager.alert({ @@ -110,7 +114,7 @@ const SwitchDepartment = ({ screenProps }: { screenProps: { [key: string]: unkno const defaultMessage = t('choose_a_department_1'); return ( - +

          {switchDepartmentMessage || defaultMessage}

          diff --git a/packages/livechat/src/routes/SwitchDepartment/stories.tsx b/packages/livechat/src/routes/SwitchDepartment/stories.tsx index 37405c97bbc7..c7409bc89695 100644 --- a/packages/livechat/src/routes/SwitchDepartment/stories.tsx +++ b/packages/livechat/src/routes/SwitchDepartment/stories.tsx @@ -2,7 +2,7 @@ import { action } from '@storybook/addon-actions'; import type { Meta, Story } from '@storybook/preact'; import type { ComponentProps } from 'preact'; -import { screenDecorator, screenProps } from '../../../.storybook/helpers'; +import { screenDecorator } from '../../../.storybook/helpers'; import SwitchDepartment from './index'; export default { @@ -28,7 +28,6 @@ export default { loading: false, onSubmit: action('submit'), onCancel: action('cancel'), - ...screenProps(), }, decorators: [screenDecorator], parameters: { diff --git a/packages/livechat/src/routes/TriggerMessage/component.js b/packages/livechat/src/routes/TriggerMessage/component.tsx similarity index 55% rename from packages/livechat/src/routes/TriggerMessage/component.js rename to packages/livechat/src/routes/TriggerMessage/component.tsx index d31ccb506f8f..86e65f1d11e0 100644 --- a/packages/livechat/src/routes/TriggerMessage/component.js +++ b/packages/livechat/src/routes/TriggerMessage/component.tsx @@ -1,15 +1,29 @@ +import type { TFunction } from 'i18next'; +import type { RefObject } from 'preact'; import { Component, createRef } from 'preact'; import { withTranslation } from 'react-i18next'; import Screen from '../../components/Screen'; +import type { ScreenContextValue } from '../../components/Screen/ScreenProvider'; import { createClassName } from '../../helpers/createClassName'; import { parentCall } from '../../lib/parentCall'; +import type { StoreState } from '../../store'; import styles from './styles.scss'; -class TriggerMessage extends Component { +type TriggerMessageProps = { + title: string; + messages: StoreState['messages']; + onStartChat: () => void; + t: TFunction; + theme: ScreenContextValue['theme']; +}; + +class TriggerMessage extends Component { state = {}; - constructor(props) { + ref: RefObject; + + constructor(props: TriggerMessageProps) { super(props); this.ref = createRef(); } @@ -24,16 +38,14 @@ class TriggerMessage extends Component { parentCall('resizeWidget', height); } - render({ title, messages, loading, onStartChat = () => {}, departments, t, ...props }) { + render = ({ title, messages, onStartChat = () => undefined, t }: TriggerMessageProps) => { const defaultTitle = t('messages'); - const { - theme: { color }, - } = props; + const { theme: { color } = {} } = this.props; + return ( - + - {messages && - messages.map((message) => message.msg &&

          {message.msg}

          )} + {messages?.map((message) => message.msg &&

          {message.msg}

          )}

          @@ -43,7 +55,7 @@ class TriggerMessage extends Component {
          ); - } + }; } export default withTranslation()(TriggerMessage); diff --git a/packages/livechat/src/routes/TriggerMessage/container.js b/packages/livechat/src/routes/TriggerMessage/container.js deleted file mode 100644 index 655279d11ea8..000000000000 --- a/packages/livechat/src/routes/TriggerMessage/container.js +++ /dev/null @@ -1,67 +0,0 @@ -import { Component } from 'preact'; -import { route } from 'preact-router'; - -import { getAvatarUrl } from '../../helpers/baseUrl'; -import { canRenderMessage } from '../../helpers/canRenderMessage'; -import { parentCall } from '../../lib/parentCall'; -import { Consumer } from '../../store'; -import TriggerMessage from './component'; - -export class TriggerMessageContainer extends Component { - handleStart(props) { - parentCall('setFullScreenDocumentMobile'); - parentCall('openWidget'); - props.onRestore(); - route('/'); - } - - render = (props) => { - parentCall('resetDocumentStyle'); - return this.handleStart(props)} {...props} />; - }; -} - -/** @type {function({ ref?: any; path?: string }): any} */ -export const TriggerMessageConnector = ({ ref, ...props }) => ( - - {({ - config: { theme: { color } = {} } = {}, - iframe: { theme: { color: customColor, fontColor: customFontColor, iconColor: customIconColor } = {} } = {}, - messages, - agent, - unread, - }) => ( - - )} - -); - -export default TriggerMessageConnector; diff --git a/packages/livechat/src/routes/TriggerMessage/container.tsx b/packages/livechat/src/routes/TriggerMessage/container.tsx new file mode 100644 index 000000000000..62a3a1927a0e --- /dev/null +++ b/packages/livechat/src/routes/TriggerMessage/container.tsx @@ -0,0 +1,39 @@ +import type { FunctionalComponent } from 'preact'; +import { route } from 'preact-router'; +import { useContext, useEffect } from 'preact/hooks'; + +import { ScreenContext } from '../../components/Screen/ScreenProvider'; +import { canRenderMessage } from '../../helpers/canRenderMessage'; +import { formatAgent } from '../../helpers/formatAgent'; +import { parentCall } from '../../lib/parentCall'; +import { StoreContext } from '../../store'; +import TriggerMessage from './component'; + +export const TriggerMessageContainer: FunctionalComponent<{ path: string }> = ({ ref }) => { + const { messages, agent, unread } = useContext(StoreContext); + const { theme, onRestore } = useContext(ScreenContext); + + const handleStart = () => { + parentCall('setFullScreenDocumentMobile'); + parentCall('openWidget'); + onRestore(); + route('/'); + }; + + useEffect(() => { + parentCall('resetDocumentStyle'); + }, []); + + return ( + handleStart()} + /> + ); +}; + +export default TriggerMessageContainer; diff --git a/packages/livechat/src/routes/TriggerMessage/index.ts b/packages/livechat/src/routes/TriggerMessage/index.ts index 71a0794e88cc..fad21836af80 100644 --- a/packages/livechat/src/routes/TriggerMessage/index.ts +++ b/packages/livechat/src/routes/TriggerMessage/index.ts @@ -1,3 +1,3 @@ export { default as TriggerMessage } from './component'; -export { TriggerMessageContainer, TriggerMessageConnector } from './container'; +export { TriggerMessageContainer } from './container'; export { default } from './container'; diff --git a/packages/livechat/src/routes/TriggerMessage/stories.tsx b/packages/livechat/src/routes/TriggerMessage/stories.tsx index 8ee5291e8e44..dedf0abca764 100644 --- a/packages/livechat/src/routes/TriggerMessage/stories.tsx +++ b/packages/livechat/src/routes/TriggerMessage/stories.tsx @@ -2,7 +2,7 @@ import { action } from '@storybook/addon-actions'; import type { Meta, Story } from '@storybook/preact'; import type { ComponentProps } from 'preact'; -import { screenDecorator, screenProps } from '../../../.storybook/helpers'; +import { screenDecorator } from '../../../.storybook/helpers'; import TriggerMessage from './component'; const now = new Date(Date.parse('2021-01-01T00:00:00.000Z')); @@ -28,7 +28,6 @@ export default { title: '', onSubmit: action('submit'), onCancel: action('cancel'), - ...screenProps(), }, decorators: [screenDecorator], parameters: { diff --git a/packages/livechat/src/routes/TriggerMessage/styles.scss b/packages/livechat/src/routes/TriggerMessage/styles.scss index e55ae2fe1a47..3f6ad5916953 100644 --- a/packages/livechat/src/routes/TriggerMessage/styles.scss +++ b/packages/livechat/src/routes/TriggerMessage/styles.scss @@ -34,7 +34,7 @@ cursor: pointer; text-decoration: none; - color: $color-blue; + color: var(--color, $color-blue); border: 0; background: none; diff --git a/packages/livechat/src/store/index.tsx b/packages/livechat/src/store/index.tsx index 48e3e1df3ddd..7f31ba739e04 100644 --- a/packages/livechat/src/store/index.tsx +++ b/packages/livechat/src/store/index.tsx @@ -1,18 +1,37 @@ +import type { ILivechatVisitor, ILivechatVisitorDTO, Serialized } from '@rocket.chat/core-typings'; import type { ComponentChildren } from 'preact'; import { Component, createContext } from 'preact'; import type { CustomField } from '../components/Form/CustomFields'; +import type { Agent } from '../definitions/agents'; import type { Department } from '../definitions/departments'; import { parentCall } from '../lib/parentCall'; import { createToken } from '../lib/random'; import Store from './Store'; -type StoreState = { +export type StoreState = { token: string; typing: string[]; config: { messages: any; - theme: any; + theme: { + title?: string; + color?: string; + offlineTitle?: string; + offlineColor?: string; + actionLinks?: { + webrtc: { + actionLinksAlignment: string; + i18nLabel: string; + label: string; + method_id: string; + }[]; + jitsi: { + icon: string; + i18nLabel: string; + }[]; + }; + }; triggers: any[]; resources: any; settings: { @@ -32,16 +51,25 @@ type StoreState = { enabled?: boolean; }; messages: any[]; - user: any; + user?: Serialized; + guest?: Serialized; sound: { src?: string; play?: boolean; enabled: boolean; }; iframe: { - guest: any; - theme: any; - visible: boolean; + guest?: Serialized; + theme: { + title?: string; + color?: string; + fontColor?: string; + iconColor?: string; + offlineTitle?: string; + }; + visible?: boolean; + department?: string; + language?: string; }; gdpr: { accepted: boolean; @@ -66,7 +94,10 @@ type StoreState = { lastReadMessageId?: any; triggerAgent?: any; queueInfo?: any; + defaultAgent?: Agent; + parentUrl?: string; connecting?: boolean; + messageListPosition?: 'top' | 'bottom' | 'free'; }; export const initialState = (): StoreState => ({ @@ -81,14 +112,14 @@ export const initialState = (): StoreState => ({ resources: {}, }, messages: [], - user: null, + user: undefined, sound: { src: '', enabled: true, play: false, }, iframe: { - guest: {}, + guest: undefined, theme: {}, visible: true, }, @@ -145,16 +176,30 @@ if (process.env.NODE_ENV === 'development') { }); } -export type Dispatch = (partialState: Partial) => void; +export type Dispatch = typeof store.setState; -type StoreContextValue = StoreState & { dispatch: Dispatch }; +type StoreContextValue = StoreState & { + dispatch: Dispatch; + on: typeof store.on; + off: typeof store.off; +}; -export const StoreContext = createContext({ ...store.state, dispatch: store.setState.bind(store) }); +export const StoreContext = createContext({ + ...store.state, + dispatch: store.setState.bind(store), + on: store.on.bind(store), + off: store.off.bind(store), +}); export class Provider extends Component { static displayName = 'StoreProvider'; - state = { ...store.state, dispatch: store.setState.bind(store) }; + state = { + ...store.state, + dispatch: store.setState.bind(store), + on: store.on.bind(store), + off: store.off.bind(store), + }; handleStoreChange = () => { this.setState({ ...store.state }); @@ -168,7 +213,9 @@ export class Provider extends Component { store.off('change', this.handleStoreChange); } - render = ({ children }: { children: ComponentChildren }) => {children}; + render = ({ children }: { children: ComponentChildren }) => { + return {children}; + }; } export const { Consumer } = StoreContext; diff --git a/packages/livechat/src/widget.js b/packages/livechat/src/widget.ts similarity index 53% rename from packages/livechat/src/widget.js rename to packages/livechat/src/widget.ts index 46781644ec36..e43bd3943844 100644 --- a/packages/livechat/src/widget.js +++ b/packages/livechat/src/widget.ts @@ -1,9 +1,47 @@ +import type { UserStatus } from '@rocket.chat/core-typings'; +import type { LivechatRoomEvents } from '@rocket.chat/ddp-client/dist/livechat/types/LivechatSDK'; import mitt from 'mitt'; -const log = - process.env.NODE_ENV === 'development' - ? (...args) => window.console.log('%cwidget%c', 'color: red', 'color: initial', ...args) - : () => {}; +import { isDefined } from './helpers/isDefined'; +import type { HooksWidgetAPI } from './lib/hooks'; +import type { StoreState } from './store'; + +type InternalWidgetAPI = { + popup: Window | null; + ready: () => void; + minimizeWindow: () => void; + restoreWindow: () => void; + openPopout: () => void; + openWidget: () => void; + resizeWidget: (height: number) => void; + removeWidget: () => void; + callback: (eventName: string, data?: unknown) => void; + showWidget: () => void; + hideWidget: () => void; + resetDocumentStyle: () => void; + setFullScreenDocumentMobile: () => void; +}; + +export type LivechatMessageEventData> = { + src?: string; + fn: keyof ApiType; + args: Parameters; +}; + +type InitializeParams = { + customField: [key: string, value: string, overwrite?: boolean]; + setCustomFields: [key: string, value: string, overwrite?: boolean][]; + theme: StoreState['iframe']['theme']; + department: string; + businessUnit: string; + guestToken: string; + guestName: string; + guestEmail: string; + registerGuest: StoreState['guest']; + language: string; + agent: StoreState['defaultAgent']; + parentUrl: string; +}; const WIDGET_OPEN_WIDTH = 365; const WIDGET_OPEN_HEIGHT = 525; @@ -12,16 +50,16 @@ const WIDGET_MINIMIZED_HEIGHT = 54; const WIDGET_MARGIN = 16; window.RocketChat = window.RocketChat || { _: [] }; -const config = {}; -let widget; -let iframe; -let hookQueue = []; +const config: { url?: string } = {}; +let widget: HTMLDivElement | null; +let iframe: HTMLIFrameElement | null; +let hookQueue: [keyof HooksWidgetAPI, Parameters][] = []; let ready = false; let smallScreen = false; -let scrollPosition; -let widget_height; +let scrollPosition: number; +let widgetHeight: number; -export const validCallbacks = [ +export const VALID_CALLBACKS = [ 'chat-maximized', 'chat-minimized', 'chat-started', @@ -38,15 +76,15 @@ export const validCallbacks = [ const callbacks = mitt(); -function registerCallback(eventName, fn) { - if (validCallbacks.indexOf(eventName) === -1) { +function registerCallback(eventName: string, fn: () => unknown) { + if (VALID_CALLBACKS.indexOf(eventName) === -1) { return false; } return callbacks.on(eventName, fn); } -function emitCallback(eventName, data) { +function emitCallback(eventName: string, data?: unknown) { if (typeof data !== 'undefined') { callbacks.emit(eventName, data); } else { @@ -59,19 +97,41 @@ function clearAllCallbacks() { } // hooks -function callHook(action, params) { +function callHook(action: keyof HooksWidgetAPI, ...params: Parameters) { if (!ready) { return hookQueue.push([action, params]); } + + if (!iframe?.contentWindow) { + throw new Error('Widget is not initialized'); + } + const data = { src: 'rocketchat', fn: action, args: params, }; - iframe.contentWindow.postMessage(data, '*'); + + iframe.contentWindow?.postMessage(data, '*'); +} + +function processHookQueue() { + if (!hookQueue.length) { + return; + } + + hookQueue.forEach(([action, params = []]) => { + callHook(action, ...params); + }); + + hookQueue = []; } -const updateWidgetStyle = (isOpened) => { +const updateWidgetStyle = (isOpened: boolean) => { + if (!iframe || !widget) { + throw new Error('Widget is not initialized'); + } + const isFullscreen = smallScreen && widget.dataset.state !== 'triggered'; if (smallScreen && isOpened) { @@ -95,7 +155,7 @@ const updateWidgetStyle = (isOpened) => { * for widget.style.width */ - widget.style.height = isFullscreen ? '100%' : `${WIDGET_MARGIN + widget_height + WIDGET_MARGIN + WIDGET_MINIMIZED_HEIGHT}px`; + widget.style.height = isFullscreen ? '100%' : `${WIDGET_MARGIN + widgetHeight + WIDGET_MARGIN + WIDGET_MINIMIZED_HEIGHT}px`; widget.style.width = isFullscreen ? '100%' : `${WIDGET_MARGIN + WIDGET_OPEN_WIDTH + WIDGET_MARGIN}px`; } else { widget.style.left = 'auto'; @@ -104,7 +164,7 @@ const updateWidgetStyle = (isOpened) => { } }; -const createWidget = (url) => { +const createWidget = (url: string) => { widget = document.createElement('div'); widget.className = 'rocketchat-widget'; widget.style.position = 'fixed'; @@ -123,7 +183,6 @@ const createWidget = (url) => { iframe = document.createElement('iframe'); iframe.id = 'rocketchat-iframe'; - iframe.allowTransparency = 'true'; iframe.src = url; iframe.style.width = '100%'; iframe.style.height = '100%'; @@ -134,7 +193,7 @@ const createWidget = (url) => { widget.appendChild(container); document.body.appendChild(widget); - const handleMediaQueryTest = ({ matches }) => { + const handleMediaQueryTest = ({ matches }: { matches: boolean }) => { if (!widget) { return; } @@ -151,24 +210,35 @@ const createWidget = (url) => { }; const openWidget = () => { + if (!iframe || !widget) { + throw new Error('Widget is not initialized'); + } + if (widget.dataset.state === 'opened') { return; } - widget_height = WIDGET_OPEN_HEIGHT; + widgetHeight = WIDGET_OPEN_HEIGHT; widget.dataset.state = 'opened'; updateWidgetStyle(true); iframe.focus(); emitCallback('chat-maximized'); }; -const resizeWidget = (height) => { - widget_height = height; +const resizeWidget = (height: number) => { + if (!widget) { + throw new Error('Widget is not initialized'); + } + widgetHeight = height; widget.dataset.state = 'triggered'; updateWidgetStyle(true); }; function closeWidget() { + if (!iframe || !widget) { + throw new Error('Widget is not initialized'); + } + if (widget.dataset.state === 'closed') { return; } @@ -178,77 +248,7 @@ function closeWidget() { emitCallback('chat-minimized'); } -const api = { - popup: null, - - ready() { - ready = true; - if (hookQueue.length > 0) { - hookQueue.forEach(function (hookParams) { - callHook.apply(this, hookParams); - }); - hookQueue = []; - } - }, - - minimizeWindow() { - closeWidget(); - }, - - restoreWindow() { - if (api.popup && api.popup.closed !== true) { - api.popup.close(); - api.popup = null; - } - openWidget(); - }, - - openPopout() { - closeWidget(); - api.popup = window.open( - `${config.url}${config.url.lastIndexOf('?') > -1 ? '&' : '?'}mode=popout`, - 'livechat-popout', - `width=${WIDGET_OPEN_WIDTH}, height=${widget_height}, toolbars=no`, - ); - api.popup.focus(); - }, - - openWidget() { - openWidget(); - }, - - resizeWidget(height) { - resizeWidget(height); - }, - - removeWidget() { - document.body.removeChild(widget); - }, - - callback(eventName, data) { - emitCallback(eventName, data); - }, - - showWidget() { - iframe.style.display = 'initial'; - emitCallback('show-widget'); - }, - - hideWidget() { - iframe.style.display = 'none'; - emitCallback('hide-widget'); - }, - - resetDocumentStyle() { - document.body.classList.remove('rc-livechat-mobile-full-screen'); - }, - - setFullScreenDocumentMobile() { - smallScreen && document.body.classList.add('rc-livechat-mobile-full-screen'); - }, -}; - -function pageVisited(change) { +function pageVisited(change: string) { callHook('pageVisited', { change, location: JSON.parse(JSON.stringify(document.location)), @@ -256,22 +256,37 @@ function pageVisited(change) { }); } -function setCustomField(key, value, overwrite) { +function setCustomField(key: string, value = '', overwrite = true) { if (typeof overwrite === 'undefined') { overwrite = true; } - callHook('setCustomField', [key, value, overwrite]); + if (!key) { + return; + } + + callHook('setCustomField', key, value, overwrite); } -function setTheme(theme) { +function setCustomFields(fields: [key: string, value: string, overwrite?: boolean][]) { + if (!Array.isArray(fields)) { + console.log('Error: Invalid parameters. Value must be an array of objects'); + return; + } + + fields.forEach(([key, value, overwrite = true]) => { + setCustomField(key, value, overwrite); + }); +} + +function setTheme(theme: StoreState['iframe']['theme']) { callHook('setTheme', theme); } -function setDepartment(department) { +function setDepartment(department: string) { callHook('setDepartment', department); } -function setBusinessUnit(businessUnit) { +function setBusinessUnit(businessUnit: string) { callHook('setBusinessUnit', businessUnit); } @@ -279,19 +294,19 @@ function clearBusinessUnit() { callHook('clearBusinessUnit'); } -function setGuestToken(token) { +function setGuestToken(token: string) { callHook('setGuestToken', token); } -function setGuestName(name) { +function setGuestName(name: string) { callHook('setGuestName', name); } -function setGuestEmail(email) { +function setGuestEmail(email: string) { callHook('setGuestEmail', email); } -function registerGuest(guest) { +function registerGuest(guest: StoreState['guest']) { callHook('registerGuest', guest); } @@ -299,12 +314,12 @@ function clearDepartment() { callHook('clearDepartment'); } -function setAgent(agent) { +function setAgent(agent: StoreState['defaultAgent']) { callHook('setAgent', agent); } -function setLanguage(language) { - callHook('setLanguage', language); +function setLanguage(lang: string) { + callHook('setLanguage', lang); } function showWidget() { @@ -327,61 +342,58 @@ function minimizeWidget() { emitCallback('chat-minimized'); } -function setParentUrl(url) { +function setParentUrl(url: string) { callHook('setParentUrl', url); } -function initialize(params) { - for (const method in params) { - if (!params.hasOwnProperty(method)) { +function initialize(initParams: Partial) { + for (const initKey in initParams) { + if (!initParams.hasOwnProperty(initKey)) { + continue; + } + + const params = initParams[initKey as keyof InitializeParams]; + + if (!isDefined(params)) { continue; } - switch (method) { + switch (initKey) { case 'customField': - const { key, value, overwrite } = params[method]; - setCustomField(key, value, overwrite); + setCustomField(...(params as InitializeParams['customField'])); continue; case 'setCustomFields': - if (!Array.isArray(params[method])) { - console.log('Error: Invalid parameters. Value must be an array of objects'); - continue; - } - params[method].forEach((data) => { - const { key, value, overwrite } = data; - setCustomField(key, value, overwrite); - }); + setCustomFields(params as InitializeParams['setCustomFields']); continue; case 'theme': - setTheme(params[method]); + setTheme(params as InitializeParams['theme']); continue; case 'department': - setDepartment(params[method]); + setDepartment(params as InitializeParams['department']); continue; - case 'businessUnit': { - setBusinessUnit(params[method]); + case 'businessUnit': + setBusinessUnit(params as InitializeParams['businessUnit']); continue; - } case 'guestToken': - setGuestToken(params[method]); + setGuestToken(params as InitializeParams['guestToken']); continue; case 'guestName': - setGuestName(params[method]); + setGuestName(params as InitializeParams['guestName']); continue; case 'guestEmail': - setGuestEmail(params[method]); + setGuestEmail(params as InitializeParams['guestEmail']); continue; case 'registerGuest': - registerGuest(params[method]); + registerGuest(params as InitializeParams['registerGuest']); continue; case 'language': - setLanguage(params[method]); + setLanguage(params as InitializeParams['language']); continue; case 'agent': - setAgent(params[method]); + setAgent(params as InitializeParams['agent']); continue; case 'parentUrl': - setParentUrl(params[method]); + setParentUrl(params as InitializeParams['parentUrl']); continue; default: continue; @@ -389,76 +401,81 @@ function initialize(params) { } } -const currentPage = { - href: null, - title: null, -}; +const api: InternalWidgetAPI = { + popup: null, -const attachMessageListener = () => { - window.addEventListener( - 'message', - (msg) => { - if (typeof msg.data === 'object' && msg.data.src !== undefined && msg.data.src === 'rocketchat') { - if (api[msg.data.fn] !== undefined && typeof api[msg.data.fn] === 'function') { - const args = [].concat(msg.data.args || []); - log(`api.${msg.data.fn}`, ...args); - api[msg.data.fn].apply(null, args); - } - } - }, - false, - ); -}; + openWidget, -const trackNavigation = () => { - setInterval(() => { - if (document.location.href !== currentPage.href) { - pageVisited('url'); - currentPage.href = document.location.href; - } + resizeWidget, - if (document.title !== currentPage.title) { - pageVisited('title'); - currentPage.title = document.title; + ready() { + ready = true; + processHookQueue(); + }, + + minimizeWindow() { + closeWidget(); + }, + + restoreWindow() { + if (api.popup && api.popup.closed !== true) { + api.popup.close(); + api.popup = null; } - }, 800); -}; + openWidget(); + }, -const init = (url) => { - const trimmedUrl = url.trim(); - if (!trimmedUrl) { - return; - } + openPopout() { + closeWidget(); + if (!config.url) { + throw new Error('Config.url is not set!'); + } + api.popup = window.open( + `${config.url}${config.url.lastIndexOf('?') > -1 ? '&' : '?'}mode=popout`, + 'livechat-popout', + `width=${WIDGET_OPEN_WIDTH}, height=${widgetHeight}, toolbars=no`, + ); + api.popup?.focus(); + }, - config.url = trimmedUrl; + removeWidget() { + document.body.removeChild(widget as Node); + }, - createWidget(trimmedUrl); - attachMessageListener(); - trackNavigation(); -}; + callback(eventName, data) { + emitCallback(eventName, data); + }, -if (typeof window.initRocket !== 'undefined') { - console.warn('initRocket is now deprecated. Please update the livechat code.'); - init(window.initRocket[0]); -} + showWidget() { + if (!iframe) { + throw new Error('Widget is not initialized'); + } + iframe.style.display = 'initial'; + emitCallback('show-widget'); + }, -if (typeof window.RocketChat.url !== 'undefined') { - init(window.RocketChat.url); -} + hideWidget() { + if (!iframe) { + throw new Error('Widget is not initialized'); + } + iframe.style.display = 'none'; + emitCallback('hide-widget'); + }, -const queue = window.RocketChat._; + resetDocumentStyle() { + document.body.classList.remove('rc-livechat-mobile-full-screen'); + }, -window.RocketChat._.push = function (c) { - c.call(window.RocketChat.livechat); + setFullScreenDocumentMobile() { + smallScreen && document.body.classList.add('rc-livechat-mobile-full-screen'); + }, }; -window.RocketChat = window.RocketChat._.push; -// exports -window.RocketChat.livechat = { - // methods +const livechatWidgetAPI = { + // initParams + initialize, pageVisited, setCustomField, - initialize, setTheme, setDepartment, clearDepartment, @@ -478,45 +495,150 @@ window.RocketChat.livechat = { clearAllCallbacks, // callbacks - onChatMaximized(fn) { + onChatMaximized(fn: () => void) { registerCallback('chat-maximized', fn); }, - onChatMinimized(fn) { + onChatMinimized(fn: () => void) { registerCallback('chat-minimized', fn); }, - onChatStarted(fn) { + onChatStarted(fn: () => void) { registerCallback('chat-started', fn); }, - onChatEnded(fn) { + onChatEnded(fn: () => void) { registerCallback('chat-ended', fn); }, - onPrechatFormSubmit(fn) { + onPrechatFormSubmit( + fn: () => { + name: string; + email: string; + department?: string; + }, + ) { registerCallback('pre-chat-form-submit', fn); }, - onOfflineFormSubmit(fn) { + onOfflineFormSubmit( + fn: () => { + name: string; + email: string; + department?: string; + message: string; + }, + ) { registerCallback('offline-form-submit', fn); }, - onWidgetShown(fn) { + onWidgetShown(fn: () => void) { registerCallback('show-widget', fn); }, - onWidgetHidden(fn) { + onWidgetHidden(fn: () => void) { registerCallback('hide-widget', fn); }, - onAssignAgent(fn) { + onAssignAgent( + fn: () => { + name: string | undefined; + username: string | undefined; + status: UserStatus | undefined; + }, + ) { registerCallback('assign-agent', fn); }, - onAgentStatusChange(fn) { + onAgentStatusChange( + fn: () => { + name: string | undefined; + username: string | undefined; + status: UserStatus | undefined; + }, + ) { registerCallback('agent-status-change', fn); }, - onQueuePositionChange(fn) { + onQueuePositionChange(fn: () => LivechatRoomEvents<'queueData' | 'agentData'>) { registerCallback('queue-position-change', fn); }, - onServiceOffline(fn) { + onServiceOffline(fn: () => void) { registerCallback('no-agent-online', fn); }, }; +const currentPage: { href: string | null; title: string | null } = { + href: null, + title: null, +}; + +function onNewMessage(event: MessageEvent>>) { + if (event.source === event.target) { + return; + } + + if (!event.data || typeof event.data !== 'object') { + return; + } + + if (!event.data.src || event.data.src !== 'rocketchat') { + return; + } + + const { fn, args } = event.data; + + if (!api.hasOwnProperty(fn)) { + return; + } + + // There is an existing issue with overload resolution with type union arguments please see https://github.com/microsoft/TypeScript/issues/14107 + // @ts-expect-error: A spread argument must either have a tuple type or be passed to a rest parameter + api[fn](...args); +} + +const attachMessageListener = () => { + window.addEventListener('message', onNewMessage, false); +}; + +const trackNavigation = () => { + setInterval(() => { + if (document.location.href !== currentPage.href) { + pageVisited('url'); + currentPage.href = document.location.href; + } + + if (document.title !== currentPage.title) { + pageVisited('title'); + currentPage.title = document.title; + } + }, 800); +}; + +const init = (url: string) => { + const trimmedUrl = url.trim(); + if (!trimmedUrl) { + return; + } + + config.url = trimmedUrl; + + createWidget(trimmedUrl); + attachMessageListener(); + trackNavigation(); +}; + +if (typeof window.initRocket !== 'undefined') { + console.warn('initRocket is now deprecated. Please update the livechat code.'); + init(window.initRocket[0]); +} + +if (typeof window.RocketChat.url !== 'undefined') { + init(window.RocketChat.url); +} + +const queue = window.RocketChat._; + +window.RocketChat._.push = function (c: () => void) { + c.call(window.RocketChat.livechat); +}; + +window.RocketChat = window.RocketChat._.push; + +// exports +window.RocketChat.livechat = livechatWidgetAPI; + // proccess queue -queue.forEach((c) => { +queue.forEach((c: () => void) => { c.call(window.RocketChat.livechat); }); diff --git a/packages/livechat/webpack.config.ts b/packages/livechat/webpack.config.ts index 24bd213a4386..a33660198e87 100644 --- a/packages/livechat/webpack.config.ts +++ b/packages/livechat/webpack.config.ts @@ -162,7 +162,7 @@ const config = (_env: any, args: webpack.WebpackOptionsNormalized): webpack.Conf { ...common(args), entry: { - 'rocketchat-livechat.min': _('./src/widget.js'), + 'rocketchat-livechat.min': _('./src/widget.ts'), } as webpack.Entry, output: { path: _('./dist'), From 8a59f100573f01ec85f4b7e67c6da5ee5f8559f3 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Thu, 29 Feb 2024 15:03:09 -0300 Subject: [PATCH 076/207] chore: Upgrade `react-virtuoso` (#31843) --- .../CustomScrollbars.tsx} | 42 +++++++--------- .../CustomScrollbars/VirtuosoScrollbars.tsx | 23 +++++++++ .../components/CustomScrollbars/index.ts | 3 ++ .../components/GenericTable/GenericTable.tsx | 6 +-- .../components/Page/PageScrollableContent.tsx | 8 +-- .../client/components/Sidebar/Content.tsx | 6 +-- .../client/sidebar/RoomList/RoomList.tsx | 4 +- .../RoomList/ScrollerWithCustomProps.tsx | 21 -------- .../FederatedRoomList.tsx | 4 +- .../search/ScrollerWithCustomProps.tsx | 17 ------- .../client/sidebar/search/SearchList.tsx | 4 +- .../history/OutgoingWebhookHistoryPage.tsx | 6 +-- .../composer/EmojiPicker/CategoriesResult.tsx | 6 +-- .../composer/EmojiPicker/SearchingResult.tsx | 4 +- .../contactHistory/ContactHistoryList.tsx | 4 +- .../ContactHistoryMessagesList.tsx | 4 +- .../OutlookEventsList/OutlookEventsList.tsx | 4 +- .../client/views/room/body/RoomBody.tsx | 6 +-- .../Discussions/DiscussionsList.tsx | 4 +- .../room/contextualBar/MessageListTab.tsx | 4 +- .../components/MessageSearch.tsx | 4 +- .../contextualBar/RoomFiles/RoomFiles.tsx | 4 +- .../contextualBar/RoomMembers/RoomMembers.tsx | 4 +- .../room/contextualBar/Threads/ThreadList.tsx | 4 +- .../Threads/components/ThreadMessageList.tsx | 10 ++-- .../VideoConfList/VideoConfList.tsx | 4 +- .../channels/BaseTeamsChannels.tsx | 4 +- .../CannedResponse/CannedResponseList.tsx | 4 +- apps/meteor/package.json | 2 +- packages/uikit-playground/package.json | 2 +- yarn.lock | 50 +++---------------- 31 files changed, 106 insertions(+), 166 deletions(-) rename apps/meteor/client/components/{ScrollableContentWrapper.tsx => CustomScrollbars/CustomScrollbars.tsx} (57%) create mode 100644 apps/meteor/client/components/CustomScrollbars/VirtuosoScrollbars.tsx create mode 100644 apps/meteor/client/components/CustomScrollbars/index.ts delete mode 100644 apps/meteor/client/sidebar/RoomList/ScrollerWithCustomProps.tsx delete mode 100644 apps/meteor/client/sidebar/search/ScrollerWithCustomProps.tsx diff --git a/apps/meteor/client/components/ScrollableContentWrapper.tsx b/apps/meteor/client/components/CustomScrollbars/CustomScrollbars.tsx similarity index 57% rename from apps/meteor/client/components/ScrollableContentWrapper.tsx rename to apps/meteor/client/components/CustomScrollbars/CustomScrollbars.tsx index 16964e07426d..c98dcb0d7e65 100644 --- a/apps/meteor/client/components/ScrollableContentWrapper.tsx +++ b/apps/meteor/client/components/CustomScrollbars/CustomScrollbars.tsx @@ -1,15 +1,7 @@ import type { ScrollValues } from 'rc-scrollbars'; import { Scrollbars } from 'rc-scrollbars'; import type { MutableRefObject, CSSProperties, ReactNode, ReactElement } from 'react'; -import React, { useMemo, memo, forwardRef } from 'react'; - -const styleDefault: CSSProperties = { - width: '100%', - height: '100%', - flexGrow: 1, - willChange: 'transform', - overflowY: 'hidden', -}; +import React, { memo, forwardRef, useCallback } from 'react'; export type CustomScrollbarsProps = { overflowX?: boolean; @@ -21,11 +13,23 @@ export type CustomScrollbarsProps = { autoHide?: boolean; }; -const ScrollableContentWrapper = forwardRef(function ScrollableContentWrapper( - { children, style, onScroll, overflowX, renderView, ...props }, +const CustomScrollbars = forwardRef(function CustomScrollbars( + { children, onScroll, overflowX, renderView, ...props }, ref, ) { - const scrollbarsStyle = useMemo((): CSSProperties => ({ ...style, ...styleDefault }), [style]); + const refSetter = useCallback( + (scrollbarRef) => { + if (ref && scrollbarRef) { + if (typeof ref === 'function') { + ref(scrollbarRef.view ?? null); + return; + } + + (ref as MutableRefObject).current = scrollbarRef.view; + } + }, + [ref], + ); return ( ( autoHide autoHideTimeout={2000} autoHideDuration={500} - style={scrollbarsStyle} onScrollFrame={onScroll} renderView={renderView} renderTrackHorizontal={ @@ -43,18 +46,9 @@ const ScrollableContentWrapper = forwardRef(
          )} children={children} - ref={(sRef): void => { - if (ref && sRef) { - if (typeof ref === 'function') { - ref(sRef.view ?? null); - return; - } - - (ref as MutableRefObject).current = sRef.view; - } - }} + ref={refSetter} /> ); }); -export default memo(ScrollableContentWrapper); +export default memo(CustomScrollbars); diff --git a/apps/meteor/client/components/CustomScrollbars/VirtuosoScrollbars.tsx b/apps/meteor/client/components/CustomScrollbars/VirtuosoScrollbars.tsx new file mode 100644 index 000000000000..a7d0371e4ab8 --- /dev/null +++ b/apps/meteor/client/components/CustomScrollbars/VirtuosoScrollbars.tsx @@ -0,0 +1,23 @@ +import type { ComponentProps, ReactElement, Ref } from 'react'; +import React, { forwardRef } from 'react'; + +import CustomScrollbars from './CustomScrollbars'; + +type VirtuosoScrollbarsProps = ComponentProps; + +const VirtuosoScrollbars = forwardRef(function VirtuosoScrollbars( + { style, children, ...props }: VirtuosoScrollbarsProps, + ref: Ref, +): ReactElement { + return ( +
          } + > + {children} + + ); +}); + +export default VirtuosoScrollbars; diff --git a/apps/meteor/client/components/CustomScrollbars/index.ts b/apps/meteor/client/components/CustomScrollbars/index.ts new file mode 100644 index 000000000000..176bb6f81657 --- /dev/null +++ b/apps/meteor/client/components/CustomScrollbars/index.ts @@ -0,0 +1,3 @@ +export { default as CustomScrollbars } from './CustomScrollbars'; +export { default as VirtuosoScrollbars } from './VirtuosoScrollbars'; +export * from './CustomScrollbars'; diff --git a/apps/meteor/client/components/GenericTable/GenericTable.tsx b/apps/meteor/client/components/GenericTable/GenericTable.tsx index 6fa3f247aa29..05173fbe7004 100644 --- a/apps/meteor/client/components/GenericTable/GenericTable.tsx +++ b/apps/meteor/client/components/GenericTable/GenericTable.tsx @@ -2,7 +2,7 @@ import { Box, Table } from '@rocket.chat/fuselage'; import type { ComponentProps } from 'react'; import React, { type ForwardedRef, type ReactNode, forwardRef } from 'react'; -import ScrollableContentWrapper from '../ScrollableContentWrapper'; +import { CustomScrollbars } from '../CustomScrollbars'; type GenericTableProps = { fixed?: boolean; @@ -15,11 +15,11 @@ export const GenericTable = forwardRef(function GenericTable( ) { return ( - + {children}
          -
          +
          ); }); diff --git a/apps/meteor/client/components/Page/PageScrollableContent.tsx b/apps/meteor/client/components/Page/PageScrollableContent.tsx index 0bd954864d23..b1731f4eb0f6 100644 --- a/apps/meteor/client/components/Page/PageScrollableContent.tsx +++ b/apps/meteor/client/components/Page/PageScrollableContent.tsx @@ -3,8 +3,8 @@ import { Box } from '@rocket.chat/fuselage'; import type { ComponentProps } from 'react'; import React, { forwardRef } from 'react'; -import type { CustomScrollbarsProps } from '../ScrollableContentWrapper'; -import ScrollableContentWrapper from '../ScrollableContentWrapper'; +import type { CustomScrollbarsProps } from '../CustomScrollbars'; +import { CustomScrollbars } from '../CustomScrollbars'; type PageScrollableContentProps = { onScrollContent?: ComponentProps['onScrollContent']; @@ -24,9 +24,9 @@ const PageScrollableContent = forwardRef - + - + ); }); diff --git a/apps/meteor/client/components/Sidebar/Content.tsx b/apps/meteor/client/components/Sidebar/Content.tsx index 1057e00fe9fe..bccdec01b7d9 100644 --- a/apps/meteor/client/components/Sidebar/Content.tsx +++ b/apps/meteor/client/components/Sidebar/Content.tsx @@ -2,15 +2,15 @@ import { Box } from '@rocket.chat/fuselage'; import type { FC } from 'react'; import React from 'react'; -import ScrollableContentWrapper from '../ScrollableContentWrapper'; +import { CustomScrollbars } from '../CustomScrollbars'; const Content: FC = ({ children, ...props }) => ( - + {children} - + ); diff --git a/apps/meteor/client/sidebar/RoomList/RoomList.tsx b/apps/meteor/client/sidebar/RoomList/RoomList.tsx index 42c096c8a175..8dc44dcb73ad 100644 --- a/apps/meteor/client/sidebar/RoomList/RoomList.tsx +++ b/apps/meteor/client/sidebar/RoomList/RoomList.tsx @@ -7,6 +7,7 @@ import type { ReactElement } from 'react'; import React, { useMemo } from 'react'; import { Virtuoso } from 'react-virtuoso'; +import { VirtuosoScrollbars } from '../../components/CustomScrollbars'; import { useOpenedRoom } from '../../lib/RoomManager'; import { useAvatarTemplate } from '../hooks/useAvatarTemplate'; import { usePreventDefault } from '../hooks/usePreventDefault'; @@ -14,7 +15,6 @@ import { useRoomList } from '../hooks/useRoomList'; import { useShortcutOpenMenu } from '../hooks/useShortcutOpenMenu'; import { useTemplateByViewMode } from '../hooks/useTemplateByViewMode'; import RoomListRow from './RoomListRow'; -import ScrollerWithCustomProps from './ScrollerWithCustomProps'; const computeItemKey = (index: number, room: IRoom): IRoom['_id'] | number => room._id || index; @@ -121,7 +121,7 @@ const RoomList = (): ReactElement => { } /> diff --git a/apps/meteor/client/sidebar/RoomList/ScrollerWithCustomProps.tsx b/apps/meteor/client/sidebar/RoomList/ScrollerWithCustomProps.tsx deleted file mode 100644 index 20fc675378e4..000000000000 --- a/apps/meteor/client/sidebar/RoomList/ScrollerWithCustomProps.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import type { ComponentProps, ReactElement, Ref } from 'react'; -import React, { forwardRef } from 'react'; - -import ScrollableContentWrapper from '../../components/ScrollableContentWrapper'; - -type ScrollerWithCustomPropsProps = ComponentProps; - -export default forwardRef(function ScrollerWithCustomProps( - { style, ...props }: ScrollerWithCustomPropsProps, - ref: Ref, -): ReactElement { - return ( -
          } - renderTrackHorizontal={(props): ReactElement =>
          } - /> - ); -}); diff --git a/apps/meteor/client/sidebar/header/MatrixFederationSearch/FederatedRoomList.tsx b/apps/meteor/client/sidebar/header/MatrixFederationSearch/FederatedRoomList.tsx index 7584733a7022..61984030429d 100644 --- a/apps/meteor/client/sidebar/header/MatrixFederationSearch/FederatedRoomList.tsx +++ b/apps/meteor/client/sidebar/header/MatrixFederationSearch/FederatedRoomList.tsx @@ -6,7 +6,7 @@ import type { VFC } from 'react'; import React from 'react'; import { Virtuoso } from 'react-virtuoso'; -import ScrollableContentWrapper from '../../../components/ScrollableContentWrapper'; +import { VirtuosoScrollbars } from '../../../components/CustomScrollbars'; import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; import FederatedRoomListEmptyPlaceholder from './FederatedRoomListEmptyPlaceholder'; import FederatedRoomListItem from './FederatedRoomListItem'; @@ -68,7 +68,7 @@ const FederatedRoomList: VFC = ({ serverName, roomName, components={{ // eslint-disable-next-line react/no-multi-comp Footer: () => (isFetchingNextPage ? : null), - Scroller: ScrollableContentWrapper, + Scroller: VirtuosoScrollbars, EmptyPlaceholder: FederatedRoomListEmptyPlaceholder, }} endReached={isLoading || isFetchingNextPage ? () => undefined : () => fetchNextPage()} diff --git a/apps/meteor/client/sidebar/search/ScrollerWithCustomProps.tsx b/apps/meteor/client/sidebar/search/ScrollerWithCustomProps.tsx deleted file mode 100644 index 176eed7c4f1f..000000000000 --- a/apps/meteor/client/sidebar/search/ScrollerWithCustomProps.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { ReactElement } from 'react'; -import React, { forwardRef } from 'react'; - -import ScrollableContentWrapper from '../../components/ScrollableContentWrapper'; - -const ScrollerWithCustomProps = forwardRef(function ScrollerWithCustomProps(props, ref: React.Ref) { - return ( -
          } - renderTrackHorizontal={(props): ReactElement =>
          } - /> - ); -}); - -export default ScrollerWithCustomProps; diff --git a/apps/meteor/client/sidebar/search/SearchList.tsx b/apps/meteor/client/sidebar/search/SearchList.tsx index 82d6d12c6213..ceb89d3d7092 100644 --- a/apps/meteor/client/sidebar/search/SearchList.tsx +++ b/apps/meteor/client/sidebar/search/SearchList.tsx @@ -12,12 +12,12 @@ import type { VirtuosoHandle } from 'react-virtuoso'; import { Virtuoso } from 'react-virtuoso'; import tinykeys from 'tinykeys'; +import { VirtuosoScrollbars } from '../../components/CustomScrollbars'; import { getConfig } from '../../lib/utils/getConfig'; import { useAvatarTemplate } from '../hooks/useAvatarTemplate'; import { usePreventDefault } from '../hooks/usePreventDefault'; import { useTemplateByViewMode } from '../hooks/useTemplateByViewMode'; import Row from './Row'; -import ScrollerWithCustomProps from './ScrollerWithCustomProps'; const mobileCheck = function () { let check = false; @@ -366,7 +366,7 @@ const SearchList = forwardRef(function SearchList({ onClose }: SearchListProps, style={{ height: '100%', width: '100%' }} totalCount={items.length} data={items} - components={{ Scroller: ScrollerWithCustomProps }} + components={{ Scroller: VirtuosoScrollbars }} computeItemKey={(_, room) => room._id} itemContent={(_, data): ReactElement => } ref={listRef} diff --git a/apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx b/apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx index 8b7b96d711f8..ac05b3bf0ddd 100644 --- a/apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx +++ b/apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx @@ -5,9 +5,9 @@ import type { ComponentProps } from 'react'; import React, { useMemo, useState, useEffect } from 'react'; import { sdk } from '../../../../../../app/utils/client/lib/SDKClient'; +import { CustomScrollbars } from '../../../../../components/CustomScrollbars'; import { usePagination } from '../../../../../components/GenericTable/hooks/usePagination'; import { Page, PageHeader, PageContent } from '../../../../../components/Page'; -import ScrollableContentWrapper from '../../../../../components/ScrollableContentWrapper'; import HistoryContent from './HistoryContent'; const OutgoingWebhookHistoryPage = (props: ComponentProps) => { @@ -117,9 +117,9 @@ const OutgoingWebhookHistoryPage = (props: ComponentProps) => { - + - + void; handleSelectEmoji: (event: MouseEvent) => void; - handleScroll: UIEventHandler<'div'>; + handleScroll: UIEventHandler; }; const CategoriesResult = forwardRef(function CategoriesResult( @@ -39,7 +39,7 @@ const CategoriesResult = forwardRef(funct totalCount={emojiListByCategory.length} data={emojiListByCategory} onScroll={handleScroll} - components={{ Scroller: ScrollableContentWrapper }} + components={{ Scroller: VirtuosoScrollbars }} isScrolling={(isScrolling: boolean) => { if (!wrapper.current) { return; diff --git a/apps/meteor/client/views/composer/EmojiPicker/SearchingResult.tsx b/apps/meteor/client/views/composer/EmojiPicker/SearchingResult.tsx index a229ab2717e4..a42e1b0580b3 100644 --- a/apps/meteor/client/views/composer/EmojiPicker/SearchingResult.tsx +++ b/apps/meteor/client/views/composer/EmojiPicker/SearchingResult.tsx @@ -6,7 +6,7 @@ import type { VirtuosoGridHandle } from 'react-virtuoso'; import { VirtuosoGrid } from 'react-virtuoso'; import type { EmojiItem } from '../../../../app/emoji/client'; -import ScrollableContentWrapper from '../../../components/ScrollableContentWrapper'; +import { VirtuosoScrollbars } from '../../../components/CustomScrollbars'; import EmojiElement from './EmojiElement'; import SearchingResultWrapper from './SearchingResultWrapper'; @@ -33,7 +33,7 @@ const SearchingResult = ({ searchResults, handleSelectEmoji }: SearchingResultPr ref={ref} totalCount={searchResults.length} components={{ - Scroller: ScrollableContentWrapper, + Scroller: VirtuosoScrollbars, List: SearchingResultWrapper, }} itemContent={(index) => { diff --git a/apps/meteor/client/views/omnichannel/contactHistory/ContactHistoryList.tsx b/apps/meteor/client/views/omnichannel/contactHistory/ContactHistoryList.tsx index 3281e4ff2f28..ba25d511e1a8 100644 --- a/apps/meteor/client/views/omnichannel/contactHistory/ContactHistoryList.tsx +++ b/apps/meteor/client/views/omnichannel/contactHistory/ContactHistoryList.tsx @@ -12,7 +12,7 @@ import { ContextualbarClose, ContextualbarEmptyContent, } from '../../../components/Contextualbar'; -import ScrollableContentWrapper from '../../../components/ScrollableContentWrapper'; +import { VirtuosoScrollbars } from '../../../components/CustomScrollbars'; import { useRecordList } from '../../../hooks/lists/useRecordList'; import { AsyncStatePhase } from '../../../lib/asyncState'; import { useOmnichannelRoom } from '../../room/contexts/RoomContext'; @@ -86,7 +86,7 @@ const ContactHistoryList = ({ setChatId, close }: { setChatId: Dispatch } /> )} diff --git a/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx b/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx index 80e042273a06..3bf6385a19a0 100644 --- a/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx +++ b/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx @@ -13,7 +13,7 @@ import { ContextualbarContent, ContextualbarEmptyContent, } from '../../../../components/Contextualbar'; -import ScrollableContentWrapper from '../../../../components/ScrollableContentWrapper'; +import { VirtuosoScrollbars } from '../../../../components/CustomScrollbars'; import { useRecordList } from '../../../../hooks/lists/useRecordList'; import { AsyncStatePhase } from '../../../../lib/asyncState'; import { isMessageNewDay } from '../../../room/MessageList/lib/isMessageNewDay'; @@ -98,7 +98,7 @@ const ContactHistoryMessagesList = ({ } overscan={25} data={messages} - components={{ Scroller: ScrollableContentWrapper as any }} + components={{ Scroller: VirtuosoScrollbars }} itemContent={(index, data): ReactElement => { const lastMessage = messages[index - 1]; const isSequential = isMessageSequential(data, lastMessage, messageGroupingPeriod); diff --git a/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventsList.tsx b/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventsList.tsx index 47d2d77668cd..286028e0059d 100644 --- a/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventsList.tsx +++ b/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventsList.tsx @@ -14,7 +14,7 @@ import { ContextualbarFooter, ContextualbarSkeleton, } from '../../../components/Contextualbar'; -import ScrollableContentWrapper from '../../../components/ScrollableContentWrapper'; +import { VirtuosoScrollbars } from '../../../components/CustomScrollbars'; import { getErrorMessage } from '../../../lib/errorHandling'; import { useOutlookAuthentication } from '../hooks/useOutlookAuthentication'; import { useMutationOutlookCalendarSync, useOutlookCalendarListForToday } from '../hooks/useOutlookCalendarList'; @@ -108,7 +108,7 @@ const OutlookEventsList = ({ onClose, changeRoute }: OutlookEventsListProps): Re totalCount={total} overscan={25} data={calendarEvents} - components={{ Scroller: ScrollableContentWrapper }} + components={{ Scroller: VirtuosoScrollbars }} itemContent={(_index, calendarData): ReactElement => } /> diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index 983695b1fa70..5dd6d9e6bb67 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -20,7 +20,7 @@ import { isAtBottom } from '../../../../app/ui/client/views/app/lib/scrolling'; import { callbacks } from '../../../../lib/callbacks'; import { isTruthy } from '../../../../lib/isTruthy'; import { withDebouncing, withThrottling } from '../../../../lib/utils/highOrderFunctions'; -import ScrollableContentWrapper from '../../../components/ScrollableContentWrapper'; +import { CustomScrollbars } from '../../../components/CustomScrollbars'; import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout'; import { useReactiveQuery } from '../../../hooks/useReactiveQuery'; import { RoomManager } from '../../../lib/RoomManager'; @@ -604,7 +604,7 @@ const RoomBody = (): ReactElement => { .join(' ')} > - +
            {
          • {isLoadingMoreMessages ? : null}
          • ) : null}
          -
          +
          diff --git a/apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsList.tsx b/apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsList.tsx index bad42d57dce4..e521a263372b 100644 --- a/apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsList.tsx +++ b/apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsList.tsx @@ -13,7 +13,7 @@ import { ContextualbarClose, ContextualbarEmptyContent, } from '../../../../components/Contextualbar'; -import ScrollableContentWrapper from '../../../../components/ScrollableContentWrapper'; +import { VirtuosoScrollbars } from '../../../../components/CustomScrollbars'; import { goToRoomById } from '../../../../lib/utils/goToRoomById'; import DiscussionsListRow from './DiscussionsListRow'; @@ -106,7 +106,7 @@ function DiscussionsList({ endReached={loading ? () => undefined : (start) => loadMoreItems(start, Math.min(50, total - start))} overscan={25} data={discussions} - components={{ Scroller: ScrollableContentWrapper }} + components={{ Scroller: VirtuosoScrollbars }} itemContent={(_, data) => ( )} diff --git a/apps/meteor/client/views/room/contextualBar/MessageListTab.tsx b/apps/meteor/client/views/room/contextualBar/MessageListTab.tsx index 2cac2a84c1cb..bd6c8da508af 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageListTab.tsx +++ b/apps/meteor/client/views/room/contextualBar/MessageListTab.tsx @@ -17,7 +17,7 @@ import { ContextualbarClose, ContextualbarEmptyContent, } from '../../../components/Contextualbar'; -import ScrollableContentWrapper from '../../../components/ScrollableContentWrapper'; +import { VirtuosoScrollbars } from '../../../components/CustomScrollbars'; import RoomMessage from '../../../components/message/variants/RoomMessage'; import SystemMessage from '../../../components/message/variants/SystemMessage'; import { useFormatDate } from '../../../hooks/useFormatDate'; @@ -71,7 +71,7 @@ const MessageListTab = ({ iconName, title, emptyResultMessage, context, queryRes totalCount={queryResult.data.length} overscan={25} data={queryResult.data} - components={{ Scroller: ScrollableContentWrapper }} + components={{ Scroller: VirtuosoScrollbars }} itemContent={(index, message) => { const previous = queryResult.data[index - 1]; diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx index e3c8753a2282..576502fa357d 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx @@ -6,7 +6,7 @@ import { Virtuoso } from 'react-virtuoso'; import { MessageTypes } from '../../../../../../app/ui-utils/client'; import { ContextualbarEmptyContent } from '../../../../../components/Contextualbar'; -import ScrollableContentWrapper from '../../../../../components/ScrollableContentWrapper'; +import { VirtuosoScrollbars } from '../../../../../components/CustomScrollbars'; import RoomMessage from '../../../../../components/message/variants/RoomMessage'; import SystemMessage from '../../../../../components/message/variants/SystemMessage'; import { useFormatDate } from '../../../../../hooks/useFormatDate'; @@ -45,7 +45,7 @@ const MessageSearch = ({ searchText, globalSearch }: MessageSearchProps): ReactE totalCount={messageSearchQuery.data.length} overscan={25} data={messageSearchQuery.data} - components={{ Scroller: ScrollableContentWrapper }} + components={{ Scroller: VirtuosoScrollbars }} itemContent={(index, message) => { const previous = messageSearchQuery.data[index - 1]; diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx index a04c4f71dc3e..b18fc41e41da 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx @@ -14,7 +14,7 @@ import { ContextualbarContent, ContextualbarEmptyContent, } from '../../../../components/Contextualbar'; -import ScrollableContentWrapper from '../../../../components/ScrollableContentWrapper'; +import { VirtuosoScrollbars } from '../../../../components/CustomScrollbars'; import FileItem from './components/FileItem'; type RoomFilesProps = { @@ -97,7 +97,7 @@ const RoomFiles = ({ endReached={(start) => loadMoreItems(start, Math.min(50, total - start))} overscan={50} data={filesItems} - components={{ Scroller: ScrollableContentWrapper }} + components={{ Scroller: VirtuosoScrollbars }} itemContent={(_, data) => } /> diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx index 816bab42a192..cdbd8329eaa1 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx @@ -16,8 +16,8 @@ import { ContextualbarFooter, ContextualbarEmptyContent, } from '../../../../components/Contextualbar'; +import { VirtuosoScrollbars } from '../../../../components/CustomScrollbars'; import InfiniteListAnchor from '../../../../components/InfiniteListAnchor'; -import ScrollableContentWrapper from '../../../../components/ScrollableContentWrapper'; import RoomMembersRow from './RoomMembersRow'; type RoomMemberUser = Pick; @@ -138,7 +138,7 @@ const RoomMembers = ({ overscan={50} data={members} // eslint-disable-next-line react/no-multi-comp - components={{ Scroller: ScrollableContentWrapper, Footer: () => }} + components={{ Scroller: VirtuosoScrollbars, Footer: () => }} itemContent={(index, data): ReactElement => ( )} diff --git a/apps/meteor/client/views/room/contextualBar/Threads/ThreadList.tsx b/apps/meteor/client/views/room/contextualBar/Threads/ThreadList.tsx index 063cb8cc4768..575aefbda14b 100644 --- a/apps/meteor/client/views/room/contextualBar/Threads/ThreadList.tsx +++ b/apps/meteor/client/views/room/contextualBar/Threads/ThreadList.tsx @@ -14,7 +14,7 @@ import { ContextualbarTitle, ContextualbarEmptyContent, } from '../../../../components/Contextualbar'; -import ScrollableContentWrapper from '../../../../components/ScrollableContentWrapper'; +import { VirtuosoScrollbars } from '../../../../components/CustomScrollbars'; import { useRecordList } from '../../../../hooks/lists/useRecordList'; import { AsyncStatePhase } from '../../../../lib/asyncState'; import type { ThreadsListOptions } from '../../../../lib/lists/ThreadsList'; @@ -181,7 +181,7 @@ const ThreadList: VFC = () => { } overscan={25} data={items} - components={{ Scroller: ScrollableContentWrapper }} + components={{ Scroller: VirtuosoScrollbars }} itemContent={(_index, data: IMessage): ReactElement => ( - +
            {loading ? (
          • @@ -121,7 +117,7 @@ const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElemen )}
          -
          +
          ); }; diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfList.tsx b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfList.tsx index 132a3a0d5857..33f60b24752d 100644 --- a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfList.tsx +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfList.tsx @@ -15,7 +15,7 @@ import { ContextualbarContent, ContextualbarEmptyContent, } from '../../../../../components/Contextualbar'; -import ScrollableContentWrapper from '../../../../../components/ScrollableContentWrapper'; +import { VirtuosoScrollbars } from '../../../../../components/CustomScrollbars'; import { getErrorMessage } from '../../../../../lib/errorHandling'; import VideoConfListItem from './VideoConfListItem'; @@ -78,7 +78,7 @@ const VideoConfList = ({ onClose, total, videoConfs, loading, error, reload, loa endReached={(start): unknown => loadMoreItems(start, Math.min(50, total - start))} overscan={25} data={videoConfs} - components={{ Scroller: ScrollableContentWrapper as any }} + components={{ Scroller: VirtuosoScrollbars }} itemContent={(_index, data): ReactElement => } /> diff --git a/apps/meteor/client/views/teams/contextualBar/channels/BaseTeamsChannels.tsx b/apps/meteor/client/views/teams/contextualBar/channels/BaseTeamsChannels.tsx index e94b2ac9a2fb..ed0a83d39fce 100644 --- a/apps/meteor/client/views/teams/contextualBar/channels/BaseTeamsChannels.tsx +++ b/apps/meteor/client/views/teams/contextualBar/channels/BaseTeamsChannels.tsx @@ -16,8 +16,8 @@ import { ContextualbarFooter, ContextualbarEmptyContent, } from '../../../../components/Contextualbar'; +import { VirtuosoScrollbars } from '../../../../components/CustomScrollbars'; import InfiniteListAnchor from '../../../../components/InfiniteListAnchor'; -import ScrollableContentWrapper from '../../../../components/ScrollableContentWrapper'; import Row from './Row'; type BaseTeamsChannelsProps = { @@ -125,7 +125,7 @@ const BaseTeamsChannels = ({ totalCount={total} data={channels} // eslint-disable-next-line react/no-multi-comp - components={{ Scroller: ScrollableContentWrapper, Footer: () => }} + components={{ Scroller: VirtuosoScrollbars, Footer: () => }} itemContent={(index, data) => } /> diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx index dd8b078c82da..b9d3e57e31de 100644 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx @@ -14,7 +14,7 @@ import { ContextualbarInnerContent, ContextualbarFooter, } from '../../../../../../client/components/Contextualbar'; -import ScrollableContentWrapper from '../../../../../../client/components/ScrollableContentWrapper'; +import { VirtuosoScrollbars } from '../../../../../../client/components/CustomScrollbars'; import { useRoomToolbox } from '../../../../../../client/views/room/contexts/RoomToolboxContext'; import Item from './Item'; import WrapCannedResponse from './WrapCannedResponse'; @@ -97,7 +97,7 @@ const CannedResponseList: FC<{ overscan={25} data={cannedItems} components={{ - Scroller: ScrollableContentWrapper, + Scroller: VirtuosoScrollbars, }} itemContent={(_index, data): ReactElement => ( =16" - checksum: 173e91c21f6a8cd506ad3b72af10656897fe1951124ed9eeb1fd85575534993bea2f97cba3f81c08ae1e88a2613df348e2c80d0ceecb3021f8c8c8fe0e053ee2 - languageName: node - linkType: hard - -"@virtuoso.dev/urx@npm:^0.2.13, @virtuoso.dev/urx@npm:^0.2.5": - version: 0.2.13 - resolution: "@virtuoso.dev/urx@npm:0.2.13" - checksum: 682a99cf40ccc429241268dd37495cd1ed4695ae58b5a1169c75df1630d5dc3fd8eb3aaa655f71c37f39ba9c23c0aaf4401b76d8a986986d1a38a422d596a6ba - languageName: node - linkType: hard - "@vitejs/plugin-react@npm:^4.0.0": version: 4.0.0 resolution: "@vitejs/plugin-react@npm:4.0.0" @@ -35125,26 +35107,13 @@ __metadata: languageName: node linkType: hard -"react-virtuoso@npm:^1.11.1": - version: 1.11.1 - resolution: "react-virtuoso@npm:1.11.1" - dependencies: - "@virtuoso.dev/react-urx": ^0.2.5 - "@virtuoso.dev/urx": ^0.2.5 - resize-observer-polyfill: ^1.5.1 - peerDependencies: - react: ">=16" - checksum: 05c66c8c543c029de94df92904aa7fb86f4beb3354348e03a7c3a109ea01de35b5861df1a7d58b29819daf81df10c9f88b45c88313ecbf872bb7db5503cf4bf8 - languageName: node - linkType: hard - -"react-virtuoso@npm:^4.3.10": - version: 4.3.10 - resolution: "react-virtuoso@npm:4.3.10" +"react-virtuoso@npm:^4.7.1": + version: 4.7.1 + resolution: "react-virtuoso@npm:4.7.1" peerDependencies: react: ">=16 || >=17 || >= 18" react-dom: ">=16 || >=17 || >= 18" - checksum: 1a2747d4b5abcba114ad9a27db4a4057464219c84cef4ae032460ab44f304073e4a115f56f93658844a4e3f76feb8175536618555ecf201bfd61e627a3e8cfb4 + checksum: c864095bd875825c2ce25b69f165fe29ad7bea2d54c31b407b3b99347f6b68311a6928566369f45641cd2f5a6e8540fbafca87d808c0495c12196cb1665145d5 languageName: node linkType: hard @@ -35919,13 +35888,6 @@ __metadata: languageName: node linkType: hard -"resize-observer-polyfill@npm:^1.5.1": - version: 1.5.1 - resolution: "resize-observer-polyfill@npm:1.5.1" - checksum: 57e7f79489867b00ba43c9c051524a5c8f162a61d5547e99333549afc23e15c44fd43f2f318ea0261ea98c0eb3158cca261e6f48d66e1ed1cd1f340a43977094 - languageName: node - linkType: hard - "resolve-cwd@npm:^3.0.0": version: 3.0.0 resolution: "resolve-cwd@npm:3.0.0" From 8b10c6cf0f48153a1c685bd1c00ac42b1c219166 Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Thu, 29 Feb 2024 16:08:22 -0300 Subject: [PATCH 077/207] fix: Login page breaking when handling not expected errors (#31804) --- .changeset/quick-cheetahs-help.md | 5 +++ .../web-ui-registration/src/LoginForm.tsx | 36 ++++++++++++------- .../web-ui-registration/src/LoginServices.tsx | 4 +-- .../src/LoginServicesButton.tsx | 8 ++--- 4 files changed, 35 insertions(+), 18 deletions(-) create mode 100644 .changeset/quick-cheetahs-help.md diff --git a/.changeset/quick-cheetahs-help.md b/.changeset/quick-cheetahs-help.md new file mode 100644 index 000000000000..12005d73ec1a --- /dev/null +++ b/.changeset/quick-cheetahs-help.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/web-ui-registration": patch +--- + +fixed the login page crashing when receiving unexpected errors diff --git a/packages/web-ui-registration/src/LoginForm.tsx b/packages/web-ui-registration/src/LoginForm.tsx index 3290b9f16191..d63886091fa0 100644 --- a/packages/web-ui-registration/src/LoginForm.tsx +++ b/packages/web-ui-registration/src/LoginForm.tsx @@ -56,7 +56,9 @@ const LOGIN_SUBMIT_ERRORS = { }, } as const; -export type LoginErrors = keyof typeof LOGIN_SUBMIT_ERRORS | 'totp-canceled'; +export type LoginErrors = keyof typeof LOGIN_SUBMIT_ERRORS | 'totp-canceled' | string; + +export type LoginErrorState = [error: LoginErrors, message?: string] | undefined; export const LoginForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRouter }): ReactElement => { const { @@ -72,7 +74,7 @@ export const LoginForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRoute const { t } = useTranslation(); const formLabelId = useUniqueId(); - const [errorOnSubmit, setErrorOnSubmit] = useState(undefined); + const [errorOnSubmit, setErrorOnSubmit] = useState(undefined); const isResetPasswordAllowed = useSetting('Accounts_PasswordReset'); const login = useLoginWithPassword(); const showFormLogin = useSetting('Accounts_ShowFormLogin'); @@ -92,11 +94,11 @@ export const LoginForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRoute } if ('error' in error && error.error !== 403) { - setErrorOnSubmit(error.error); + setErrorOnSubmit([error.error, error.reason]); return; } - setErrorOnSubmit('user-not-found'); + setErrorOnSubmit(['user-not-found']); }, }); @@ -110,18 +112,28 @@ export const LoginForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRoute } }, [errorOnSubmit]); - const renderErrorOnSubmit = (error: LoginErrors) => { + const renderErrorOnSubmit = ([error, message]: Exclude) => { + if (error in LOGIN_SUBMIT_ERRORS) { + const { type, i18n } = LOGIN_SUBMIT_ERRORS[error as Exclude]; + return ( + + {t(i18n)} + + ); + } + if (error === 'totp-canceled') { return null; } - const { type, i18n } = LOGIN_SUBMIT_ERRORS[error]; - - return ( - - {t(i18n)} - - ); + if (message) { + return ( + + {message} + + ); + } + return null; }; if (errors.usernameOrEmail?.type === 'invalid-email') { diff --git a/packages/web-ui-registration/src/LoginServices.tsx b/packages/web-ui-registration/src/LoginServices.tsx index 530dce7e9438..a5068fbaac3f 100644 --- a/packages/web-ui-registration/src/LoginServices.tsx +++ b/packages/web-ui-registration/src/LoginServices.tsx @@ -3,7 +3,7 @@ import { useLoginServices, useSetting } from '@rocket.chat/ui-contexts'; import type { Dispatch, ReactElement, SetStateAction } from 'react'; import { useTranslation } from 'react-i18next'; -import type { LoginErrors } from './LoginForm'; +import type { LoginErrorState } from './LoginForm'; import LoginServicesButton from './LoginServicesButton'; const LoginServices = ({ @@ -11,7 +11,7 @@ const LoginServices = ({ setError, }: { disabled?: boolean; - setError: Dispatch>; + setError: Dispatch>; }): ReactElement | null => { const { t } = useTranslation(); const services = useLoginServices(); diff --git a/packages/web-ui-registration/src/LoginServicesButton.tsx b/packages/web-ui-registration/src/LoginServicesButton.tsx index cdba5e26474e..d9f43b0e484c 100644 --- a/packages/web-ui-registration/src/LoginServicesButton.tsx +++ b/packages/web-ui-registration/src/LoginServicesButton.tsx @@ -5,7 +5,7 @@ import { useLoginWithService, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement, SetStateAction, Dispatch } from 'react'; import { useCallback } from 'react'; -import type { LoginErrors } from './LoginForm'; +import type { LoginErrorState, LoginErrors } from './LoginForm'; const LoginServicesButton = ({ buttonLabelText, @@ -19,17 +19,17 @@ const LoginServicesButton = ({ }: T & { className?: string; disabled?: boolean; - setError?: Dispatch>; + setError?: Dispatch>; }): ReactElement => { const t = useTranslation(); const handler = useLoginWithService({ service, buttonLabelText, ...props }); const handleOnClick = useCallback(() => { - handler().catch((e: { error?: LoginErrors }) => { + handler().catch((e: { error?: LoginErrors; reason?: string }) => { if (!e.error || typeof e.error !== 'string') { return; } - setError?.(e.error); + setError?.([e.error, e.reason]); }); }, [handler, setError]); From 86296724a1bf8fdbe354fb9112e4a6d6a32a2d81 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Thu, 29 Feb 2024 18:08:00 -0300 Subject: [PATCH 078/207] chore: `RoomHeader` keyboard navigability (#31837) --- .../client/views/room/Header/ParentRoom.tsx | 9 ++- .../client/views/room/Header/ParentTeam.tsx | 12 ++- .../client/views/room/Header/RoomTitle.tsx | 48 ++++++++--- .../views/room/Header/icons/Favorite.tsx | 28 ++++--- .../client/views/room/body/LeaderBar.tsx | 2 +- .../tests/e2e/channel-management.spec.ts | 79 +++++++++++++++---- apps/meteor/tests/e2e/messaging.spec.ts | 5 +- .../page-objects/fragments/home-content.ts | 2 +- apps/meteor/tests/e2e/team-management.spec.ts | 10 +++ .../components/Header/HeaderTag/HeaderTag.tsx | 2 +- .../components/Header/HeaderTitleButton.tsx | 25 ++++++ .../ui-client/src/components/Header/index.ts | 1 + 12 files changed, 177 insertions(+), 46 deletions(-) create mode 100644 packages/ui-client/src/components/Header/HeaderTitleButton.tsx diff --git a/apps/meteor/client/views/room/Header/ParentRoom.tsx b/apps/meteor/client/views/room/Header/ParentRoom.tsx index 00b181f47df5..3d598cce4c26 100644 --- a/apps/meteor/client/views/room/Header/ParentRoom.tsx +++ b/apps/meteor/client/views/room/Header/ParentRoom.tsx @@ -13,10 +13,15 @@ type ParentRoomProps = { const ParentRoom = ({ room }: ParentRoomProps): ReactElement => { const icon = useRoomIcon(room); - const handleClick = (): void => roomCoordinator.openRouteLink(room.t, { rid: room._id, ...room }); + const handleRedirect = (): void => roomCoordinator.openRouteLink(room.t, { rid: room._id, ...room }); return ( - + (e.code === 'Space' || e.code === 'Enter') && handleRedirect()} + onClick={handleRedirect} + > {roomCoordinator.getRoomName(room.t, room)} diff --git a/apps/meteor/client/views/room/Header/ParentTeam.tsx b/apps/meteor/client/views/room/Header/ParentTeam.tsx index 2f1f15327c79..33ef98bbe81b 100644 --- a/apps/meteor/client/views/room/Header/ParentTeam.tsx +++ b/apps/meteor/client/views/room/Header/ParentTeam.tsx @@ -41,11 +41,14 @@ const ParentTeam = ({ room }: { room: IRoom }): ReactElement | null => { const redirectToMainRoom = (): void => { const rid = teamInfoData?.teamInfo.roomId; - if (!rid) { return; } + if (!(isTeamPublic || userBelongsToTeam)) { + return; + } + goToRoomById(rid); }; @@ -58,7 +61,12 @@ const ParentTeam = ({ room }: { room: IRoom }): ReactElement | null => { } return ( - + (e.code === 'Space' || e.code === 'Enter') && redirectToMainRoom()} + onClick={redirectToMainRoom} + > {teamInfoData?.teamInfo.name} diff --git a/apps/meteor/client/views/room/Header/RoomTitle.tsx b/apps/meteor/client/views/room/Header/RoomTitle.tsx index 13b628ab3823..4d81d077c154 100644 --- a/apps/meteor/client/views/room/Header/RoomTitle.tsx +++ b/apps/meteor/client/views/room/Header/RoomTitle.tsx @@ -1,22 +1,50 @@ -import type { IRoom } from '@rocket.chat/core-typings'; -import { HeaderTitle, useDocumentTitle } from '@rocket.chat/ui-client'; -import type { ReactElement } from 'react'; +import { isTeamRoom, type IRoom } from '@rocket.chat/core-typings'; +import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { HeaderTitle, HeaderTitleButton, useDocumentTitle } from '@rocket.chat/ui-client'; +import type { KeyboardEvent, ReactElement } from 'react'; import React from 'react'; +import { useRoomToolbox } from '../contexts/RoomToolboxContext'; import HeaderIconWithRoom from './HeaderIconWithRoom'; -type RoomTitleProps = { - room: IRoom; -}; - -const RoomTitle = ({ room }: RoomTitleProps): ReactElement => { +const RoomTitle = ({ room }: { room: IRoom }): ReactElement => { useDocumentTitle(room.name, false); + const { openTab } = useRoomToolbox(); + + const handleOpenRoomInfo = useEffectEvent(() => { + if (isTeamRoom(room)) { + return openTab('team-info'); + } + + switch (room.t) { + case 'l': + openTab('room-info'); + break; + + case 'v': + openTab('voip-room-info'); + break; + + case 'd': + (room.uids?.length ?? 0) > 2 ? openTab('user-info-group') : openTab('user-info'); + break; + + default: + openTab('channel-settings'); + break; + } + }); return ( - <> + (e.code === 'Enter' || e.code === 'Space') && handleOpenRoomInfo()} + onClick={() => handleOpenRoomInfo()} + tabIndex={0} + role='button' + > {room.name} - + ); }; diff --git a/apps/meteor/client/views/room/Header/icons/Favorite.tsx b/apps/meteor/client/views/room/Header/icons/Favorite.tsx index f66af3443ed8..4a99a7a0411e 100644 --- a/apps/meteor/client/views/room/Header/icons/Favorite.tsx +++ b/apps/meteor/client/views/room/Header/icons/Favorite.tsx @@ -1,39 +1,41 @@ import type { IRoom, ISubscription } from '@rocket.chat/core-typings'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { HeaderState } from '@rocket.chat/ui-client'; import { useSetting, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; import React, { memo } from 'react'; import { useUserIsSubscribed } from '../../contexts/RoomContext'; -const Favorite = ({ room: { _id, f: favorite = false, t: type } }: { room: IRoom & { f?: ISubscription['f'] } }) => { +const Favorite = ({ room: { _id, f: favorite = false, t: type, name } }: { room: IRoom & { f?: ISubscription['f'] } }) => { const t = useTranslation(); const subscribed = useUserIsSubscribed(); const isFavoritesEnabled = useSetting('Favorite_Rooms') && ['c', 'p', 'd', 't'].includes(type); const toggleFavorite = useMethod('toggleFavorite'); - const handleFavoriteClick = useMutableCallback(() => { + + const handleFavoriteClick = useEffectEvent(() => { if (!isFavoritesEnabled) { return; } + toggleFavorite(_id, !favorite); }); - const favoriteLabel = favorite ? t('Unfavorite') : t('Favorite'); + + const favoriteLabel = favorite ? `${t('Unfavorite')} ${name}` : `${t('Favorite')} ${name}`; if (!subscribed || !isFavoritesEnabled) { return null; } return ( - isFavoritesEnabled && ( - - ) + ); }; diff --git a/apps/meteor/client/views/room/body/LeaderBar.tsx b/apps/meteor/client/views/room/body/LeaderBar.tsx index 4cf5266b89d8..bb0ba305633a 100644 --- a/apps/meteor/client/views/room/body/LeaderBar.tsx +++ b/apps/meteor/client/views/room/body/LeaderBar.tsx @@ -66,7 +66,7 @@ const LeaderBar = ({ _id, name, username, visible, onAvatarClick, triggerProps } className={[roomLeaderStyle, 'room-leader', !visible && 'animated-hidden'].filter(isTruthy)} > - + diff --git a/apps/meteor/tests/e2e/channel-management.spec.ts b/apps/meteor/tests/e2e/channel-management.spec.ts index 96d50bae6151..f76752085771 100644 --- a/apps/meteor/tests/e2e/channel-management.spec.ts +++ b/apps/meteor/tests/e2e/channel-management.spec.ts @@ -11,6 +11,7 @@ test.use({ storageState: Users.admin.state }); test.describe.serial('channel-management', () => { let poHomeChannel: HomeChannel; let targetChannel: string; + let discussionName: string; test.beforeAll(async ({ api }) => { targetChannel = await createTargetChannel(api); @@ -56,7 +57,8 @@ test.describe.serial('channel-management', () => { await expect(page.getByRole('button', { name: 'Start call' })).toBeFocused(); }); - test('expect add "user1" to "targetChannel"', async () => { + // FIXME: bad assertion + test('should add "user1" to "targetChannel"', async () => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.tabs.btnTabMembers.click(); await poHomeChannel.tabs.members.showAllUsers(); @@ -65,7 +67,8 @@ test.describe.serial('channel-management', () => { await expect(poHomeChannel.toastSuccess).toBeVisible(); }); - test('expect create invite to the room', async () => { + // FIXME: bad assertion + test('should create invite to the room', async () => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.tabs.btnTabMembers.click(); await poHomeChannel.tabs.members.inviteUser(); @@ -73,28 +76,29 @@ test.describe.serial('channel-management', () => { await expect(poHomeChannel.toastSuccess).toBeVisible(); }); - test('expect mute "user1"', async () => { + test.fixme('should mute "user1"', async () => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.tabs.btnTabMembers.click(); await poHomeChannel.tabs.members.showAllUsers(); await poHomeChannel.tabs.members.muteUser('user1'); }); - test('expect set "user1" as owner', async () => { + test.fixme('should set "user1" as owner', async () => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.tabs.btnTabMembers.click(); await poHomeChannel.tabs.members.showAllUsers(); await poHomeChannel.tabs.members.setUserAsOwner('user1'); }); - test('expect set "user1" as moderator', async () => { + + test.fixme('should set "user1" as moderator', async () => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.tabs.btnTabMembers.click(); await poHomeChannel.tabs.members.showAllUsers(); await poHomeChannel.tabs.members.setUserAsModerator('user1'); }); - test('expect edit topic of "targetChannel"', async () => { + test.fixme('should edit topic of "targetChannel"', async () => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.tabs.btnRoomInfo.click(); await poHomeChannel.tabs.room.btnEdit.click(); @@ -102,7 +106,7 @@ test.describe.serial('channel-management', () => { await poHomeChannel.tabs.room.btnSave.click(); }); - test('expect edit announcement of "targetChannel"', async () => { + test.fixme('should edit announcement of "targetChannel"', async () => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.tabs.btnRoomInfo.click(); await poHomeChannel.tabs.room.btnEdit.click(); @@ -110,7 +114,7 @@ test.describe.serial('channel-management', () => { await poHomeChannel.tabs.room.btnSave.click(); }); - test('expect edit description of "targetChannel"', async () => { + test.fixme('should edit description of "targetChannel"', async () => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.tabs.btnRoomInfo.click(); await poHomeChannel.tabs.room.btnEdit.click(); @@ -118,18 +122,65 @@ test.describe.serial('channel-management', () => { await poHomeChannel.tabs.room.btnSave.click(); }); - test('expect edit name of "targetChannel"', async ({ page }) => { + test('should edit name of "targetChannel"', async ({ page }) => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.tabs.btnRoomInfo.click(); await poHomeChannel.tabs.room.btnEdit.click(); await poHomeChannel.tabs.room.inputName.fill(`NAME-EDITED-${targetChannel}`); await poHomeChannel.tabs.room.btnSave.click(); - await poHomeChannel.sidenav.openChat(`NAME-EDITED-${targetChannel}`); - await expect(page).toHaveURL(`/channel/NAME-EDITED-${targetChannel}`); + targetChannel = `NAME-EDITED-${targetChannel}`; + await poHomeChannel.sidenav.openChat(targetChannel); + + await expect(page).toHaveURL(`/channel/${targetChannel}`); + }); + + test('should truncate the room name for small screens', async ({ page }) => { + const hugeName = faker.string.alpha(100); + await poHomeChannel.sidenav.openChat(targetChannel); + await poHomeChannel.tabs.btnRoomInfo.click(); + await poHomeChannel.tabs.room.btnEdit.click(); + await poHomeChannel.tabs.room.inputName.fill(hugeName); + await poHomeChannel.tabs.room.btnSave.click(); + targetChannel = hugeName; + + await page.setViewportSize({ width: 640, height: 460 }); + await expect(page.getByRole('heading', { name: hugeName })).toHaveCSS('width', '423px'); + }); + + test('should info contextualbar when clicking on roomName', async ({ page }) => { + await poHomeChannel.sidenav.openChat(targetChannel); + await page.getByRole('button', { name: targetChannel }).first().focus(); + await page.keyboard.press('Space'); + await page.getByRole('complementary').waitFor(); + + await expect(page.getByRole('complementary')).toBeVisible(); + }); + + test('should create a discussion using the message composer', async ({ page }) => { + discussionName = faker.string.uuid(); + await poHomeChannel.sidenav.openChat(targetChannel); + await poHomeChannel.content.btnMenuMoreActions.click(); + await page.getByRole('menuitem', { name: 'Discussion' }).click(); + await page.getByRole('textbox', { name: 'Discussion name' }).fill(discussionName); + await page.getByRole('button', { name: 'Create' }).click(); + + await expect(page.getByRole('heading', { name: discussionName })).toBeVisible(); + }); + + test('should access targetTeam through discussion header', async ({ page }) => { + await poHomeChannel.sidenav.openChat(targetChannel); + await page.locator('[data-qa-type="message"]', { hasText: discussionName }).locator('button').first().click(); + await page.getByRole('button', { name: discussionName }).first().focus(); + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await page.keyboard.press('Space'); + + await expect(page).toHaveURL(`/channel/${targetChannel}`); }); - test.skip('expect edit notification preferences of "targetChannel"', async () => { + // FIXME: bad assertion + test.fixme('should edit notification preferences of "targetChannel"', async () => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.tabs.kebab.click({ force: true }); await poHomeChannel.tabs.btnNotificationPreferences.click({ force: true }); @@ -140,7 +191,7 @@ test.describe.serial('channel-management', () => { }); let regularUserPage: Page; - test('expect "readOnlyChannel" to show join button', async ({ browser }) => { + test('should "readOnlyChannel" show join button', async ({ browser }) => { const channelName = faker.string.uuid(); await poHomeChannel.sidenav.openNewByLabel('Channel'); @@ -160,7 +211,7 @@ test.describe.serial('channel-management', () => { await regularUserPage.close(); }); - test.skip('expect all notification preferences of "targetChannel" to be "Mentions"', async () => { + test.fixme('should all notification preferences of "targetChannel" to be "Mentions"', async () => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.tabs.kebab.click({ force: true }); await poHomeChannel.tabs.btnNotificationPreferences.click({ force: true }); diff --git a/apps/meteor/tests/e2e/messaging.spec.ts b/apps/meteor/tests/e2e/messaging.spec.ts index 4fa248e72183..ab31dcba2729 100644 --- a/apps/meteor/tests/e2e/messaging.spec.ts +++ b/apps/meteor/tests/e2e/messaging.spec.ts @@ -38,13 +38,14 @@ test.describe.serial('Messaging', () => { await page.keyboard.press('ArrowDown'); await expect(page.locator('[data-qa-type="message"]:has-text("msg1")')).toBeFocused(); - // move focus to the favorite icon + // move focus to the room title await page.keyboard.press('Shift+Tab'); - await expect(poHomeChannel.roomHeaderFavoriteBtn).toBeFocused(); + await expect(page.getByRole('button', { name: targetChannel }).first()).toBeFocused(); // refocus on the first typed message await page.keyboard.press('Tab'); await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); await expect(page.locator('[data-qa-type="message"]:has-text("msg1")')).toBeFocused(); // move focus to the message toolbar diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts index 2c739ae6667d..5435985fedb3 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts @@ -153,7 +153,7 @@ export class HomeContent { } get btnMenuMoreActions() { - return this.page.locator('[data-qa-id="menu-more-actions"]'); + return this.page.getByRole('button', { name: 'More actions' }); } get userCard(): Locator { diff --git a/apps/meteor/tests/e2e/team-management.spec.ts b/apps/meteor/tests/e2e/team-management.spec.ts index 338f5c5eb0ef..11609ec51ed6 100644 --- a/apps/meteor/tests/e2e/team-management.spec.ts +++ b/apps/meteor/tests/e2e/team-management.spec.ts @@ -89,4 +89,14 @@ test.describe.serial('teams-management', () => { await poHomeTeam.tabs.channels.btnAdd.click(); await expect(page.locator('//main//aside >> li')).toContainText(targetChannel); }); + + test('should access team channel through "targetTeam" header', async ({ page }) => { + await poHomeTeam.sidenav.openChat(targetChannel); + await page.getByRole('button', { name: targetChannel }).first().focus(); + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await page.keyboard.press('Space'); + + await expect(page).toHaveURL(`/group/${targetTeam}`); + }); }); diff --git a/packages/ui-client/src/components/Header/HeaderTag/HeaderTag.tsx b/packages/ui-client/src/components/Header/HeaderTag/HeaderTag.tsx index c921f2b93525..44661229707c 100644 --- a/packages/ui-client/src/components/Header/HeaderTag/HeaderTag.tsx +++ b/packages/ui-client/src/components/Header/HeaderTag/HeaderTag.tsx @@ -2,7 +2,7 @@ import { Box, Tag } from '@rocket.chat/fuselage'; import type { ComponentProps, FC } from 'react'; const HeaderTag: FC> = ({ children, ...props }) => ( - + {children} diff --git a/packages/ui-client/src/components/Header/HeaderTitleButton.tsx b/packages/ui-client/src/components/Header/HeaderTitleButton.tsx new file mode 100644 index 000000000000..c27f20ac9e09 --- /dev/null +++ b/packages/ui-client/src/components/Header/HeaderTitleButton.tsx @@ -0,0 +1,25 @@ +import { css } from '@rocket.chat/css-in-js'; +import { Box, Palette } from '@rocket.chat/fuselage'; +import type { ComponentProps } from 'react'; + +const HeaderTitleButton = ({ className, ...props }: { className?: string } & ComponentProps) => { + const customClass = css` + border-width: 1px; + border-style: solid; + border-color: transparent; + + &:hover { + cursor: pointer; + background-color: ${Palette.surface['surface-hover']}; + } + &:focus.focus-visible { + outline: 0; + box-shadow: 0 0 0 2px ${Palette.stroke['stroke-extra-light-highlight']}; + border-color: ${Palette.stroke['stroke-highlight']}; + } + `; + + return ; +}; + +export default HeaderTitleButton; diff --git a/packages/ui-client/src/components/Header/index.ts b/packages/ui-client/src/components/Header/index.ts index b8d62fb777f9..00e2c0ab17dc 100644 --- a/packages/ui-client/src/components/Header/index.ts +++ b/packages/ui-client/src/components/Header/index.ts @@ -8,4 +8,5 @@ export { default as HeaderState } from './HeaderState'; export { default as HeaderSubtitle } from './HeaderSubtitle'; export * from './HeaderTag'; export { default as HeaderTitle } from './HeaderTitle'; +export { default as HeaderTitleButton } from './HeaderTitleButton'; export * from './HeaderToolbar'; From 299aa15dc551d8fa343fae7cd8df49d3755b9e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Thu, 29 Feb 2024 20:39:42 -0300 Subject: [PATCH 079/207] feat: show date on message's scroll (#31572) Co-authored-by: Guilherme Gazzo <5263975+ggazzo@users.noreply.github.com> --- .changeset/nine-ads-hide.md | 5 + .../views/room/MessageList/MessageList.tsx | 76 +++---------- .../room/MessageList/MessageListItem.tsx | 92 +++++++++++++++ apps/meteor/client/views/room/Room.tsx | 59 +++++----- .../client/views/room/body/RoomBody.tsx | 27 ++++- .../room/body/RoomForeword/RoomForeword.tsx | 4 +- .../Threads/components/ThreadChat.tsx | 69 ++++++------ .../Threads/components/ThreadMessageItem.tsx | 67 +++++++++++ .../Threads/components/ThreadMessageList.tsx | 78 +++++++------ .../client/views/room/hooks/useDateScroll.ts | 105 ++++++++++++++++++ .../views/room/hooks/useScrollMessageList.ts | 4 +- .../views/room/providers/DateListProvider.tsx | 46 ++++++++ 12 files changed, 470 insertions(+), 162 deletions(-) create mode 100644 .changeset/nine-ads-hide.md create mode 100644 apps/meteor/client/views/room/MessageList/MessageListItem.tsx create mode 100644 apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageItem.tsx create mode 100644 apps/meteor/client/views/room/hooks/useDateScroll.ts create mode 100644 apps/meteor/client/views/room/providers/DateListProvider.tsx diff --git a/.changeset/nine-ads-hide.md b/.changeset/nine-ads-hide.md new file mode 100644 index 000000000000..b09886787279 --- /dev/null +++ b/.changeset/nine-ads-hide.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": minor +--- + +feat: show date on message's scroll diff --git a/apps/meteor/client/views/room/MessageList/MessageList.tsx b/apps/meteor/client/views/room/MessageList/MessageList.tsx index 5a3a267b73dc..b39981d44bb7 100644 --- a/apps/meteor/client/views/room/MessageList/MessageList.tsx +++ b/apps/meteor/client/views/room/MessageList/MessageList.tsx @@ -1,20 +1,15 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { isThreadMessage } from '@rocket.chat/core-typings'; -import { MessageDivider } from '@rocket.chat/fuselage'; -import { useSetting, useTranslation, useUserPreference } from '@rocket.chat/ui-contexts'; -import type { ReactElement, ComponentProps } from 'react'; -import React, { Fragment, memo } from 'react'; +import { useSetting, useUserPreference } from '@rocket.chat/ui-contexts'; +import type { ComponentProps } from 'react'; +import React, { Fragment, forwardRef } from 'react'; import { MessageTypes } from '../../../../app/ui-utils/client'; -import RoomMessage from '../../../components/message/variants/RoomMessage'; -import SystemMessage from '../../../components/message/variants/SystemMessage'; -import ThreadMessagePreview from '../../../components/message/variants/ThreadMessagePreview'; -import { useFormatDate } from '../../../hooks/useFormatDate'; import { useRoomSubscription } from '../contexts/RoomContext'; import { useFirstUnreadMessageId } from '../hooks/useFirstUnreadMessageId'; import { SelectedMessagesProvider } from '../providers/SelectedMessagesProvider'; +import { MessageListItem } from './MessageListItem'; import { useMessages } from './hooks/useMessages'; -import { isMessageNewDay } from './lib/isMessageNewDay'; import { isMessageSequential } from './lib/isMessageSequential'; import MessageListProvider from './providers/MessageListProvider'; @@ -23,14 +18,11 @@ type MessageListProps = { scrollMessageList: ComponentProps['scrollMessageList']; }; -export const MessageList = ({ rid, scrollMessageList }: MessageListProps): ReactElement => { - const t = useTranslation(); +export const MessageList = forwardRef(function MessageList({ rid, scrollMessageList }: MessageListProps) { const messages = useMessages({ rid }); const subscription = useRoomSubscription(); const showUserAvatar = !!useUserPreference('displayAvatars'); const messageGroupingPeriod = Number(useSetting('Message_GroupingPeriod')); - const formatDate = useFormatDate(); - const firstUnreadMessageId = useFirstUnreadMessageId(); return ( @@ -38,62 +30,26 @@ export const MessageList = ({ rid, scrollMessageList }: MessageListProps): React {messages.map((message, index, { [index - 1]: previous }) => { const sequential = isMessageSequential(message, previous, messageGroupingPeriod); - - const newDay = isMessageNewDay(message, previous); - const showUnreadDivider = firstUnreadMessageId === message._id; - - const showDivider = newDay || showUnreadDivider; - - const shouldShowAsSequential = sequential && !newDay; - const system = MessageTypes.isSystemMessage(message); const visible = !isThreadMessage(message) && !system; - const unread = Boolean(subscription?.tunread?.includes(message._id)); - const mention = Boolean(subscription?.tunreadUser?.includes(message._id)); - const all = Boolean(subscription?.tunreadGroup?.includes(message._id)); - const ignoredUser = Boolean(subscription?.ignored?.includes(message.u._id)); - return ( - {showDivider && ( - - {newDay && formatDate(message.ts)} - - )} - - {visible && ( - - )} - - {isThreadMessage(message) && ( - - )} - - {system && } + ); })} ); -}; - -export default memo(MessageList); +}); diff --git a/apps/meteor/client/views/room/MessageList/MessageListItem.tsx b/apps/meteor/client/views/room/MessageList/MessageListItem.tsx new file mode 100644 index 000000000000..538290c8a7ad --- /dev/null +++ b/apps/meteor/client/views/room/MessageList/MessageListItem.tsx @@ -0,0 +1,92 @@ +import { isThreadMessage, type IMessage, type ISubscription } from '@rocket.chat/core-typings'; +import { Box, Bubble, MessageDivider } from '@rocket.chat/fuselage'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +import RoomMessage from '../../../components/message/variants/RoomMessage'; +import SystemMessage from '../../../components/message/variants/SystemMessage'; +import ThreadMessagePreview from '../../../components/message/variants/ThreadMessagePreview'; +import { useFormatDate } from '../../../hooks/useFormatDate'; +import { useDateRef } from '../providers/DateListProvider'; +import { isMessageNewDay } from './lib/isMessageNewDay'; + +type MessageListItemProps = { + message: IMessage; + previous?: IMessage; + showUnreadDivider: boolean; + + sequential: boolean; + showUserAvatar: boolean; + visible: boolean; + subscription: ISubscription | undefined; + system: boolean; +}; +export const MessageListItem = ({ + message, + previous, + showUnreadDivider, + sequential, + showUserAvatar, + visible, + subscription, + system, +}: MessageListItemProps) => { + const t = useTranslation(); + const formatDate = useFormatDate(); + + const ref = useDateRef(); + + const newDay = isMessageNewDay(message, previous); + const showDivider = newDay || showUnreadDivider; + const unread = Boolean(subscription?.tunread?.includes(message._id)); + const mention = Boolean(subscription?.tunreadUser?.includes(message._id)); + const all = Boolean(subscription?.tunreadGroup?.includes(message._id)); + const ignoredUser = Boolean(subscription?.ignored?.includes(message.u._id)); + const shouldShowAsSequential = sequential && !newDay; + + return ( + <> + {showDivider && ( + + + {newDay && ( + + {formatDate(message.ts)} + + )} + + + )} + {visible && ( + + )} + {isThreadMessage(message) && ( + + )} + {system && } + + ); +}; diff --git a/apps/meteor/client/views/room/Room.tsx b/apps/meteor/client/views/room/Room.tsx index 4d6d7d56f9d5..8dcc76a20e57 100644 --- a/apps/meteor/client/views/room/Room.tsx +++ b/apps/meteor/client/views/room/Room.tsx @@ -13,6 +13,7 @@ import { useRoomToolbox } from './contexts/RoomToolboxContext'; import { useAppsContextualBar } from './hooks/useAppsContextualBar'; import RoomLayout from './layout/RoomLayout'; import ChatProvider from './providers/ChatProvider'; +import { DateListProvider } from './providers/DateListProvider'; import { SelectedMessagesProvider } from './providers/SelectedMessagesProvider'; const UiKitContextualBar = lazy(() => import('./contextualBar/uikit/UiKitContextualBar')); @@ -27,34 +28,36 @@ const Room = (): ReactElement => { - } - body={} - aside={ - (toolbox.tab?.tabComponent && ( - - - }>{createElement(toolbox.tab.tabComponent)} - - - )) || - (contextualBarView && ( - - - }> - - - - - )) - } - /> + + } + body={} + aside={ + (toolbox.tab?.tabComponent && ( + + + }>{createElement(toolbox.tab.tabComponent)} + + + )) || + (contextualBarView && ( + + + }> + + + + + )) + } + /> + diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index 5dd6d9e6bb67..83cbf184b1bd 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -1,5 +1,6 @@ import type { IMessage, IUser } from '@rocket.chat/core-typings'; import { isEditedMessage } from '@rocket.chat/core-typings'; +import { Box, Bubble } from '@rocket.chat/fuselage'; import { usePermission, useRole, @@ -22,13 +23,14 @@ import { isTruthy } from '../../../../lib/isTruthy'; import { withDebouncing, withThrottling } from '../../../../lib/utils/highOrderFunctions'; import { CustomScrollbars } from '../../../components/CustomScrollbars'; import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout'; +import { useFormatDate } from '../../../hooks/useFormatDate'; import { useReactiveQuery } from '../../../hooks/useReactiveQuery'; import { RoomManager } from '../../../lib/RoomManager'; import type { Upload } from '../../../lib/chats/Upload'; import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; import { setMessageJumpQueryStringParameter } from '../../../lib/utils/setMessageJumpQueryStringParameter'; import Announcement from '../Announcement'; -import { MessageList } from '../MessageList/MessageList'; +import { MessageList } from '../MessageList'; import MessageListErrorBoundary from '../MessageList/MessageListErrorBoundary'; import ComposerContainer from '../composer/ComposerContainer'; import RoomComposer from '../composer/RoomComposer/RoomComposer'; @@ -36,8 +38,10 @@ import { useChat } from '../contexts/ChatContext'; import { useRoom, useRoomSubscription, useRoomMessages } from '../contexts/RoomContext'; import { useRoomToolbox } from '../contexts/RoomToolboxContext'; import { useUserCard } from '../contexts/UserCardContext'; +import { useDateScroll } from '../hooks/useDateScroll'; import { useMessageListNavigation } from '../hooks/useMessageListNavigation'; import { useScrollMessageList } from '../hooks/useScrollMessageList'; +import { useDateListController } from '../providers/DateListProvider'; import DropTargetOverlay from './DropTargetOverlay'; import JumpToRecentMessageButton from './JumpToRecentMessageButton'; import LeaderBar from './LeaderBar'; @@ -53,7 +57,10 @@ import { useRestoreScrollPosition } from './hooks/useRestoreScrollPosition'; import { useRetentionPolicy } from './hooks/useRetentionPolicy'; import { useUnreadMessages } from './hooks/useUnreadMessages'; +const BUBBLE_OFFSET = 8; + const RoomBody = (): ReactElement => { + const formatDate = useFormatDate(); const t = useTranslation(); const isLayoutEmbedded = useEmbeddedLayout(); const room = useRoom(); @@ -62,6 +69,9 @@ const RoomBody = (): ReactElement => { const admin = useRole('admin'); const subscription = useRoomSubscription(); + const { list } = useDateListController(); + const { listStyle, bubbleDate, onScroll: handleDateOnScroll, showBubble, style: bubbleDateStyle } = useDateScroll(BUBBLE_OFFSET); + const [lastMessageDate, setLastMessageDate] = useState(); const [hideLeaderHeader, setHideLeaderHeader] = useState(false); const [hasNewMessages, setHasNewMessages] = useState(false); @@ -380,12 +390,14 @@ const RoomBody = (): ReactElement => { wrapper.addEventListener('scroll', updateUnreadCount); wrapper.addEventListener('scroll', handleWrapperScroll); + wrapper.addEventListener('scroll', () => handleDateOnScroll(list)); return () => { wrapper.removeEventListener('scroll', updateUnreadCount); wrapper.removeEventListener('scroll', handleWrapperScroll); + wrapper.removeEventListener('scroll', () => handleDateOnScroll(list)); }; - }, [_isAtBottom, room._id, setUnreadCount]); + }, [_isAtBottom, handleDateOnScroll, list, room._id, setUnreadCount]); useEffect(() => { const wrapper = wrapperRef.current; @@ -540,7 +552,7 @@ const RoomBody = (): ReactElement => { return ( <> {!isLayoutEmbedded && room.announcement && } -
          +
          { /> ))}
          + {bubbleDate && ( + + + {formatDate(bubbleDate)} + + + )} {unread && ( {
          -
          + ); }; diff --git a/apps/meteor/client/views/room/body/RoomForeword/RoomForeword.tsx b/apps/meteor/client/views/room/body/RoomForeword/RoomForeword.tsx index 63ba8f895e6c..a765407ad6d4 100644 --- a/apps/meteor/client/views/room/body/RoomForeword/RoomForeword.tsx +++ b/apps/meteor/client/views/room/body/RoomForeword/RoomForeword.tsx @@ -20,7 +20,7 @@ const RoomForeword = ({ user, room }: RoomForewordProps): ReactElement | null => if (!isDirectMessageRoom(room)) { return ( - + {t('Start_of_conversation')} ); @@ -33,7 +33,7 @@ const RoomForeword = ({ user, room }: RoomForewordProps): ReactElement | null => } return ( - + {usernames.map((username, index) => ( diff --git a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadChat.tsx b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadChat.tsx index 510d4f3e8017..52c16308206a 100644 --- a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadChat.tsx +++ b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadChat.tsx @@ -15,6 +15,7 @@ import RoomComposer from '../../../composer/RoomComposer/RoomComposer'; import { useChat } from '../../../contexts/ChatContext'; import { useRoom, useRoomSubscription } from '../../../contexts/RoomContext'; import { useRoomToolbox } from '../../../contexts/RoomToolboxContext'; +import { DateListProvider } from '../../../providers/DateListProvider'; import ThreadMessageList from './ThreadMessageList'; type ThreadChatProps = { @@ -93,39 +94,41 @@ const ThreadChat = ({ mainMessage }: ThreadChatProps) => { return ( - - - - - - - - - - - setSendToChannel((checked) => !checked)} - name='alsoSendThreadToChannel' - /> - - {t('Also_send_to_channel')} - - - - - - + + + + + + + + + + + + setSendToChannel((checked) => !checked)} + name='alsoSendThreadToChannel' + /> + + {t('Also_send_to_channel')} + + + + + + + ); }; diff --git a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageItem.tsx b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageItem.tsx new file mode 100644 index 000000000000..1a7da3e0ea63 --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageItem.tsx @@ -0,0 +1,67 @@ +import type { IThreadMainMessage, IThreadMessage } from '@rocket.chat/core-typings'; +import { Box, Bubble, MessageDivider } from '@rocket.chat/fuselage'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +import SystemMessage from '../../../../../components/message/variants/SystemMessage'; +import ThreadMessage from '../../../../../components/message/variants/ThreadMessage'; +import { useFormatDate } from '../../../../../hooks/useFormatDate'; +import { isMessageNewDay } from '../../../MessageList/lib/isMessageNewDay'; +import { useDateRef } from '../../../providers/DateListProvider'; + +type ThreadMessageProps = { + message: IThreadMessage | IThreadMainMessage; + previous: IThreadMessage | IThreadMainMessage; + sequential: boolean; + shouldShowAsSequential: boolean; + showUserAvatar: boolean; + firstUnread: boolean; + system: boolean; +}; + +export const ThreadMessageItem = ({ + message, + previous, + shouldShowAsSequential, + showUserAvatar, + firstUnread, + system, +}: ThreadMessageProps) => { + const t = useTranslation(); + const formatDate = useFormatDate(); + const ref = useDateRef(); + + const newDay = isMessageNewDay(message, previous); + + const showDivider = newDay || firstUnread; + + return ( + <> + {showDivider && ( + + + {newDay && ( + + {formatDate(message.ts)} + + )} + + + )} +
        • + {system ? ( + + ) : ( + + )} +
        • + + ); +}; diff --git a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx index 48b764d51816..249a71ec4f54 100644 --- a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx +++ b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx @@ -1,7 +1,7 @@ import type { IMessage, IThreadMainMessage } from '@rocket.chat/core-typings'; -import { MessageDivider } from '@rocket.chat/fuselage'; +import { Box, Bubble } from '@rocket.chat/fuselage'; import { useMergedRefs } from '@rocket.chat/fuselage-hooks'; -import { useSetting, useTranslation, useUserPreference } from '@rocket.chat/ui-contexts'; +import { useSetting, useUserPreference } from '@rocket.chat/ui-contexts'; import { differenceInSeconds } from 'date-fns'; import type { ReactElement } from 'react'; import React, { Fragment } from 'react'; @@ -9,18 +9,21 @@ import React, { Fragment } from 'react'; import { MessageTypes } from '../../../../../../app/ui-utils/client'; import { isTruthy } from '../../../../../../lib/isTruthy'; import { CustomScrollbars } from '../../../../../components/CustomScrollbars'; -import SystemMessage from '../../../../../components/message/variants/SystemMessage'; -import ThreadMessage from '../../../../../components/message/variants/ThreadMessage'; import { useFormatDate } from '../../../../../hooks/useFormatDate'; import { isMessageNewDay } from '../../../MessageList/lib/isMessageNewDay'; import MessageListProvider from '../../../MessageList/providers/MessageListProvider'; import LoadingMessagesIndicator from '../../../body/LoadingMessagesIndicator'; +import { useDateScroll } from '../../../hooks/useDateScroll'; import { useFirstUnreadMessageId } from '../../../hooks/useFirstUnreadMessageId'; import { useMessageListNavigation } from '../../../hooks/useMessageListNavigation'; import { useScrollMessageList } from '../../../hooks/useScrollMessageList'; +import { useDateListController } from '../../../providers/DateListProvider'; import { useLegacyThreadMessageJump } from '../hooks/useLegacyThreadMessageJump'; import { useLegacyThreadMessageListScrolling } from '../hooks/useLegacyThreadMessageListScrolling'; import { useLegacyThreadMessages } from '../hooks/useLegacyThreadMessages'; +import { ThreadMessageItem } from './ThreadMessageItem'; + +const BUBBLE_OFFSET = 64; const isMessageSequential = (current: IMessage, previous: IMessage | undefined, groupingRange: number): boolean => { if (!previous) { @@ -50,6 +53,10 @@ type ThreadMessageListProps = { }; const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElement => { + const formatDate = useFormatDate(); + const { list } = useDateListController(); + const { listStyle, bubbleDate, onScroll: handleDateOnScroll, showBubble, style: bubbleDateStyle } = useDateScroll(BUBBLE_OFFSET); + const { messages, loading } = useLegacyThreadMessages(mainMessage._id); const { listWrapperRef: listWrapperScrollRef, @@ -60,22 +67,37 @@ const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElemen const hideUsernames = useUserPreference('hideUsernames'); const showUserAvatar = !!useUserPreference('displayAvatars'); - - const formatDate = useFormatDate(); - const t = useTranslation(); + const firstUnreadMessageId = useFirstUnreadMessageId(); const messageGroupingPeriod = Number(useSetting('Message_GroupingPeriod')); const scrollMessageList = useScrollMessageList(listWrapperScrollRef); - - const firstUnreadMessageId = useFirstUnreadMessageId(); const { messageListRef, messageListProps } = useMessageListNavigation(); - const listRef = useMergedRefs(listScrollRef, listJumpRef, messageListRef); return (
          - -
            + {bubbleDate && ( + + + {formatDate(bubbleDate)} + + + )} + { + handleScroll(args); + handleDateOnScroll(list); + }} + style={{ scrollBehavior: 'smooth', overflowX: 'hidden' }} + > + {loading ? (
          • @@ -85,38 +107,28 @@ const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElemen {[mainMessage, ...messages].map((message, index, { [index - 1]: previous }) => { const sequential = isMessageSequential(message, previous, messageGroupingPeriod); const newDay = isMessageNewDay(message, previous); - const firstUnread = firstUnreadMessageId === message._id; - const showDivider = newDay || firstUnread; - const shouldShowAsSequential = sequential && !newDay; + const firstUnread = firstUnreadMessageId === message._id; const system = MessageTypes.isSystemMessage(message); return ( - {showDivider && ( - - {newDay && formatDate(message.ts)} - - )} -
          • - {system ? ( - - ) : ( - - )} -
          • + ); })} )} -
          +
          ); diff --git a/apps/meteor/client/views/room/hooks/useDateScroll.ts b/apps/meteor/client/views/room/hooks/useDateScroll.ts new file mode 100644 index 000000000000..62e69f10b6b0 --- /dev/null +++ b/apps/meteor/client/views/room/hooks/useDateScroll.ts @@ -0,0 +1,105 @@ +import { css } from '@rocket.chat/css-in-js'; +import { useSafely } from '@rocket.chat/fuselage-hooks'; +import { useCallback, useState } from 'react'; + +import { withThrottling } from '../../../../lib/utils/highOrderFunctions'; + +type useDateScrollReturn = { + bubbleDate: string | undefined; + onScroll: (list: Set) => void; + style: ReturnType; + showBubble: boolean; + listStyle?: ReturnType; +}; + +export const useDateScroll = (offset = 0): useDateScrollReturn => { + const [bubbleDate, setBubbleDate] = useSafely( + useState<{ + date: string; + show: boolean; + }>({ + date: '', + show: false, + }), + ); + + const onScroll = useCallback( + withThrottling({ wait: 30 })( + (() => { + let timeout: ReturnType; + return (elements: Set) => { + clearTimeout(timeout); + + const bubbleOffset = offset + 56; + // Gets the first non visible message date and sets the bubble date to it + const [date, message] = [...elements].reduce((ret, message) => { + // Sanitize elements + if (!message.dataset.id) { + return ret; + } + + const { top } = message.getBoundingClientRect(); + const { id } = message.dataset; + + if (top < bubbleOffset) { + // Remove T - . : from the date + return [new Date(id).toISOString(), message]; + } + return ret; + }, [] as [string, HTMLElement] | []); + + // We always keep the previous date if we don't have a new one, so when the bubble disappears it doesn't flicker + setBubbleDate(() => ({ + date: '', + ...(date && { date }), + show: Boolean(date), + })); + + if (message) { + const { top } = message.getBoundingClientRect(); + + if (top - offset > 0) { + return; + } + } + + timeout = setTimeout( + () => + setBubbleDate((current) => ({ + ...current, + show: false, + })), + 1000, + ); + }; + })(), + ), + [offset], + ); + + const dateBubbleStyle = css` + position: absolute; + top: ${offset}px; + left: 50%; + translate: -50%; + z-index: 1; + + opacity: 0; + transition: opacity 0.6s; + + &.bubble-visible { + opacity: 1; + } + `; + + const listStyle = + bubbleDate.show && bubbleDate.date + ? css` + & [data-time='${bubbleDate.date.replaceAll(/[-T:.]/g, '').substring(0, 8)}'] { + opacity: 0; + } + ` + : undefined; + + return { listStyle, bubbleDate: bubbleDate.date, onScroll, style: dateBubbleStyle, showBubble: Boolean(bubbleDate.show) }; +}; diff --git a/apps/meteor/client/views/room/hooks/useScrollMessageList.ts b/apps/meteor/client/views/room/hooks/useScrollMessageList.ts index 489ecbced48e..4b324dbcb0fa 100644 --- a/apps/meteor/client/views/room/hooks/useScrollMessageList.ts +++ b/apps/meteor/client/views/room/hooks/useScrollMessageList.ts @@ -1,11 +1,11 @@ import type { ComponentProps, RefObject } from 'react'; import { useCallback } from 'react'; -import type { MessageList } from '../MessageList'; +import type MessageListProvider from '../MessageList/providers/MessageListProvider'; export const useScrollMessageList = ( wrapperRef: RefObject, -): Exclude['scrollMessageList'], undefined> => { +): Exclude['scrollMessageList'], undefined> => { // Passing a callback instead of the values so that the wrapper is exposed return useCallback( (callback) => { diff --git a/apps/meteor/client/views/room/providers/DateListProvider.tsx b/apps/meteor/client/views/room/providers/DateListProvider.tsx new file mode 100644 index 000000000000..43fd70943b45 --- /dev/null +++ b/apps/meteor/client/views/room/providers/DateListProvider.tsx @@ -0,0 +1,46 @@ +import React, { createContext, useContext, useMemo, useState } from 'react'; + +type DateListContextValue = { + list: Set; + dateRef: () => (ref: HTMLElement | null) => void; +}; + +const DateListContext = createContext(undefined); + +const useDateRef = () => { + const context = useDateListController(); + return useMemo(() => context.dateRef(), [context]); +}; + +const DateListProvider = ({ children }: { children: React.ReactNode }) => { + const [list] = useState>(new Set()); + + const addToList = (value: HTMLElement) => { + list.add(value); + return () => { + list.delete(value); + }; + }; + + const dateRef = () => { + let remove: () => void; + return (ref: HTMLElement | null) => { + if (remove) remove(); + + if (!ref) return; + remove = addToList(ref); + }; + }; + + return {children}; +}; + +const useDateListController = () => { + const context = useContext(DateListContext); + if (!context) { + throw new Error('useDateController must be used within an DateScrollProvider'); + } + return context; +}; + +export { DateListProvider, useDateListController, useDateRef }; From 48d041048c006615b46f9a8485cc40d8fabf5511 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 1 Mar 2024 00:31:32 -0300 Subject: [PATCH 080/207] chore: remove hardcoded position for message date (#31866) --- .../client/views/room/body/RoomBody.tsx | 15 ++-- .../Threads/components/ThreadChat.tsx | 11 ++- .../Threads/components/ThreadMessageList.tsx | 11 +-- .../client/views/room/hooks/useDateScroll.ts | 73 +++++++++++-------- 4 files changed, 65 insertions(+), 45 deletions(-) diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index 83cbf184b1bd..d6fb6e8cbfe1 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -1,6 +1,7 @@ import type { IMessage, IUser } from '@rocket.chat/core-typings'; import { isEditedMessage } from '@rocket.chat/core-typings'; import { Box, Bubble } from '@rocket.chat/fuselage'; +import { useMergedRefs } from '@rocket.chat/fuselage-hooks'; import { usePermission, useRole, @@ -57,8 +58,6 @@ import { useRestoreScrollPosition } from './hooks/useRestoreScrollPosition'; import { useRetentionPolicy } from './hooks/useRetentionPolicy'; import { useUnreadMessages } from './hooks/useUnreadMessages'; -const BUBBLE_OFFSET = 8; - const RoomBody = (): ReactElement => { const formatDate = useFormatDate(); const t = useTranslation(); @@ -70,7 +69,7 @@ const RoomBody = (): ReactElement => { const subscription = useRoomSubscription(); const { list } = useDateListController(); - const { listStyle, bubbleDate, onScroll: handleDateOnScroll, showBubble, style: bubbleDateStyle } = useDateScroll(BUBBLE_OFFSET); + const { callbackRef, listStyle, bubbleDate, showBubble, style: bubbleDateStyle } = useDateScroll(); const [lastMessageDate, setLastMessageDate] = useState(); const [hideLeaderHeader, setHideLeaderHeader] = useState(false); @@ -390,14 +389,12 @@ const RoomBody = (): ReactElement => { wrapper.addEventListener('scroll', updateUnreadCount); wrapper.addEventListener('scroll', handleWrapperScroll); - wrapper.addEventListener('scroll', () => handleDateOnScroll(list)); return () => { wrapper.removeEventListener('scroll', updateUnreadCount); wrapper.removeEventListener('scroll', handleWrapperScroll); - wrapper.removeEventListener('scroll', () => handleDateOnScroll(list)); }; - }, [_isAtBottom, handleDateOnScroll, list, room._id, setUnreadCount]); + }, [_isAtBottom, list, room._id, setUnreadCount]); useEffect(() => { const wrapper = wrapperRef.current; @@ -549,6 +546,8 @@ const RoomBody = (): ReactElement => { const { messageListRef, messageListProps } = useMessageListNavigation(); + const ref = useMergedRefs(callbackRef, wrapperRef); + return ( <> {!isLayoutEmbedded && room.announcement && } @@ -559,7 +558,7 @@ const RoomBody = (): ReactElement => { onClick={hideFlexTab && handleCloseFlexTab} >
          -
          +
          {uploads.map((upload) => ( @@ -623,7 +622,7 @@ const RoomBody = (): ReactElement => { .join(' ')} > - +
            { - + diff --git a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx index 249a71ec4f54..77c2854cd7ce 100644 --- a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx +++ b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx @@ -17,14 +17,11 @@ import { useDateScroll } from '../../../hooks/useDateScroll'; import { useFirstUnreadMessageId } from '../../../hooks/useFirstUnreadMessageId'; import { useMessageListNavigation } from '../../../hooks/useMessageListNavigation'; import { useScrollMessageList } from '../../../hooks/useScrollMessageList'; -import { useDateListController } from '../../../providers/DateListProvider'; import { useLegacyThreadMessageJump } from '../hooks/useLegacyThreadMessageJump'; import { useLegacyThreadMessageListScrolling } from '../hooks/useLegacyThreadMessageListScrolling'; import { useLegacyThreadMessages } from '../hooks/useLegacyThreadMessages'; import { ThreadMessageItem } from './ThreadMessageItem'; -const BUBBLE_OFFSET = 64; - const isMessageSequential = (current: IMessage, previous: IMessage | undefined, groupingRange: number): boolean => { if (!previous) { return false; @@ -54,8 +51,7 @@ type ThreadMessageListProps = { const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElement => { const formatDate = useFormatDate(); - const { list } = useDateListController(); - const { listStyle, bubbleDate, onScroll: handleDateOnScroll, showBubble, style: bubbleDateStyle } = useDateScroll(BUBBLE_OFFSET); + const { callbackRef, listStyle, bubbleDate, showBubble, style: bubbleDateStyle } = useDateScroll(); const { messages, loading } = useLegacyThreadMessages(mainMessage._id); const { @@ -74,6 +70,8 @@ const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElemen const { messageListRef, messageListProps } = useMessageListNavigation(); const listRef = useMergedRefs(listScrollRef, listJumpRef, messageListRef); + const scrollRef = useMergedRefs(callbackRef, listWrapperScrollRef); + return (
            {bubbleDate && ( @@ -84,10 +82,9 @@ const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElemen )} { handleScroll(args); - handleDateOnScroll(list); }} style={{ scrollBehavior: 'smooth', overflowX: 'hidden' }} > diff --git a/apps/meteor/client/views/room/hooks/useDateScroll.ts b/apps/meteor/client/views/room/hooks/useDateScroll.ts index 62e69f10b6b0..c10aa3bf9640 100644 --- a/apps/meteor/client/views/room/hooks/useDateScroll.ts +++ b/apps/meteor/client/views/room/hooks/useDateScroll.ts @@ -3,34 +3,43 @@ import { useSafely } from '@rocket.chat/fuselage-hooks'; import { useCallback, useState } from 'react'; import { withThrottling } from '../../../../lib/utils/highOrderFunctions'; +import { useDateListController } from '../providers/DateListProvider'; type useDateScrollReturn = { bubbleDate: string | undefined; - onScroll: (list: Set) => void; - style: ReturnType; + callbackRef: (node: HTMLElement | null) => void; + style?: ReturnType; showBubble: boolean; listStyle?: ReturnType; }; -export const useDateScroll = (offset = 0): useDateScrollReturn => { +export const useDateScroll = (margin = 8): useDateScrollReturn => { const [bubbleDate, setBubbleDate] = useSafely( useState<{ date: string; show: boolean; + style?: ReturnType; }>({ date: '', show: false, + style: undefined, }), ); - const onScroll = useCallback( - withThrottling({ wait: 30 })( - (() => { + const { list } = useDateListController(); + + const callbackRef = useCallback( + (node: HTMLElement | null) => { + if (!node) { + return; + } + const onScroll = (() => { let timeout: ReturnType; - return (elements: Set) => { + return (elements: Set, offset: number) => { clearTimeout(timeout); - const bubbleOffset = offset + 56; + const bubbleOffset = offset; + // Gets the first non visible message date and sets the bubble date to it const [date, message] = [...elements].reduce((ret, message) => { // Sanitize elements @@ -49,16 +58,30 @@ export const useDateScroll = (offset = 0): useDateScrollReturn => { }, [] as [string, HTMLElement] | []); // We always keep the previous date if we don't have a new one, so when the bubble disappears it doesn't flicker + setBubbleDate(() => ({ date: '', ...(date && { date }), show: Boolean(date), + style: css` + position: absolute; + top: ${margin}px; + left: 50%; + translate: -50%; + z-index: 1; + + opacity: 0; + transition: opacity 0.6s; + + &.bubble-visible { + opacity: 1; + } + `, })); if (message) { const { top } = message.getBoundingClientRect(); - - if (top - offset > 0) { + if (top < offset && top > 0) { return; } } @@ -72,34 +95,26 @@ export const useDateScroll = (offset = 0): useDateScrollReturn => { 1000, ); }; - })(), - ), - [offset], + })(); + const fn = withThrottling({ wait: 30 })(() => { + const offset = node.getBoundingClientRect().top; + onScroll(list, offset); + }); + + node.addEventListener('scroll', fn, { passive: true }); + }, + [list, margin, setBubbleDate], ); - const dateBubbleStyle = css` - position: absolute; - top: ${offset}px; - left: 50%; - translate: -50%; - z-index: 1; - - opacity: 0; - transition: opacity 0.6s; - - &.bubble-visible { - opacity: 1; - } - `; - const listStyle = bubbleDate.show && bubbleDate.date ? css` + position: relative; & [data-time='${bubbleDate.date.replaceAll(/[-T:.]/g, '').substring(0, 8)}'] { opacity: 0; } ` : undefined; - return { listStyle, bubbleDate: bubbleDate.date, onScroll, style: dateBubbleStyle, showBubble: Boolean(bubbleDate.show) }; + return { callbackRef, listStyle, bubbleDate: bubbleDate.date, style: bubbleDate.style, showBubble: Boolean(bubbleDate.show) }; }; From 000f601e473e861a0e2c77c75812d0e8a32fa6ae Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 1 Mar 2024 18:15:51 -0300 Subject: [PATCH 081/207] fix: message parser emphasis black hole (#31868) Co-authored-by: Guilherme Gazzo <5263975+ggazzo@users.noreply.github.com> --- packages/message-parser/src/grammar.pegjs | 4 +-- packages/message-parser/src/utils.ts | 27 ++++++++++--------- .../message-parser/tests/emphasis.test.ts | 16 +++++++++++ packages/message-parser/tsconfig.json | 1 + 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/packages/message-parser/src/grammar.pegjs b/packages/message-parser/src/grammar.pegjs index 3445f8e18231..138ff482d208 100644 --- a/packages/message-parser/src/grammar.pegjs +++ b/packages/message-parser/src/grammar.pegjs @@ -359,10 +359,10 @@ EmphasisForReferences = BoldForReferences / ItalicForReferences / StrikethroughF Italic = value:$([a-zA-Z0-9]+ [\x5F] [\x5F]?) { return plain(value); } / [\x5F] [\x5F] i:ItalicContentItems [\x5F] [\x5F] t:$[a-zA-Z0-9]+ { - return reducePlainTexts([plain('__'), ...i, plain('__'), plain(t)])[0]; + return reducePlainTexts([plain('__'), ...i, plain('__'), plain(t)]); } / [\x5F] i:ItalicContentItems [\x5F] t:$[a-zA-Z]+ { - return reducePlainTexts([plain('_'), ...i, plain('_'), plain(t)])[0]; + return reducePlainTexts([plain('_'), ...i, plain('_'), plain(t)]); } / [\x5F] [\x5F] @ItalicContent [\x5F] [\x5F] / [\x5F] @ItalicContent [\x5F] diff --git a/packages/message-parser/src/utils.ts b/packages/message-parser/src/utils.ts index 5902d8e33e7d..44527529b4a6 100644 --- a/packages/message-parser/src/utils.ts +++ b/packages/message-parser/src/utils.ts @@ -197,20 +197,21 @@ const joinEmoji = ( export const reducePlainTexts = ( values: Paragraph['value'] ): Paragraph['value'] => - values.reduce((result, item, index) => { - const next = values[index + 1]; - const current = joinEmoji(item, values[index - 1], next); - const previous: Inlines = result[result.length - 1]; - - if (previous) { - if (current.type === 'PLAIN_TEXT' && current.type === previous.type) { - previous.value += current.value; - return result; + values + .flatMap((item) => item) + .reduce((result, item, index, values) => { + const next = values[index + 1]; + const current = joinEmoji(item, values[index - 1], next); + const previous: Inlines = result[result.length - 1]; + + if (previous) { + if (current.type === 'PLAIN_TEXT' && current.type === previous.type) { + previous.value += current.value; + return result; + } } - } - - return [...result, current]; - }, [] as Paragraph['value']); + return [...result, current]; + }, [] as Paragraph['value']); export const lineBreak = (): LineBreak => ({ type: 'LINE_BREAK', value: undefined, diff --git a/packages/message-parser/tests/emphasis.test.ts b/packages/message-parser/tests/emphasis.test.ts index bae05be7d158..e8e72a5882f1 100644 --- a/packages/message-parser/tests/emphasis.test.ts +++ b/packages/message-parser/tests/emphasis.test.ts @@ -11,6 +11,7 @@ import { emojiUnicode, mentionChannel, mentionUser, + inlineCode, } from '../src/utils'; test.each([ @@ -169,6 +170,21 @@ test.each([ ]), ], ], + ['_ouch_ouch', [paragraph([plain('_ouch_ouch')])]], + [ + `_@mention _gone`, + [paragraph([plain('_'), mentionUser('mention'), plain(' _gone')])], + ], + [ + '_nothing `should` be _gone', + [ + paragraph([ + plain('_nothing '), + inlineCode(plain('should')), + plain(' be _gone'), + ]), + ], + ], ])('parses %p', (input, output) => { expect(parse(input)).toMatchObject(output); }); diff --git a/packages/message-parser/tsconfig.json b/packages/message-parser/tsconfig.json index 3e59aeae898f..619475cd7914 100644 --- a/packages/message-parser/tsconfig.json +++ b/packages/message-parser/tsconfig.json @@ -6,6 +6,7 @@ "declaration": true, "declarationMap": true, "sourceMap": true, + "lib": ["es2019"], "outDir": "./dist", "strict": true, "esModuleInterop": true, From 09ee4fd2d9c672e80550c7bb67af7fe820fcf1e2 Mon Sep 17 00:00:00 2001 From: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Date: Mon, 4 Mar 2024 14:07:46 -0300 Subject: [PATCH 082/207] fix: Room import doesn't honor the specified owner (#31803) --- .changeset/funny-cooks-sneeze.md | 5 + .../server/classes/ImportDataConverter.ts | 6 +- .../client/views/admin/rooms/EditRoom.tsx | 2 +- .../tests/e2e/fixtures/files/csv_import.zip | Bin 0 -> 991 bytes .../e2e/fixtures/files/csv_import_rooms.csv | 4 + .../e2e/fixtures/files/csv_import_users.csv | 2 + .../tests/e2e/fixtures/files/dm_messages.csv | 2 + apps/meteor/tests/e2e/imports.spec.ts | 136 ++++++++++++++++-- apps/meteor/tests/e2e/page-objects/admin.ts | 8 ++ 9 files changed, 152 insertions(+), 13 deletions(-) create mode 100644 .changeset/funny-cooks-sneeze.md create mode 100644 apps/meteor/tests/e2e/fixtures/files/csv_import.zip create mode 100644 apps/meteor/tests/e2e/fixtures/files/csv_import_rooms.csv create mode 100644 apps/meteor/tests/e2e/fixtures/files/csv_import_users.csv create mode 100644 apps/meteor/tests/e2e/fixtures/files/dm_messages.csv diff --git a/.changeset/funny-cooks-sneeze.md b/.changeset/funny-cooks-sneeze.md new file mode 100644 index 000000000000..c7b019a3d92b --- /dev/null +++ b/.changeset/funny-cooks-sneeze.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed room owner specified on room import not being inserted as a room member or owner. diff --git a/apps/meteor/app/importer/server/classes/ImportDataConverter.ts b/apps/meteor/app/importer/server/classes/ImportDataConverter.ts index 1ce40b9415e5..f5315b4f1e6c 100644 --- a/apps/meteor/app/importer/server/classes/ImportDataConverter.ts +++ b/apps/meteor/app/importer/server/classes/ImportDataConverter.ts @@ -1020,13 +1020,13 @@ export class ImportDataConverter { return; } if (roomData.t === 'p') { - const user = await Users.findOneById(startedByUserId); + const user = await Users.findOneById(creatorId); if (!user) { throw new Error('importer-channel-invalid-creator'); } - roomInfo = await createPrivateGroupMethod(user, roomData.name, members, false, {}, {}, true); + roomInfo = await createPrivateGroupMethod(user, roomData.name, members, false, {}, {}); } else { - roomInfo = await createChannelMethod(startedByUserId, roomData.name, members, false, {}, {}, true); + roomInfo = await createChannelMethod(creatorId, roomData.name, members, false, {}, {}); } } diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx index 18edd8cc815a..1522f4694160 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -182,7 +182,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps) => { {t('Owner')} - + )} diff --git a/apps/meteor/tests/e2e/fixtures/files/csv_import.zip b/apps/meteor/tests/e2e/fixtures/files/csv_import.zip new file mode 100644 index 0000000000000000000000000000000000000000..19415a7cd142c71c4b8dcbda71b72d8c3ffd7ecd GIT binary patch literal 991 zcmWIWW@Zs#-~hrYRiP0KQ1A;#^DrndBxfY%<)!8n>m?VLg@*7lu>Y{TmI}gWIvCoi1!J?rbGtEHA89CJnL(5X}YN3*3E zrze>5F$8$Cb66MJorwdw4CJ~1u)9Idl_(2?yBoyk2f8~YvnVyWBsaCVI59o7SRd?k z#?I@htUwH+(VPx33={~+hD`<W%;tMd>z!181(V+-%tM zXHw4L|_6?h7;)a(&E%2MC?`YT}uUFEV1Ve zi>FJ09!-X_LE1va+(kiOCw(q16`A93h|BM%Cs&+nRg8dZFH&B!z2OcQOU|>t*L7+`UM__ { - fs.createReadStream(slackCsvDir) - .pipe(parse({ delimiter: ',', from_line: 2 })) - .on('data', (rows) => { - rowUserName.push(rows[0]); - }); +// These files have the same content from users.csv, channels.csv and messages1.csv from the zip file +// They have been extracted just so that we don't need to do that on the fly +const usersCsvDir = path.resolve(__dirname, 'fixtures', 'files', 'csv_import_users.csv'); +const roomsCsvDir = path.resolve(__dirname, 'fixtures', 'files', 'csv_import_rooms.csv'); +const dmMessagesCsvDir = path.resolve(__dirname, 'fixtures', 'files', 'dm_messages.csv'); + +const usersCsvsToJson = async (): Promise => { + await new Promise((resolve) => + fs.createReadStream(slackCsvDir) + .pipe(parse({ delimiter: ',', from_line: 2 })) + .on('data', (rows) => { + rowUserName.push(rows[0]); + }) + .on('end', resolve) + ); + + await new Promise((resolve) => + fs.createReadStream(usersCsvDir) + .pipe(parse({ delimiter: ',' })) + .on('data', (rows) => { + rowUserName.push(rows[0]); + csvImportedUsernames.push(rows[0]); + }) + .on('end', resolve) + ); }; +const countDmMessages = (): Promise => ( + new Promise((resolve) => + fs.createReadStream(dmMessagesCsvDir) + .pipe(parse({ delimiter: ',' })) + .on('data', (rows) => { + dmMessages.push(rows[3]); + }) + .on('end', resolve) + ) +); + +const roomsCsvToJson = (): Promise => ( + new Promise((resolve) => + fs.createReadStream(roomsCsvDir) + .pipe(parse({ delimiter: ',' })) + .on('data', (rows) => { + importedRooms.push({ + name: rows[0], + ownerUsername: rows[1], + visibility: rows[2], + members: rows[3], + }); + }) + .on('end', resolve) + ) +); + test.describe.serial('imports', () => { - test.beforeAll(() => { - csvToJson(); + test.beforeAll(async () => { + await usersCsvsToJson(); + await roomsCsvToJson(); + await countDmMessages(); }); test('expect import users data from slack', async ({ page }) => { @@ -43,11 +102,70 @@ test.describe.serial('imports', () => { }); }); - test('expect to all users imported are actually listed as users', async ({ page }) => { + test('expect import users data from zipped CSV files', async ({ page }) => { + const poAdmin: Admin = new Admin(page); + await page.goto('/admin/import'); + + await poAdmin.btnImportNewFile.click(); + + await (await poAdmin.getOptionFileType('CSV')).click(); + + await poAdmin.inputFile.setInputFiles(zipCsvImportDir); + await poAdmin.btnImport.click(); + + await poAdmin.btnStartImport.click(); + + await expect(poAdmin.importStatusTableFirstRowCell).toBeVisible({ + timeout: 30_000, + }); + }); + + test('expect all imported users to be actually listed as users', async ({ page }) => { await page.goto('/admin/users'); for (const user of rowUserName) { expect(page.locator(`tbody tr td:first-child >> text="${user}"`)); } }); + + test('expect all imported rooms to be actually listed as rooms with correct members count', async ({ page }) => { + const poAdmin: Admin = new Admin(page); + await page.goto('/admin/rooms'); + + for await (const room of importedRooms) { + await poAdmin.inputSearchRooms.fill(room.name); + + const expectedMembersCount = room.members.split(';').filter((username) => username !== room.ownerUsername).length + 1; + expect(page.locator(`tbody tr td:nth-child(2) >> text="${ expectedMembersCount }"`)); + } + }); + + test('expect all imported rooms to have correct room type and owner', async ({ page }) => { + const poAdmin: Admin = new Admin(page); + await page.goto('/admin/rooms'); + + for await (const room of importedRooms) { + await poAdmin.inputSearchRooms.fill(room.name); + await poAdmin.getRoomRow(room.name).click(); + + room.visibility === 'private' ? await expect(poAdmin.privateInput).toBeChecked() : await expect(poAdmin.privateInput).not.toBeChecked(); + await expect(poAdmin.roomOwnerInput).toHaveValue(room.ownerUsername); + } + }); + + test('expect imported DM to be actually listed as a room with correct members and messages count', async ({ page }) => { + const poAdmin: Admin = new Admin(page); + await page.goto('/admin/rooms'); + + for await (const user of csvImportedUsernames) { + await poAdmin.inputSearchRooms.fill(user); + expect(page.locator(`tbody tr td:first-child >> text="${user}"`)); + + const expectedMembersCount = 2; + expect(page.locator(`tbody tr td:nth-child(2) >> text="${ expectedMembersCount }"`)); + + const expectedMessagesCount = dmMessages.length; + expect(page.locator(`tbody tr td:nth-child(3) >> text="${ expectedMessagesCount }"`)); + } + }); }); diff --git a/apps/meteor/tests/e2e/page-objects/admin.ts b/apps/meteor/tests/e2e/page-objects/admin.ts index 30b58cd29bec..519b15f0cc0c 100644 --- a/apps/meteor/tests/e2e/page-objects/admin.ts +++ b/apps/meteor/tests/e2e/page-objects/admin.ts @@ -28,10 +28,18 @@ export class Admin { return this.page.locator(`label >> text=Private`); } + get privateInput(): Locator { + return this.page.locator('input[name="roomType"]'); + } + get roomNameInput(): Locator { return this.page.locator('input[name="roomName"]'); } + get roomOwnerInput(): Locator { + return this.page.locator('input[name="roomOwner"]'); + } + get archivedLabel(): Locator { return this.page.locator('label >> text=Archived'); } From f09953b196d4bb1699598a72e7dfa1fd6aa5b585 Mon Sep 17 00:00:00 2001 From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Mon, 4 Mar 2024 14:30:55 -0300 Subject: [PATCH 083/207] chore: Remove references to EE code from the app bridges (#31757) --- .../app/apps/server/bridges/activation.ts | 6 +-- apps/meteor/app/apps/server/bridges/api.ts | 5 +- apps/meteor/app/apps/server/bridges/cloud.ts | 4 +- .../app/apps/server/bridges/commands.ts | 46 +++++++++++------- .../meteor/app/apps/server/bridges/details.ts | 11 +++-- .../app/apps/server/bridges/environmental.ts | 5 +- apps/meteor/app/apps/server/bridges/http.ts | 5 +- .../app/apps/server/bridges/internal.ts | 17 ++++--- .../app/apps/server/bridges/livechat.ts | 48 +++++++++++-------- .../app/apps/server/bridges/messages.ts | 43 +++++++++-------- .../app/apps/server/bridges/moderation.ts | 4 +- .../app/apps/server/bridges/oauthApps.ts | 5 +- .../app/apps/server/bridges/persistence.ts | 13 +++-- apps/meteor/app/apps/server/bridges/roles.ts | 21 ++++---- apps/meteor/app/apps/server/bridges/rooms.ts | 30 +++++++----- .../app/apps/server/bridges/scheduler.ts | 5 +- .../app/apps/server/bridges/settings.ts | 5 +- apps/meteor/app/apps/server/bridges/thread.ts | 5 +- .../app/apps/server/bridges/uiInteraction.ts | 5 +- .../meteor/app/apps/server/bridges/uploads.ts | 8 ++-- apps/meteor/app/apps/server/bridges/users.ts | 28 +++++++---- .../apps/server/bridges/videoConferences.ts | 8 ++-- .../app/apps/server/converters/departments.js | 2 +- .../app/apps/server/converters/messages.js | 2 +- .../app/apps/server/converters/roles.ts | 9 ++-- .../app/apps/server/converters/rooms.js | 2 +- .../app/apps/server/converters/threads.ts | 43 ++++++++++------- .../server/converters/transformMappedData.ts} | 35 ++++++++++---- .../app/apps/server/converters/uploads.js | 2 +- .../server/converters/videoConferences.ts | 22 +++++---- .../app/apps/server/converters/visitors.js | 2 +- apps/meteor/package.json | 2 + packages/apps/.eslintrc.json | 4 ++ packages/apps/package.json | 29 +++++++++++ packages/apps/src/AppsEngine.ts | 20 ++++++++ packages/apps/src/IAppServerNotifier.ts | 14 ++++++ packages/apps/src/IAppServerOrchestrator.ts | 17 +++++++ .../apps/src/converters/IAppConvertersMap.ts | 27 +++++++++++ .../converters/IAppDepartmentsConverter.ts | 13 +++++ .../src/converters/IAppMessagesConverter.ts | 13 +++++ .../apps/src/converters/IAppRolesConverter.ts | 8 ++++ .../apps/src/converters/IAppRoomsConverter.ts | 14 ++++++ .../src/converters/IAppSettingsConverter.ts | 8 ++++ .../src/converters/IAppThreadsConverter.ts | 14 ++++++ .../src/converters/IAppUploadsConverter.ts | 13 +++++ .../apps/src/converters/IAppUsersConverter.ts | 14 ++++++ .../IAppVideoConferencesConverter.ts | 11 +++++ .../src/converters/IAppVisitorsConverter.ts | 14 ++++++ packages/apps/src/converters/index.ts | 11 +++++ packages/apps/src/index.ts | 4 ++ packages/apps/tsconfig.json | 9 ++++ yarn.lock | 28 ++++++++++- 52 files changed, 545 insertions(+), 188 deletions(-) rename apps/meteor/{ee/lib/misc/transformMappedData.js => app/apps/server/converters/transformMappedData.ts} (73%) create mode 100644 packages/apps/.eslintrc.json create mode 100644 packages/apps/package.json create mode 100644 packages/apps/src/AppsEngine.ts create mode 100644 packages/apps/src/IAppServerNotifier.ts create mode 100644 packages/apps/src/IAppServerOrchestrator.ts create mode 100644 packages/apps/src/converters/IAppConvertersMap.ts create mode 100644 packages/apps/src/converters/IAppDepartmentsConverter.ts create mode 100644 packages/apps/src/converters/IAppMessagesConverter.ts create mode 100644 packages/apps/src/converters/IAppRolesConverter.ts create mode 100644 packages/apps/src/converters/IAppRoomsConverter.ts create mode 100644 packages/apps/src/converters/IAppSettingsConverter.ts create mode 100644 packages/apps/src/converters/IAppThreadsConverter.ts create mode 100644 packages/apps/src/converters/IAppUploadsConverter.ts create mode 100644 packages/apps/src/converters/IAppUsersConverter.ts create mode 100644 packages/apps/src/converters/IAppVideoConferencesConverter.ts create mode 100644 packages/apps/src/converters/IAppVisitorsConverter.ts create mode 100644 packages/apps/src/converters/index.ts create mode 100644 packages/apps/src/index.ts create mode 100644 packages/apps/tsconfig.json diff --git a/apps/meteor/app/apps/server/bridges/activation.ts b/apps/meteor/app/apps/server/bridges/activation.ts index 72a5b882b18a..dc5a0f57e003 100644 --- a/apps/meteor/app/apps/server/bridges/activation.ts +++ b/apps/meteor/app/apps/server/bridges/activation.ts @@ -1,13 +1,11 @@ -import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; +import type { IAppServerOrchestrator, AppStatus } from '@rocket.chat/apps'; import type { ProxiedApp } from '@rocket.chat/apps-engine/server/ProxiedApp'; import { AppActivationBridge as ActivationBridge } from '@rocket.chat/apps-engine/server/bridges/AppActivationBridge'; import { Users } from '@rocket.chat/models'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; - export class AppActivationBridge extends ActivationBridge { // eslint-disable-next-line no-empty-function - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/api.ts b/apps/meteor/app/apps/server/bridges/api.ts index a5767f11b977..46bb70e3339a 100644 --- a/apps/meteor/app/apps/server/bridges/api.ts +++ b/apps/meteor/app/apps/server/bridges/api.ts @@ -1,3 +1,4 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { RequestMethod } from '@rocket.chat/apps-engine/definition/accessors'; import type { IApiRequest, IApiEndpoint, IApi } from '@rocket.chat/apps-engine/definition/api'; import { ApiBridge } from '@rocket.chat/apps-engine/server/bridges/ApiBridge'; @@ -7,7 +8,6 @@ import express from 'express'; import { Meteor } from 'meteor/meteor'; import { WebApp } from 'meteor/webapp'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { authenticationMiddleware } from '../../../api/server/middlewares/authentication'; const apiServer = express(); @@ -24,8 +24,7 @@ interface IRequestWithPrivateHash extends Request { export class AppApisBridge extends ApiBridge { appRouters: Map; - // eslint-disable-next-line no-empty-function - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); this.appRouters = new Map(); diff --git a/apps/meteor/app/apps/server/bridges/cloud.ts b/apps/meteor/app/apps/server/bridges/cloud.ts index a0675c115f01..30ca897240f8 100644 --- a/apps/meteor/app/apps/server/bridges/cloud.ts +++ b/apps/meteor/app/apps/server/bridges/cloud.ts @@ -1,11 +1,11 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { IWorkspaceToken } from '@rocket.chat/apps-engine/definition/cloud/IWorkspaceToken'; import { CloudWorkspaceBridge } from '@rocket.chat/apps-engine/server/bridges/CloudWorkspaceBridge'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { getWorkspaceAccessTokenWithScope } from '../../../cloud/server'; export class AppCloudBridge extends CloudWorkspaceBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/commands.ts b/apps/meteor/app/apps/server/bridges/commands.ts index ee7e73e16eb5..5e018c51de89 100644 --- a/apps/meteor/app/apps/server/bridges/commands.ts +++ b/apps/meteor/app/apps/server/bridges/commands.ts @@ -1,3 +1,4 @@ +import type { IAppServerOrchestrator, IAppsRoom, IAppsUser } from '@rocket.chat/apps'; import type { ISlashCommand, ISlashCommandPreview, ISlashCommandPreviewItem } from '@rocket.chat/apps-engine/definition/slashcommands'; import { SlashCommandContext } from '@rocket.chat/apps-engine/definition/slashcommands'; import { CommandBridge } from '@rocket.chat/apps-engine/server/bridges/CommandBridge'; @@ -5,14 +6,13 @@ import type { IMessage, RequiredField, SlashCommand, SlashCommandCallbackParams import { Meteor } from 'meteor/meteor'; import { Utilities } from '../../../../ee/lib/misc/Utilities'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { parseParameters } from '../../../../lib/utils/parseParameters'; import { slashCommands } from '../../../utils/server/slashCommand'; export class AppCommandsBridge extends CommandBridge { disabledCommands: Map; - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); this.disabledCommands = new Map(); } @@ -44,7 +44,7 @@ export class AppCommandsBridge extends CommandBridge { slashCommands.commands[cmd] = this.disabledCommands.get(cmd) as (typeof slashCommands.commands)[string]; this.disabledCommands.delete(cmd); - this.orch.getNotifier().commandUpdated(cmd); + void this.orch.getNotifier().commandUpdated(cmd); } protected async disableCommand(command: string, appId: string): Promise { @@ -69,7 +69,7 @@ export class AppCommandsBridge extends CommandBridge { this.disabledCommands.set(cmd, commandObj); delete slashCommands.commands[cmd]; - this.orch.getNotifier().commandDisabled(cmd); + void this.orch.getNotifier().commandDisabled(cmd); } // command: { command, paramsExample, i18nDescription, executor: function } @@ -95,7 +95,7 @@ export class AppCommandsBridge extends CommandBridge { ) as (typeof slashCommands.commands)[string]['previewCallback']; slashCommands.commands[cmd] = item; - this.orch.getNotifier().commandUpdated(cmd); + void this.orch.getNotifier().commandUpdated(cmd); } protected async registerCommand(command: ISlashCommand, appId: string): Promise { @@ -118,7 +118,7 @@ export class AppCommandsBridge extends CommandBridge { } as SlashCommand; slashCommands.commands[command.command.toLowerCase()] = item; - this.orch.getNotifier().commandAdded(command.command.toLowerCase()); + void this.orch.getNotifier().commandAdded(command.command.toLowerCase()); } protected async unregisterCommand(command: string, appId: string): Promise { @@ -132,7 +132,7 @@ export class AppCommandsBridge extends CommandBridge { this.disabledCommands.delete(cmd); delete slashCommands.commands[cmd]; - this.orch.getNotifier().commandRemoved(cmd); + void this.orch.getNotifier().commandRemoved(cmd); } private _verifyCommand(command: ISlashCommand): void { @@ -162,14 +162,15 @@ export class AppCommandsBridge extends CommandBridge { } private async _appCommandExecutor({ command, message, params, triggerId, userId }: SlashCommandCallbackParams): Promise { - const user = await this.orch.getConverters()?.get('users').convertById(userId); - const room = await this.orch.getConverters()?.get('rooms').convertById(message.rid); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const user: IAppsUser | undefined = await this.orch.getConverters()?.get('users').convertById(userId); + const room: IAppsRoom | undefined = await this.orch.getConverters()?.get('rooms').convertById(message.rid); const threadId = message.tmid; const parameters = parseParameters(params); const context = new SlashCommandContext( - Object.freeze(user), - Object.freeze(room), + Object.freeze(user as IAppsUser), + Object.freeze(room as IAppsRoom), Object.freeze(parameters) as string[], threadId, triggerId, @@ -183,12 +184,19 @@ export class AppCommandsBridge extends CommandBridge { parameters: any, message: RequiredField, 'rid'>, ): Promise { - const user = await this.orch.getConverters()?.get('users').convertById(Meteor.userId()); - const room = await this.orch.getConverters()?.get('rooms').convertById(message.rid); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const uid = Meteor.userId() as string; + const user: IAppsUser | undefined = await this.orch.getConverters()?.get('users').convertById(uid); + const room: IAppsRoom | undefined = await this.orch.getConverters()?.get('rooms').convertById(message.rid); const threadId = message.tmid; const params = parseParameters(parameters); - const context = new SlashCommandContext(Object.freeze(user), Object.freeze(room), Object.freeze(params) as string[], threadId); + const context = new SlashCommandContext( + Object.freeze(user as IAppsUser), + Object.freeze(room as IAppsRoom), + Object.freeze(params) as string[], + threadId, + ); return this.orch.getManager()?.getCommandManager().getPreviews(command, context); } @@ -199,14 +207,16 @@ export class AppCommandsBridge extends CommandBridge { preview: ISlashCommandPreviewItem, triggerId: string, ): Promise { - const user = await this.orch.getConverters()?.get('users').convertById(Meteor.userId()); - const room = await this.orch.getConverters()?.get('rooms').convertById(message.rid); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const uid = Meteor.userId() as string; + const user: IAppsUser | undefined = await this.orch.getConverters()?.get('users').convertById(uid); + const room: IAppsRoom | undefined = await this.orch.getConverters()?.get('rooms').convertById(message.rid); const threadId = message.tmid; const params = parseParameters(parameters); const context = new SlashCommandContext( - Object.freeze(user), - Object.freeze(room), + Object.freeze(user as IAppsUser), + Object.freeze(room as IAppsRoom), Object.freeze(params) as string[], threadId, triggerId, diff --git a/apps/meteor/app/apps/server/bridges/details.ts b/apps/meteor/app/apps/server/bridges/details.ts index 50709917183a..3930cdd451cc 100644 --- a/apps/meteor/app/apps/server/bridges/details.ts +++ b/apps/meteor/app/apps/server/bridges/details.ts @@ -1,18 +1,19 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { ISetting } from '@rocket.chat/apps-engine/definition/settings'; import { AppDetailChangesBridge as DetailChangesBridge } from '@rocket.chat/apps-engine/server/bridges/AppDetailChangesBridge'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; - export class AppDetailChangesBridge extends DetailChangesBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } protected onAppSettingsChange(appId: string, setting: ISetting): void { + const logFailure = () => console.warn('failed to notify about the setting change.', appId); + try { - this.orch.getNotifier().appSettingsChange(appId, setting); + this.orch.getNotifier().appSettingsChange(appId, setting).catch(logFailure); } catch (e) { - console.warn('failed to notify about the setting change.', appId); + logFailure(); } } } diff --git a/apps/meteor/app/apps/server/bridges/environmental.ts b/apps/meteor/app/apps/server/bridges/environmental.ts index 43b34674e95f..705a27186dee 100644 --- a/apps/meteor/app/apps/server/bridges/environmental.ts +++ b/apps/meteor/app/apps/server/bridges/environmental.ts @@ -1,11 +1,10 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import { EnvironmentalVariableBridge } from '@rocket.chat/apps-engine/server/bridges/EnvironmentalVariableBridge'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; - export class AppEnvironmentalVariableBridge extends EnvironmentalVariableBridge { allowed: Array; - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); this.allowed = ['NODE_ENV', 'ROOT_URL', 'INSTANCE_IP']; } diff --git a/apps/meteor/app/apps/server/bridges/http.ts b/apps/meteor/app/apps/server/bridges/http.ts index a4b09e848c90..1535a18823c5 100644 --- a/apps/meteor/app/apps/server/bridges/http.ts +++ b/apps/meteor/app/apps/server/bridges/http.ts @@ -1,10 +1,9 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { IHttpResponse } from '@rocket.chat/apps-engine/definition/accessors'; import type { IHttpBridgeRequestInfo } from '@rocket.chat/apps-engine/server/bridges'; import { HttpBridge } from '@rocket.chat/apps-engine/server/bridges/HttpBridge'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; - const isGetOrHead = (method: string): boolean => ['GET', 'HEAD'].includes(method.toUpperCase()); // Previously, there was no timeout for HTTP requests. @@ -13,7 +12,7 @@ const isGetOrHead = (method: string): boolean => ['GET', 'HEAD'].includes(method const DEFAULT_TIMEOUT = 3 * 60 * 1000; export class AppHttpBridge extends HttpBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/internal.ts b/apps/meteor/app/apps/server/bridges/internal.ts index b41ffbb2889e..c5cd9a3f1a60 100644 --- a/apps/meteor/app/apps/server/bridges/internal.ts +++ b/apps/meteor/app/apps/server/bridges/internal.ts @@ -1,14 +1,13 @@ -import type { ISetting } from '@rocket.chat/apps-engine/definition/settings'; +import type { IAppServerOrchestrator, IAppsSetting } from '@rocket.chat/apps'; import { InternalBridge } from '@rocket.chat/apps-engine/server/bridges/InternalBridge'; -import type { ISubscription } from '@rocket.chat/core-typings'; +import type { ISetting, ISubscription } from '@rocket.chat/core-typings'; import { Settings, Subscriptions } from '@rocket.chat/models'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { isTruthy } from '../../../../lib/isTruthy'; import { deasyncPromise } from '../../../../server/deasync/deasync'; export class AppInternalBridge extends InternalBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } @@ -37,9 +36,13 @@ export class AppInternalBridge extends InternalBridge { return records.map((s: ISubscription) => s.u.username).filter(isTruthy); } - protected async getWorkspacePublicKey(): Promise { - const publicKeySetting = await Settings.findOneById('Cloud_Workspace_PublicKey'); + protected async getWorkspacePublicKey(): Promise { + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const publicKeySetting: ISetting | null = await Settings.findOneById('Cloud_Workspace_PublicKey'); - return this.orch.getConverters()?.get('settings').convertToApp(publicKeySetting); + return this.orch + .getConverters() + ?.get('settings') + .convertToApp(publicKeySetting as ISetting); } } diff --git a/apps/meteor/app/apps/server/bridges/livechat.ts b/apps/meteor/app/apps/server/bridges/livechat.ts index 2c5eaa841cdd..bf175d1d1426 100644 --- a/apps/meteor/app/apps/server/bridges/livechat.ts +++ b/apps/meteor/app/apps/server/bridges/livechat.ts @@ -1,28 +1,22 @@ +import type { IAppServerOrchestrator, IAppsLivechatMessage } from '@rocket.chat/apps'; import type { IExtraRoomParams } from '@rocket.chat/apps-engine/definition/accessors/ILivechatCreator'; -import type { - ILivechatMessage, - IVisitor, - ILivechatRoom, - ILivechatTransferData, - IDepartment, -} from '@rocket.chat/apps-engine/definition/livechat'; +import type { IVisitor, ILivechatRoom, ILivechatTransferData, IDepartment } from '@rocket.chat/apps-engine/definition/livechat'; import type { IMessage as IAppsEngineMesage } from '@rocket.chat/apps-engine/definition/messages'; import type { IUser } from '@rocket.chat/apps-engine/definition/users'; import { LivechatBridge } from '@rocket.chat/apps-engine/server/bridges/LivechatBridge'; -import type { SelectedAgent } from '@rocket.chat/core-typings'; +import type { ILivechatDepartment, IOmnichannelRoom, SelectedAgent, IMessage, ILivechatVisitor } from '@rocket.chat/core-typings'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; import { LivechatVisitors, LivechatRooms, LivechatDepartment, Users } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { callbacks } from '../../../../lib/callbacks'; import { deasyncPromise } from '../../../../server/deasync/deasync'; import { getRoom } from '../../../livechat/server/api/lib/livechat'; -import { Livechat as LivechatTyped } from '../../../livechat/server/lib/LivechatTyped'; +import { type ILivechatMessage, Livechat as LivechatTyped } from '../../../livechat/server/lib/LivechatTyped'; import { settings } from '../../../settings/server'; export class AppLivechatBridge extends LivechatBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } @@ -36,16 +30,21 @@ export class AppLivechatBridge extends LivechatBridge { return LivechatTyped.online(departmentId); } - protected async createMessage(message: ILivechatMessage, appId: string): Promise { + protected async createMessage(message: IAppsLivechatMessage, appId: string): Promise { this.orch.debugLog(`The App ${appId} is creating a new message.`); if (!message.token) { throw new Error('Invalid token for livechat message'); } + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const guest = this.orch.getConverters().get('visitors').convertAppVisitor(message.visitor); + const appMessage = (await this.orch.getConverters().get('messages').convertAppMessage(message)) as IMessage | undefined; + const livechatMessage = appMessage as ILivechatMessage | undefined; + const msg = await LivechatTyped.sendMessage({ - guest: this.orch.getConverters()?.get('visitors').convertAppVisitor(message.visitor), - message: await this.orch.getConverters()?.get('messages').convertAppMessage(message), + guest: guest as ILivechatVisitor, + message: livechatMessage as ILivechatMessage, agent: undefined, roomInfo: { source: { @@ -59,13 +58,16 @@ export class AppLivechatBridge extends LivechatBridge { return msg._id; } - protected async getMessageById(messageId: string, appId: string): Promise { + protected async getMessageById(messageId: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is getting the message: "${messageId}"`); - return this.orch.getConverters()?.get('messages').convertById(messageId); + const message = await this.orch.getConverters().get('messages').convertById(messageId); + + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + return message as IAppsLivechatMessage; } - protected async updateMessage(message: ILivechatMessage, appId: string): Promise { + protected async updateMessage(message: IAppsLivechatMessage, appId: string): Promise { this.orch.debugLog(`The App ${appId} is updating a message.`); const data = { @@ -114,7 +116,8 @@ export class AppLivechatBridge extends LivechatBridge { extraParams: undefined, }); - return this.orch.getConverters()?.get('rooms').convertRoom(result.room); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + return this.orch.getConverters()?.get('rooms').convertRoom(result.room) as Promise; } protected async closeRoom(room: ILivechatRoom, comment: string, closer: IUser | undefined, appId: string): Promise { @@ -152,7 +155,8 @@ export class AppLivechatBridge extends LivechatBridge { result = await LivechatRooms.findOpenByVisitorToken(visitor.token, {}, extraQuery).toArray(); } - return Promise.all((result as unknown as ILivechatRoom[]).map((room) => this.orch.getConverters()?.get('rooms').convertRoom(room))); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + return Promise.all(result.map((room) => this.orch.getConverters()?.get('rooms').convertRoom(room) as Promise)); } protected async createVisitor(visitor: IVisitor, appId: string): Promise { @@ -208,8 +212,9 @@ export class AppLivechatBridge extends LivechatBridge { userId = transferredTo._id; } + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. return LivechatTyped.transfer( - await this.orch.getConverters()?.get('rooms').convertAppRoom(currentRoom), + (await this.orch.getConverters()?.get('rooms').convertAppRoom(currentRoom)) as IOmnichannelRoom, this.orch.getConverters()?.get('visitors').convertAppVisitor(visitor), { userId, departmentId, transferredBy, transferredTo }, ); @@ -275,7 +280,8 @@ export class AppLivechatBridge extends LivechatBridge { this.orch.debugLog(`The App ${appId} is looking for livechat departments.`); const converter = this.orch.getConverters()?.get('departments'); - const boundConverter = converter.convertDepartment.bind(converter); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const boundConverter = converter.convertDepartment.bind(converter) as (_: ILivechatDepartment) => Promise; return Promise.all((await LivechatDepartment.findEnabledWithAgents().toArray()).map(boundConverter)); } diff --git a/apps/meteor/app/apps/server/bridges/messages.ts b/apps/meteor/app/apps/server/bridges/messages.ts index 311e3aaca1e1..18a68220998f 100644 --- a/apps/meteor/app/apps/server/bridges/messages.ts +++ b/apps/meteor/app/apps/server/bridges/messages.ts @@ -1,39 +1,41 @@ -import type { IMessage } from '@rocket.chat/apps-engine/definition/messages'; +import type { IAppServerOrchestrator, IAppsMessage, IAppsUser } from '@rocket.chat/apps'; import type { IRoom } from '@rocket.chat/apps-engine/definition/rooms'; -import type { IUser } from '@rocket.chat/apps-engine/definition/users'; import type { ITypingDescriptor } from '@rocket.chat/apps-engine/server/bridges/MessageBridge'; import { MessageBridge } from '@rocket.chat/apps-engine/server/bridges/MessageBridge'; import { api } from '@rocket.chat/core-services'; +import type { IMessage } from '@rocket.chat/core-typings'; import { Users, Subscriptions, Messages } from '@rocket.chat/models'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { deleteMessage } from '../../../lib/server/functions/deleteMessage'; import { updateMessage } from '../../../lib/server/functions/updateMessage'; import { executeSendMessage } from '../../../lib/server/methods/sendMessage'; import notifications from '../../../notifications/server/lib/Notifications'; export class AppMessageBridge extends MessageBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } - protected async create(message: IMessage, appId: string): Promise { + protected async create(message: IAppsMessage, appId: string): Promise { this.orch.debugLog(`The App ${appId} is creating a new message.`); - const convertedMessage = await this.orch.getConverters()?.get('messages').convertAppMessage(message); - - const sentMessage = await executeSendMessage(convertedMessage.u._id, convertedMessage); + const convertedMessage: IMessage | undefined = await this.orch.getConverters()?.get('messages').convertAppMessage(message); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const definedMessage = convertedMessage as IMessage; + const sentMessage = await executeSendMessage(definedMessage.u._id, definedMessage); return sentMessage._id; } - protected async getById(messageId: string, appId: string): Promise { + protected async getById(messageId: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is getting the message: "${messageId}"`); - return this.orch.getConverters()?.get('messages').convertById(messageId); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const message: IAppsMessage | undefined = await this.orch.getConverters()?.get('messages').convertById(messageId); + return message as IAppsMessage; } - protected async update(message: IMessage, appId: string): Promise { + protected async update(message: IAppsMessage, appId: string): Promise { this.orch.debugLog(`The App ${appId} is updating a message.`); if (!message.editor) { @@ -44,17 +46,18 @@ export class AppMessageBridge extends MessageBridge { throw new Error('A message must exist to update.'); } - const msg = await this.orch.getConverters()?.get('messages').convertAppMessage(message); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const msg: IMessage | undefined = await this.orch.getConverters()?.get('messages').convertAppMessage(message); const editor = await Users.findOneById(message.editor.id); if (!editor) { throw new Error('Invalid editor assigned to the message for the update.'); } - await updateMessage(msg, editor); + await updateMessage(msg as IMessage, editor); } - protected async delete(message: IMessage, user: IUser, appId: string): Promise { + protected async delete(message: IAppsMessage, user: IAppsUser, appId: string): Promise { this.orch.debugLog(`The App ${appId} is deleting a message.`); if (!message.id) { @@ -64,10 +67,10 @@ export class AppMessageBridge extends MessageBridge { const convertedMsg = await this.orch.getConverters()?.get('messages').convertAppMessage(message); const convertedUser = (await Users.findOneById(user.id)) || this.orch.getConverters()?.get('users').convertToRocketChat(user); - await deleteMessage(convertedMsg, convertedUser); + await deleteMessage(convertedMsg as IMessage, convertedUser); } - protected async notifyUser(user: IUser, message: IMessage, appId: string): Promise { + protected async notifyUser(user: IAppsUser, message: IAppsMessage, appId: string): Promise { this.orch.debugLog(`The App ${appId} is notifying a user.`); const msg = await this.orch.getConverters()?.get('messages').convertAppMessage(message); @@ -81,21 +84,23 @@ export class AppMessageBridge extends MessageBridge { }); } - protected async notifyRoom(room: IRoom, message: IMessage, appId: string): Promise { + protected async notifyRoom(room: IRoom, message: IAppsMessage, appId: string): Promise { this.orch.debugLog(`The App ${appId} is notifying a room's users.`); if (!room?.id) { return; } - const msg = await this.orch.getConverters()?.get('messages').convertAppMessage(message); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const msg: IMessage | undefined = await this.orch.getConverters()?.get('messages').convertAppMessage(message); + const convertedMessage = msg as IMessage; const users = (await Subscriptions.findByRoomIdWhenUserIdExists(room.id, { projection: { 'u._id': 1 } }).toArray()).map((s) => s.u._id); await Users.findByIds(users, { projection: { _id: 1 } }).forEach( ({ _id }: { _id: string }) => void api.broadcast('notify.ephemeralMessage', _id, room.id, { - ...msg, + ...convertedMessage, }), ); } diff --git a/apps/meteor/app/apps/server/bridges/moderation.ts b/apps/meteor/app/apps/server/bridges/moderation.ts index 4581f9a6ac6b..0f1e56bbdec3 100644 --- a/apps/meteor/app/apps/server/bridges/moderation.ts +++ b/apps/meteor/app/apps/server/bridges/moderation.ts @@ -1,13 +1,13 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { IMessage } from '@rocket.chat/apps-engine/definition/messages'; import type { IUser } from '@rocket.chat/apps-engine/definition/users'; import { ModerationBridge } from '@rocket.chat/apps-engine/server/bridges/ModerationBridge'; import { ModerationReports } from '@rocket.chat/models'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { reportMessage } from '../../../../server/lib/moderation/reportMessage'; export class AppModerationBridge extends ModerationBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/oauthApps.ts b/apps/meteor/app/apps/server/bridges/oauthApps.ts index 943c082ba85a..ba8ed8124690 100644 --- a/apps/meteor/app/apps/server/bridges/oauthApps.ts +++ b/apps/meteor/app/apps/server/bridges/oauthApps.ts @@ -1,3 +1,4 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { IOAuthApp, IOAuthAppParams } from '@rocket.chat/apps-engine/definition/accessors/IOAuthApp'; import { OAuthAppsBridge } from '@rocket.chat/apps-engine/server/bridges/OAuthAppsBridge'; import type { IOAuthApps } from '@rocket.chat/core-typings'; @@ -5,10 +6,8 @@ import { OAuthApps, Users } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; import { v4 as uuidv4 } from 'uuid'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; - export class AppOAuthAppsBridge extends OAuthAppsBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/persistence.ts b/apps/meteor/app/apps/server/bridges/persistence.ts index 3810ed367e99..857f6a561ed6 100644 --- a/apps/meteor/app/apps/server/bridges/persistence.ts +++ b/apps/meteor/app/apps/server/bridges/persistence.ts @@ -1,11 +1,10 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { RocketChatAssociationRecord } from '@rocket.chat/apps-engine/definition/metadata'; import { PersistenceBridge } from '@rocket.chat/apps-engine/server/bridges/PersistenceBridge'; -import type { InsertOneResult, UpdateResult } from 'mongodb'; - -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; +import type { InsertOneResult } from 'mongodb'; export class AppPersistenceBridge extends PersistenceBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } @@ -25,7 +24,7 @@ export class AppPersistenceBridge extends PersistenceBridge { return this.orch .getPersistenceModel() .insertOne({ appId, data }) - .then(({ insertedId }: InsertOneResult) => insertedId || ''); + .then(({ insertedId }: InsertOneResult) => (insertedId as unknown as string) || ''); } protected async createWithAssociations(data: object, associations: Array, appId: string): Promise { @@ -42,7 +41,7 @@ export class AppPersistenceBridge extends PersistenceBridge { return this.orch .getPersistenceModel() .insertOne({ appId, associations, data }) - .then(({ insertedId }: InsertOneResult) => insertedId || ''); + .then(({ insertedId }: InsertOneResult) => (insertedId as unknown as string) || ''); } protected async readById(id: string, appId: string): Promise { @@ -135,6 +134,6 @@ export class AppPersistenceBridge extends PersistenceBridge { return this.orch .getPersistenceModel() .update(query, { $set: { data } }, { upsert }) - .then(({ upsertedId }: UpdateResult) => upsertedId || ''); + .then(({ upsertedId }: any) => upsertedId || ''); } } diff --git a/apps/meteor/app/apps/server/bridges/roles.ts b/apps/meteor/app/apps/server/bridges/roles.ts index f973b7f49ed2..aa0fcdc7b80b 100644 --- a/apps/meteor/app/apps/server/bridges/roles.ts +++ b/apps/meteor/app/apps/server/bridges/roles.ts @@ -1,27 +1,30 @@ -import type { IRole } from '@rocket.chat/apps-engine/definition/roles'; +import type { IAppServerOrchestrator, IAppsRole } from '@rocket.chat/apps'; import { RoleBridge } from '@rocket.chat/apps-engine/server/bridges'; +import type { IRole } from '@rocket.chat/core-typings'; import { Roles } from '@rocket.chat/models'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; - export class AppRoleBridge extends RoleBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } - protected async getOneByIdOrName(idOrName: IRole['id'] | IRole['name'], appId: string): Promise { + protected async getOneByIdOrName(idOrName: IAppsRole['id'] | IAppsRole['name'], appId: string): Promise { this.orch.debugLog(`The App ${appId} is getting the roleByIdOrName: "${idOrName}"`); - const role = await Roles.findOneByIdOrName(idOrName); - return this.orch.getConverters()?.get('roles').convertRole(role); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const role: IRole | null = await Roles.findOneByIdOrName(idOrName); + return this.orch + .getConverters() + ?.get('roles') + .convertRole(role as IRole); } - protected async getCustomRoles(appId: string): Promise> { + protected async getCustomRoles(appId: string): Promise> { this.orch.debugLog(`The App ${appId} is getting the custom roles`); const cursor = Roles.findCustomRoles(); - const roles: IRole[] = []; + const roles: IAppsRole[] = []; for await (const role of cursor) { const convRole = await this.orch.getConverters()?.get('roles').convertRole(role); diff --git a/apps/meteor/app/apps/server/bridges/rooms.ts b/apps/meteor/app/apps/server/bridges/rooms.ts index 91b0049513f0..bbd24152716f 100644 --- a/apps/meteor/app/apps/server/bridges/rooms.ts +++ b/apps/meteor/app/apps/server/bridges/rooms.ts @@ -1,3 +1,4 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { IMessage } from '@rocket.chat/apps-engine/definition/messages'; import type { IRoom } from '@rocket.chat/apps-engine/definition/rooms'; import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; @@ -6,7 +7,6 @@ import { RoomBridge } from '@rocket.chat/apps-engine/server/bridges/RoomBridge'; import type { ISubscription, IUser as ICoreUser, IRoom as ICoreRoom } from '@rocket.chat/core-typings'; import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { createDirectMessage } from '../../../../server/methods/createDirectMessage'; import { createDiscussion } from '../../../discussion/server/methods/createDiscussion'; import { addUserToRoom } from '../../../lib/server/functions/addUserToRoom'; @@ -15,7 +15,7 @@ import { createChannelMethod } from '../../../lib/server/methods/createChannel'; import { createPrivateGroupMethod } from '../../../lib/server/methods/createPrivateGroup'; export class AppRoomBridge extends RoomBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } @@ -65,13 +65,17 @@ export class AppRoomBridge extends RoomBridge { protected async getById(roomId: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is getting the roomById: "${roomId}"`); - return this.orch.getConverters()?.get('rooms').convertById(roomId); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const promise: Promise = this.orch.getConverters()?.get('rooms').convertById(roomId); + return promise as Promise; } protected async getByName(roomName: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is getting the roomByName: "${roomName}"`); - return this.orch.getConverters()?.get('rooms').convertByName(roomName); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const promise: Promise = this.orch.getConverters()?.get('rooms').convertByName(roomName); + return promise as Promise; } protected async getCreatorById(roomId: string, appId: string): Promise { @@ -79,7 +83,7 @@ export class AppRoomBridge extends RoomBridge { const room = await Rooms.findOneById(roomId); - if (!room || !room.u || !room.u._id) { + if (!room?.u?._id) { return undefined; } @@ -91,7 +95,7 @@ export class AppRoomBridge extends RoomBridge { const room = await Rooms.findOneByName(roomName, {}); - if (!room || !room.u || !room.u._id) { + if (!room?.u?._id) { return undefined; } @@ -101,9 +105,12 @@ export class AppRoomBridge extends RoomBridge { protected async getMembers(roomId: string, appId: string): Promise> { this.orch.debugLog(`The App ${appId} is getting the room's members by room id: "${roomId}"`); const subscriptions = await Subscriptions.findByRoomId(roomId, {}); - return Promise.all( + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const promises: Promise<(IUser | undefined)[]> = Promise.all( (await subscriptions.toArray()).map((sub: ISubscription) => this.orch.getConverters()?.get('users').convertById(sub.u?._id)), ); + + return promises as Promise; } protected async getDirectByUsernames(usernames: Array, appId: string): Promise { @@ -124,7 +131,7 @@ export class AppRoomBridge extends RoomBridge { const rm = await this.orch.getConverters()?.get('rooms').convertAppRoom(room); - await Rooms.updateOne({ _id: rm._id }, { $set: rm }); + await Rooms.updateOne({ _id: rm._id }, { $set: rm as Partial }); for await (const username of members) { const member = await Users.findOneByUsername(username, {}); @@ -162,9 +169,10 @@ export class AppRoomBridge extends RoomBridge { throw new Error('There must be a parent room to create a discussion.'); } + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. const discussion = { prid: rcRoom.prid, - t_name: rcRoom.fname, + t_name: rcRoom.fname as string, pmid: rcMessage ? rcMessage._id : undefined, reply: reply && reply.trim() !== '' ? reply : undefined, users: members.length > 0 ? members : [], @@ -198,7 +206,7 @@ export class AppRoomBridge extends RoomBridge { }[]; // Was this a bug? const users = await Users.findByIds(subs.map((user: { uid: string }) => user.uid)).toArray(); - const userConverter = this.orch.getConverters()!.get('users'); - return users.map((user: ICoreUser) => userConverter!.convertToApp(user)); + const userConverter = this.orch.getConverters().get('users'); + return users.map((user: ICoreUser) => userConverter.convertToApp(user)); } } diff --git a/apps/meteor/app/apps/server/bridges/scheduler.ts b/apps/meteor/app/apps/server/bridges/scheduler.ts index 0b2995166e63..2f4799c79d70 100644 --- a/apps/meteor/app/apps/server/bridges/scheduler.ts +++ b/apps/meteor/app/apps/server/bridges/scheduler.ts @@ -1,13 +1,12 @@ import type { Job } from '@rocket.chat/agenda'; import { Agenda } from '@rocket.chat/agenda'; +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { IProcessor, IOnetimeSchedule, IRecurringSchedule, IJobContext } from '@rocket.chat/apps-engine/definition/scheduler'; import { StartupType } from '@rocket.chat/apps-engine/definition/scheduler'; import { SchedulerBridge } from '@rocket.chat/apps-engine/server/bridges/SchedulerBridge'; import { ObjectID } from 'bson'; import { MongoInternals } from 'meteor/mongo'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; - function _callProcessor(processor: IProcessor['processor']): (job: Job) => Promise { return (job) => { const data = job?.attrs?.data || {}; @@ -36,7 +35,7 @@ export class AppSchedulerBridge extends SchedulerBridge { private scheduler: Agenda; - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); this.scheduler = new Agenda({ mongo: (MongoInternals.defaultRemoteCollectionDriver().mongo as any).client.db(), diff --git a/apps/meteor/app/apps/server/bridges/settings.ts b/apps/meteor/app/apps/server/bridges/settings.ts index d61de9e8eca5..e90171813df8 100644 --- a/apps/meteor/app/apps/server/bridges/settings.ts +++ b/apps/meteor/app/apps/server/bridges/settings.ts @@ -1,11 +1,10 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { ISetting } from '@rocket.chat/apps-engine/definition/settings'; import { ServerSettingBridge } from '@rocket.chat/apps-engine/server/bridges/ServerSettingBridge'; import { Settings } from '@rocket.chat/models'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; - export class AppSettingBridge extends ServerSettingBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/thread.ts b/apps/meteor/app/apps/server/bridges/thread.ts index 313f60ecc4ac..099fe9184e00 100644 --- a/apps/meteor/app/apps/server/bridges/thread.ts +++ b/apps/meteor/app/apps/server/bridges/thread.ts @@ -1,10 +1,9 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { IMessage } from '@rocket.chat/apps-engine/definition/messages'; import { ThreadBridge } from '@rocket.chat/apps-engine/server/bridges/ThreadBridge'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; - export class AppThreadBridge extends ThreadBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/uiInteraction.ts b/apps/meteor/app/apps/server/bridges/uiInteraction.ts index 5783ac2b4eb3..fc68e4e30d3f 100644 --- a/apps/meteor/app/apps/server/bridges/uiInteraction.ts +++ b/apps/meteor/app/apps/server/bridges/uiInteraction.ts @@ -1,13 +1,12 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { IUIKitInteraction } from '@rocket.chat/apps-engine/definition/uikit'; import type { IUser } from '@rocket.chat/apps-engine/definition/users'; import { UiInteractionBridge as AppsEngineUiInteractionBridge } from '@rocket.chat/apps-engine/server/bridges/UiInteractionBridge'; import { api } from '@rocket.chat/core-services'; import type * as UiKit from '@rocket.chat/ui-kit'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; - export class UiInteractionBridge extends AppsEngineUiInteractionBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/uploads.ts b/apps/meteor/app/apps/server/bridges/uploads.ts index df16704a3196..b9d0ff67de58 100644 --- a/apps/meteor/app/apps/server/bridges/uploads.ts +++ b/apps/meteor/app/apps/server/bridges/uploads.ts @@ -1,9 +1,9 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { IUpload } from '@rocket.chat/apps-engine/definition/uploads'; import type { IUploadDetails } from '@rocket.chat/apps-engine/definition/uploads/IUploadDetails'; import { UploadBridge } from '@rocket.chat/apps-engine/server/bridges/UploadBridge'; import { determineFileType } from '../../../../ee/lib/misc/determineFileType'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { FileUpload } from '../../../file-upload/server'; import { sendFileMessage } from '../../../file-upload/server/methods/sendFileMessage'; import { sendFileLivechatMessage } from '../../../livechat/server/methods/sendFileLivechatMessage'; @@ -16,14 +16,16 @@ const getUploadDetails = (details: IUploadDetails): Partial => { return details; }; export class AppUploadBridge extends UploadBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } protected async getById(id: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is getting the upload: "${id}"`); - return this.orch.getConverters()?.get('uploads').convertById(id); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const promise: Promise = this.orch.getConverters()?.get('uploads').convertById(id); + return promise as Promise; } protected async getBuffer(upload: IUpload, appId: string): Promise { diff --git a/apps/meteor/app/apps/server/bridges/users.ts b/apps/meteor/app/apps/server/bridges/users.ts index d3c7dbc2a3d2..b0dfedd6273b 100644 --- a/apps/meteor/app/apps/server/bridges/users.ts +++ b/apps/meteor/app/apps/server/bridges/users.ts @@ -1,3 +1,4 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { IUserCreationOptions, IUser, UserType } from '@rocket.chat/apps-engine/definition/users'; import { UserBridge } from '@rocket.chat/apps-engine/server/bridges/UserBridge'; import { Presence } from '@rocket.chat/core-services'; @@ -5,7 +6,6 @@ import type { UserStatus } from '@rocket.chat/core-typings'; import { Subscriptions, Users } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { checkUsernameAvailability } from '../../../lib/server/functions/checkUsernameAvailability'; import { deleteUser } from '../../../lib/server/functions/deleteUser'; import { getUserCreatedByApp } from '../../../lib/server/functions/getUserCreatedByApp'; @@ -13,20 +13,23 @@ import { setUserActiveStatus } from '../../../lib/server/functions/setUserActive import { setUserAvatar } from '../../../lib/server/functions/setUserAvatar'; export class AppUserBridge extends UserBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } protected async getById(userId: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is getting the userId: "${userId}"`); - - return this.orch.getConverters()?.get('users').convertById(userId); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const promise: Promise = this.orch.getConverters()?.get('users').convertById(userId); + return promise as Promise; } protected async getByUsername(username: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is getting the username: "${username}"`); - return this.orch.getConverters()?.get('users').convertByUsername(username); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const promise: Promise = this.orch.getConverters()?.get('users').convertByUsername(username); + return promise as Promise; } protected async getAppUser(appId?: string): Promise { @@ -61,7 +64,11 @@ export class AppUserBridge extends UserBridge { protected async create(userDescriptor: Partial, appId: string, options?: IUserCreationOptions): Promise { this.orch.debugLog(`The App ${appId} is requesting to create a new user.`); - const user = this.orch.getConverters()?.get('users').convertToRocketChat(userDescriptor); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const user = this.orch + .getConverters() + ?.get('users') + .convertToRocketChat(userDescriptor as IUser); if (!user._id) { user._id = Random.id(); @@ -74,7 +81,7 @@ export class AppUserBridge extends UserBridge { switch (user.type) { case 'bot': case 'app': - if (!(await checkUsernameAvailability(user.username))) { + if (!(await checkUsernameAvailability(user.username as string))) { throw new Error(`The username "${user.username}" is already being used. Rename or remove the user using it to install this App`); } @@ -139,9 +146,12 @@ export class AppUserBridge extends UserBridge { if (!userId) { throw new Error('Invalid user id'); } - const convertedUser = await this.orch.getConverters()?.get('users').convertById(userId); - await setUserActiveStatus(convertedUser.id, false, confirmRelinquish); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const convertedUser: IUser | undefined = await this.orch.getConverters()?.get('users').convertById(userId); + const { id: uid } = convertedUser as IUser; + + await setUserActiveStatus(uid, false, confirmRelinquish); return true; } diff --git a/apps/meteor/app/apps/server/bridges/videoConferences.ts b/apps/meteor/app/apps/server/bridges/videoConferences.ts index c70f7f562fab..bebcb25a6f51 100644 --- a/apps/meteor/app/apps/server/bridges/videoConferences.ts +++ b/apps/meteor/app/apps/server/bridges/videoConferences.ts @@ -1,21 +1,23 @@ +import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import type { IVideoConfProvider } from '@rocket.chat/apps-engine/definition/videoConfProviders'; import type { AppVideoConference, VideoConference } from '@rocket.chat/apps-engine/definition/videoConferences'; import { VideoConferenceBridge } from '@rocket.chat/apps-engine/server/bridges/VideoConferenceBridge'; import { VideoConf } from '@rocket.chat/core-services'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { videoConfProviders } from '../../../../server/lib/videoConfProviders'; import type { AppVideoConferencesConverter } from '../converters/videoConferences'; export class AppVideoConferenceBridge extends VideoConferenceBridge { - constructor(private readonly orch: AppServerOrchestrator) { + constructor(private readonly orch: IAppServerOrchestrator) { super(); } protected async getById(callId: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is getting the video conference byId: "${callId}"`); - return this.orch.getConverters()?.get('videoConferences').convertById(callId); + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const promise: Promise = this.orch.getConverters()?.get('videoConferences').convertById(callId); + return promise as Promise; } protected async create(call: AppVideoConference, appId: string): Promise { diff --git a/apps/meteor/app/apps/server/converters/departments.js b/apps/meteor/app/apps/server/converters/departments.js index 087d1956ca6b..3fbfd07e9e99 100644 --- a/apps/meteor/app/apps/server/converters/departments.js +++ b/apps/meteor/app/apps/server/converters/departments.js @@ -1,6 +1,6 @@ import { LivechatDepartment } from '@rocket.chat/models'; -import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; +import { transformMappedData } from './transformMappedData'; export class AppDepartmentsConverter { constructor(orch) { diff --git a/apps/meteor/app/apps/server/converters/messages.js b/apps/meteor/app/apps/server/converters/messages.js index 6243bd2c603e..187a6519339a 100644 --- a/apps/meteor/app/apps/server/converters/messages.js +++ b/apps/meteor/app/apps/server/converters/messages.js @@ -1,7 +1,7 @@ import { Messages, Rooms, Users } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; -import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; +import { transformMappedData } from './transformMappedData'; export class AppMessagesConverter { constructor(orch) { diff --git a/apps/meteor/app/apps/server/converters/roles.ts b/apps/meteor/app/apps/server/converters/roles.ts index 4ac1f3956420..10841a038c93 100644 --- a/apps/meteor/app/apps/server/converters/roles.ts +++ b/apps/meteor/app/apps/server/converters/roles.ts @@ -1,10 +1,11 @@ +import type { IAppRolesConverter } from '@rocket.chat/apps'; import type { IRole as AppsEngineRole } from '@rocket.chat/apps-engine/definition/roles'; import type { IRole } from '@rocket.chat/core-typings'; import { Roles } from '@rocket.chat/models'; -import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; +import { transformMappedData } from './transformMappedData'; -export class AppRolesConverter { +export class AppRolesConverter implements IAppRolesConverter { async convertById(roleId: string): Promise { const role = await Roles.findOneById(roleId); @@ -22,8 +23,8 @@ export class AppRolesConverter { mandatory2fa: 'mandatory2fa', protected: 'protected', scope: 'scope', - }; + } as const; - return (await transformMappedData(role, map)) as unknown as AppsEngineRole; + return transformMappedData(role, map); } } diff --git a/apps/meteor/app/apps/server/converters/rooms.js b/apps/meteor/app/apps/server/converters/rooms.js index 905534212836..670c1a248a0f 100644 --- a/apps/meteor/app/apps/server/converters/rooms.js +++ b/apps/meteor/app/apps/server/converters/rooms.js @@ -1,7 +1,7 @@ import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; import { LivechatVisitors, Rooms, LivechatDepartment, Users } from '@rocket.chat/models'; -import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; +import { transformMappedData } from './transformMappedData'; export class AppRoomsConverter { constructor(orch) { diff --git a/apps/meteor/app/apps/server/converters/threads.ts b/apps/meteor/app/apps/server/converters/threads.ts index 19d3b4aeae6d..840f4f1613eb 100644 --- a/apps/meteor/app/apps/server/converters/threads.ts +++ b/apps/meteor/app/apps/server/converters/threads.ts @@ -1,18 +1,20 @@ -import type { IMessage as AppsEngineMessage } from '@rocket.chat/apps-engine/definition/messages'; +import type { IAppRoomsConverter, IAppThreadsConverter, IAppUsersConverter, IAppsMessage, IAppsUser } from '@rocket.chat/apps'; +import type { IMessage as AppsEngineMessage, IMessageAttachment } from '@rocket.chat/apps-engine/definition/messages'; import type { IRoom } from '@rocket.chat/apps-engine/definition/rooms'; +import type { IUser } from '@rocket.chat/core-typings'; import { isEditedMessage, type IMessage } from '@rocket.chat/core-typings'; import { Messages } from '@rocket.chat/models'; -import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; +import { transformMappedData } from './transformMappedData'; // eslint-disable-next-line @typescript-eslint/naming-convention interface Orchestrator { rooms: () => { - convertById(id: string): Promise; + convertById: IAppRoomsConverter['convertById']; }; users: () => { - convertById(id: string): Promise; - convertToApp(user: unknown): Promise; + convertById: IAppUsersConverter['convertById']; + convertToApp: IAppUsersConverter['convertToApp']; }; } @@ -34,7 +36,7 @@ const cachedFunction = any>(fn: F) => { }) as F; }; -export class AppThreadsConverter { +export class AppThreadsConverter implements IAppThreadsConverter { constructor( private readonly orch: { getConverters: () => { @@ -111,7 +113,7 @@ export class AppThreadsConverter { return convertUserById(editedBy._id); }, - attachments: async (message: IMessage) => { + attachments: async (message: IMessage): Promise => { if (!message.attachments) { return undefined; } @@ -119,26 +121,33 @@ export class AppThreadsConverter { delete message.attachments; return result; }, - sender: async (message: IMessage) => { + sender: async (message: IMessage): Promise => { + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. if (!message.u?._id) { - return undefined; + return undefined as unknown as IAppsUser; } - let user = await convertUserById(message.u._id); + let user: IAppsUser | undefined = await convertUserById(message.u._id); // When the sender of the message is a Guest (livechat) and not a user if (!user) { - user = await convertToApp(message.u); + user = await convertToApp(message.u as unknown as IUser); } - return user; + return user as IAppsUser; }, - }; + } as const; - return (await transformMappedData(msgObj, map)) as unknown as AppsEngineMessage; + // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. + const msgData = { + ...msgObj, + reactions: msgObj.reactions as unknown as AppsEngineMessage['reactions'], + } as IMessage & { reactions?: AppsEngineMessage['reactions'] }; + + return transformMappedData(msgData, map); } - async _convertAttachmentsToApp(attachments: NonNullable) { + async _convertAttachmentsToApp(attachments: NonNullable): Promise> { const map = { collapsed: 'collapsed', color: 'color', @@ -161,7 +170,7 @@ export class AppThreadsConverter { actions: 'actions', type: 'type', description: 'description', - author: (attachment: NonNullable[number]) => { + author: (attachment: NonNullable[number]): IMessageAttachment['author'] => { if (!('author_name' in attachment)) { return; } @@ -188,7 +197,7 @@ export class AppThreadsConverter { delete attachment.ts; return result; }, - }; + } as const; return Promise.all(attachments.map(async (attachment) => transformMappedData(attachment, map))); } diff --git a/apps/meteor/ee/lib/misc/transformMappedData.js b/apps/meteor/app/apps/server/converters/transformMappedData.ts similarity index 73% rename from apps/meteor/ee/lib/misc/transformMappedData.js rename to apps/meteor/app/apps/server/converters/transformMappedData.ts index 963f98cb6cf5..df2f16138d73 100644 --- a/apps/meteor/ee/lib/misc/transformMappedData.js +++ b/apps/meteor/app/apps/server/converters/transformMappedData.ts @@ -12,7 +12,7 @@ import cloneDeep from 'lodash.clonedeep'; * * ```javascript * const data = { _id: 'abcde123456', size: 10 }; - * const map = { id: '_id' } + * const map = Object.freeze({ id: '_id' }); * * transformMappedData(data, map); * // { id: 'abcde123456', _unmappedProperties_: { size: 10 } } @@ -43,14 +43,14 @@ import cloneDeep from 'lodash.clonedeep'; * // { id: 'abcde123456', newSize: 20, _unmappedProperties_: { size: 10 } } * * // You need to explicitly remove it from the original `data` - * const map = { + * const map = Object.freeze({ * id: '_id', * newSize: (data) => { * const result = data.size + 10; * delete data.size; * return result; * } - * }; + * }); * * transformMappedData(data, map); * // { id: 'abcde123456', newSize: 20, _unmappedProperties_: {} } @@ -62,9 +62,25 @@ import cloneDeep from 'lodash.clonedeep'; * @returns Object The data after transformations have been applied */ -export const transformMappedData = async (data, map) => { - const originalData = cloneDeep(data); - const transformedData = {}; +export const transformMappedData = async < + ResultType extends { + -readonly [p in keyof MapType]: MapType[p] extends keyof DataType + ? DataType[MapType[p]] + : MapType[p] extends (...args: any[]) => any + ? Awaited> + : never; + }, + DataType extends Record, + MapType extends { [p in string]: string | ((data: DataType) => Promise) | ((data: DataType) => unknown) }, + UnmappedProperties extends { + [p in keyof DataType as Exclude]: DataType[p]; + }, +>( + data: DataType, + map: MapType, +): Promise => { + const originalData: DataType = cloneDeep(data); + const transformedData: Record = {}; for await (const [to, from] of Object.entries(map)) { if (typeof from === 'function') { @@ -81,7 +97,8 @@ export const transformMappedData = async (data, map) => { } } - transformedData._unmappedProperties_ = originalData; - - return transformedData; + return { + ...(transformedData as ResultType), + _unmappedProperties_: originalData as unknown as UnmappedProperties, + }; }; diff --git a/apps/meteor/app/apps/server/converters/uploads.js b/apps/meteor/app/apps/server/converters/uploads.js index b6531854aa38..60f85a8aa72f 100644 --- a/apps/meteor/app/apps/server/converters/uploads.js +++ b/apps/meteor/app/apps/server/converters/uploads.js @@ -1,6 +1,6 @@ import { Uploads } from '@rocket.chat/models'; -import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; +import { transformMappedData } from './transformMappedData'; export class AppUploadsConverter { constructor(orch) { diff --git a/apps/meteor/app/apps/server/converters/videoConferences.ts b/apps/meteor/app/apps/server/converters/videoConferences.ts index 00eb4e915137..7a9120bf8508 100644 --- a/apps/meteor/app/apps/server/converters/videoConferences.ts +++ b/apps/meteor/app/apps/server/converters/videoConferences.ts @@ -1,27 +1,33 @@ -import type { VideoConference } from '@rocket.chat/apps-engine/definition/videoConferences'; +import type { IAppVideoConferencesConverter, AppsVideoConference } from '@rocket.chat/apps'; import { VideoConf } from '@rocket.chat/core-services'; -import type { IVideoConference } from '@rocket.chat/core-typings'; +import type { VideoConference } from '@rocket.chat/core-typings'; -export class AppVideoConferencesConverter { - async convertById(callId: string): Promise { +export class AppVideoConferencesConverter implements IAppVideoConferencesConverter { + async convertById(callId: string): Promise { const call = await VideoConf.getUnfiltered(callId); return this.convertVideoConference(call); } - convertVideoConference(call: IVideoConference | null): VideoConference | undefined { + convertVideoConference(call: undefined | null): undefined; + + convertVideoConference(call: VideoConference): AppsVideoConference; + + convertVideoConference(call: VideoConference | undefined | null): AppsVideoConference | undefined; + + convertVideoConference(call: VideoConference | undefined | null): AppsVideoConference | undefined { if (!call) { return; } return { ...call, - } as VideoConference; + } as AppsVideoConference; } - convertAppVideoConference(call: VideoConference): IVideoConference { + convertAppVideoConference(call: AppsVideoConference): VideoConference { return { ...call, - } as IVideoConference; + } as VideoConference; } } diff --git a/apps/meteor/app/apps/server/converters/visitors.js b/apps/meteor/app/apps/server/converters/visitors.js index a9f5d450efad..c8fb0b7c4a21 100644 --- a/apps/meteor/app/apps/server/converters/visitors.js +++ b/apps/meteor/app/apps/server/converters/visitors.js @@ -1,6 +1,6 @@ import { LivechatVisitors } from '@rocket.chat/models'; -import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; +import { transformMappedData } from './transformMappedData'; // TODO: check if functions from this converter can be async export class AppVisitorsConverter { diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 26dc53518ce3..5315be345652 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -122,6 +122,7 @@ "@types/later": "^1.2.8", "@types/ldapjs": "^2.2.5", "@types/less": "~3.0.5", + "@types/lodash.clonedeep": "^4.5.9", "@types/lodash.get": "^4.4.8", "@types/mailparser": "^3.4.3", "@types/marked": "^4.0.8", @@ -229,6 +230,7 @@ "@rocket.chat/account-utils": "workspace:^", "@rocket.chat/agenda": "workspace:^", "@rocket.chat/api-client": "workspace:^", + "@rocket.chat/apps": "workspace:^", "@rocket.chat/apps-engine": "1.41.0", "@rocket.chat/base64": "workspace:^", "@rocket.chat/cas-validate": "workspace:^", diff --git a/packages/apps/.eslintrc.json b/packages/apps/.eslintrc.json new file mode 100644 index 000000000000..a83aeda48e66 --- /dev/null +++ b/packages/apps/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "extends": ["@rocket.chat/eslint-config"], + "ignorePatterns": ["**/dist"] +} diff --git a/packages/apps/package.json b/packages/apps/package.json new file mode 100644 index 000000000000..ab37d0d388b0 --- /dev/null +++ b/packages/apps/package.json @@ -0,0 +1,29 @@ +{ + "name": "@rocket.chat/apps", + "version": "0.0.1", + "private": true, + "devDependencies": { + "@types/jest": "~29.5.7", + "eslint": "~8.45.0", + "jest": "~29.6.4", + "ts-jest": "~29.1.1", + "typescript": "~5.3.2" + }, + "scripts": { + "lint": "eslint --ext .js,.jsx,.ts,.tsx .", + "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix", + "test": "jest", + "build": "rm -rf dist && tsc -p tsconfig.json", + "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput" + }, + "main": "./dist/index.js", + "typings": "./dist/index.d.ts", + "files": [ + "/dist" + ], + "dependencies": { + "@rocket.chat/apps-engine": "^1.41.0", + "@rocket.chat/core-typings": "workspace:^", + "@rocket.chat/model-typings": "workspace:^" + } +} diff --git a/packages/apps/src/AppsEngine.ts b/packages/apps/src/AppsEngine.ts new file mode 100644 index 000000000000..117e93c0ec2f --- /dev/null +++ b/packages/apps/src/AppsEngine.ts @@ -0,0 +1,20 @@ +export type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; +export type { + IDepartment as IAppsDepartment, + ILivechatMessage as IAppsLivechatMessage, + ILivechatRoom as IAppsLivechatRoom, + IVisitor as IAppsVisitor, + IVisitorEmail as IAppsVisitorEmail, + IVisitorPhone as IAppsVisitorPhone, +} from '@rocket.chat/apps-engine/definition/livechat'; +export type { IMessage as IAppsMessage } from '@rocket.chat/apps-engine/definition/messages'; +export type { IUser as IAppsUser } from '@rocket.chat/apps-engine/definition/users'; +export type { IRole as IAppsRole } from '@rocket.chat/apps-engine/definition/roles'; +export type { IRoom as IAppsRoom } from '@rocket.chat/apps-engine/definition/rooms'; +export type { ISetting as IAppsSetting } from '@rocket.chat/apps-engine/definition/settings'; +export type { IUpload as IAppsUpload } from '@rocket.chat/apps-engine/definition/uploads'; +export type { + IVideoConference as IAppsVideoConference, + VideoConference as AppsVideoConference, +} from '@rocket.chat/apps-engine/definition/videoConferences'; +export { AppManager } from '@rocket.chat/apps-engine/server/AppManager'; diff --git a/packages/apps/src/IAppServerNotifier.ts b/packages/apps/src/IAppServerNotifier.ts new file mode 100644 index 000000000000..954f4a2ba5df --- /dev/null +++ b/packages/apps/src/IAppServerNotifier.ts @@ -0,0 +1,14 @@ +import type { AppStatus, IAppsSetting } from './AppsEngine'; + +export interface IAppServerNotifier { + appAdded(appId: string): Promise; + appRemoved(appId: string): Promise; + appUpdated(appId: string): Promise; + appStatusUpdated(appId: string, status: AppStatus): Promise; + appSettingsChange(appId: string, setting: IAppsSetting): Promise; + commandAdded(command: string): Promise; + commandDisabled(command: string): Promise; + commandUpdated(command: string): Promise; + commandRemoved(command: string): Promise; + actionsChanged(): Promise; +} diff --git a/packages/apps/src/IAppServerOrchestrator.ts b/packages/apps/src/IAppServerOrchestrator.ts new file mode 100644 index 000000000000..dbfc5aee7a20 --- /dev/null +++ b/packages/apps/src/IAppServerOrchestrator.ts @@ -0,0 +1,17 @@ +import type { AppManager } from '@rocket.chat/apps-engine/server/AppManager'; +import type { Logger } from '@rocket.chat/logger'; +import type { IAppsPersistenceModel } from '@rocket.chat/model-typings'; + +import type { IAppServerNotifier } from './IAppServerNotifier'; +import type { IAppConvertersMap } from './converters'; + +export interface IAppServerOrchestrator { + initialize(): void; + getNotifier(): IAppServerNotifier; + isDebugging(): boolean; + debugLog(...args: any[]): void; + getManager(): AppManager; + getConverters(): IAppConvertersMap; + getPersistenceModel(): IAppsPersistenceModel; + getRocketChatLogger(): Logger; +} diff --git a/packages/apps/src/converters/IAppConvertersMap.ts b/packages/apps/src/converters/IAppConvertersMap.ts new file mode 100644 index 000000000000..63c94d44cb75 --- /dev/null +++ b/packages/apps/src/converters/IAppConvertersMap.ts @@ -0,0 +1,27 @@ +import type { IAppDepartmentsConverter } from './IAppDepartmentsConverter'; +import type { IAppMessagesConverter } from './IAppMessagesConverter'; +import type { IAppRolesConverter } from './IAppRolesConverter'; +import type { IAppRoomsConverter } from './IAppRoomsConverter'; +import type { IAppSettingsConverter } from './IAppSettingsConverter'; +import type { IAppThreadsConverter } from './IAppThreadsConverter'; +import type { IAppUploadsConverter } from './IAppUploadsConverter'; +import type { IAppUsersConverter } from './IAppUsersConverter'; +import type { IAppVideoConferencesConverter } from './IAppVideoConferencesConverter'; +import type { IAppVisitorsConverter } from './IAppVisitorsConverter'; + +type AppConverters = { + departments: IAppDepartmentsConverter; + messages: IAppMessagesConverter; + rooms: IAppRoomsConverter; + roles: IAppRolesConverter; + settings: IAppSettingsConverter; + threads: IAppThreadsConverter; + uploads: IAppUploadsConverter; + users: IAppUsersConverter; + visitors: IAppVisitorsConverter; + videoConferences: IAppVideoConferencesConverter; +}; + +export interface IAppConvertersMap extends Map { + get(key: T): AppConverters[T]; +} diff --git a/packages/apps/src/converters/IAppDepartmentsConverter.ts b/packages/apps/src/converters/IAppDepartmentsConverter.ts new file mode 100644 index 000000000000..8dee30caa38c --- /dev/null +++ b/packages/apps/src/converters/IAppDepartmentsConverter.ts @@ -0,0 +1,13 @@ +import type { ILivechatDepartment } from '@rocket.chat/core-typings'; + +import type { IAppsDepartment } from '../AppsEngine'; + +export interface IAppDepartmentsConverter { + convertById(departmentId: ILivechatDepartment['_id']): Promise; + convertDepartment(department: undefined | null): Promise; + convertDepartment(department: ILivechatDepartment): Promise; + convertDepartment(department: ILivechatDepartment | undefined | null): Promise; + convertAppDepartment(department: undefined | null): undefined; + convertAppDepartment(department: IAppsDepartment): ILivechatDepartment; + convertAppDepartment(department: IAppsDepartment | undefined | null): ILivechatDepartment | undefined; +} diff --git a/packages/apps/src/converters/IAppMessagesConverter.ts b/packages/apps/src/converters/IAppMessagesConverter.ts new file mode 100644 index 000000000000..185e247895de --- /dev/null +++ b/packages/apps/src/converters/IAppMessagesConverter.ts @@ -0,0 +1,13 @@ +import type { IMessage } from '@rocket.chat/core-typings'; + +import type { IAppsMessage } from '../AppsEngine'; + +export interface IAppMessagesConverter { + convertById(messageId: IMessage['_id']): Promise; + convertMessage(message: undefined | null): Promise; + convertMessage(message: IMessage): Promise; + convertMessage(message: IMessage | undefined | null): Promise; + convertAppMessage(message: undefined | null): Promise; + convertAppMessage(message: IAppsMessage): Promise; + convertAppMessage(message: IAppsMessage | undefined | null): Promise; +} diff --git a/packages/apps/src/converters/IAppRolesConverter.ts b/packages/apps/src/converters/IAppRolesConverter.ts new file mode 100644 index 000000000000..07ed84232ade --- /dev/null +++ b/packages/apps/src/converters/IAppRolesConverter.ts @@ -0,0 +1,8 @@ +import type { IRole } from '@rocket.chat/core-typings'; + +import type { IAppsRole } from '../AppsEngine'; + +export interface IAppRolesConverter { + convertById(roleId: IRole['_id']): Promise; + convertRole(role: IRole): Promise; +} diff --git a/packages/apps/src/converters/IAppRoomsConverter.ts b/packages/apps/src/converters/IAppRoomsConverter.ts new file mode 100644 index 000000000000..9408b3f9b63c --- /dev/null +++ b/packages/apps/src/converters/IAppRoomsConverter.ts @@ -0,0 +1,14 @@ +import type { IRoom } from '@rocket.chat/core-typings'; + +import type { IAppsRoom, IAppsLivechatRoom } from '../AppsEngine'; + +export interface IAppRoomsConverter { + convertById(roomId: IRoom['_id']): Promise; + convertByName(roomName: IRoom['name']): Promise; + convertRoom(room: undefined | null): Promise; + convertRoom(room: IRoom): Promise; + convertRoom(room: IRoom | undefined | null): Promise; + convertAppRoom(room: undefined | null): Promise; + convertAppRoom(room: IAppsRoom): Promise; + convertAppRoom(room: IAppsRoom | undefined | null): Promise; +} diff --git a/packages/apps/src/converters/IAppSettingsConverter.ts b/packages/apps/src/converters/IAppSettingsConverter.ts new file mode 100644 index 000000000000..32db63e06c70 --- /dev/null +++ b/packages/apps/src/converters/IAppSettingsConverter.ts @@ -0,0 +1,8 @@ +import type { ISetting } from '@rocket.chat/core-typings'; + +import type { IAppsSetting } from '../AppsEngine'; + +export interface IAppSettingsConverter { + convertById(settingId: ISetting['_id']): Promise; + convertToApp(setting: ISetting): IAppsSetting; +} diff --git a/packages/apps/src/converters/IAppThreadsConverter.ts b/packages/apps/src/converters/IAppThreadsConverter.ts new file mode 100644 index 000000000000..5253651c2683 --- /dev/null +++ b/packages/apps/src/converters/IAppThreadsConverter.ts @@ -0,0 +1,14 @@ +import type { IMessage } from '@rocket.chat/core-typings'; + +import type { IAppsMessage, IAppsRoom } from '../AppsEngine'; +import type { IAppUsersConverter } from './IAppUsersConverter'; + +export interface IAppThreadsConverter { + convertById(threadId: string): Promise; + convertMessage( + msgObj: IMessage, + room: IAppsRoom, + convertUserById: IAppUsersConverter['convertById'], + convertToApp: IAppUsersConverter['convertToApp'], + ): Promise; +} diff --git a/packages/apps/src/converters/IAppUploadsConverter.ts b/packages/apps/src/converters/IAppUploadsConverter.ts new file mode 100644 index 000000000000..4c7e4c2855c9 --- /dev/null +++ b/packages/apps/src/converters/IAppUploadsConverter.ts @@ -0,0 +1,13 @@ +import type { IUpload } from '@rocket.chat/core-typings'; + +import type { IAppsUpload } from '../AppsEngine'; + +export interface IAppUploadsConverter { + convertById(uploadId: string): Promise; + convertToApp(upload: undefined | null): Promise; + convertToApp(upload: IUpload): Promise; + convertToApp(upload: IUpload | undefined | null): Promise; + convertToRocketChat(upload: undefined | null): undefined; + convertToRocketChat(upload: IAppsUpload): IUpload; + convertToRocketChat(upload: IAppsUpload | undefined | null): IUpload | undefined; +} diff --git a/packages/apps/src/converters/IAppUsersConverter.ts b/packages/apps/src/converters/IAppUsersConverter.ts new file mode 100644 index 000000000000..8d67cb9e5240 --- /dev/null +++ b/packages/apps/src/converters/IAppUsersConverter.ts @@ -0,0 +1,14 @@ +import type { IUser } from '@rocket.chat/core-typings'; + +import type { IAppsUser } from '../AppsEngine'; + +export interface IAppUsersConverter { + convertById(userId: IUser['_id']): Promise; + convertByUsername(username: IUser['username']): Promise; + convertToApp(user: undefined | null): undefined; + convertToApp(user: IUser): IAppsUser; + convertToApp(user: IUser | undefined | null): IAppsUser | undefined; + convertToRocketChat(user: undefined | null): undefined; + convertToRocketChat(user: IAppsUser): IUser; + convertToRocketChat(user: IAppsUser | undefined | null): IUser | undefined; +} diff --git a/packages/apps/src/converters/IAppVideoConferencesConverter.ts b/packages/apps/src/converters/IAppVideoConferencesConverter.ts new file mode 100644 index 000000000000..b599c29e3864 --- /dev/null +++ b/packages/apps/src/converters/IAppVideoConferencesConverter.ts @@ -0,0 +1,11 @@ +import type { VideoConference } from '@rocket.chat/core-typings'; + +import type { AppsVideoConference } from '../AppsEngine'; + +export interface IAppVideoConferencesConverter { + convertById(videoConferenceId: VideoConference['_id']): Promise; + convertVideoConference(videoConference: undefined | null): undefined; + convertVideoConference(videoConference: VideoConference): AppsVideoConference; + convertVideoConference(videoConference: VideoConference | undefined | null): AppsVideoConference | undefined; + convertAppVideoConference(videoConference: AppsVideoConference): VideoConference; +} diff --git a/packages/apps/src/converters/IAppVisitorsConverter.ts b/packages/apps/src/converters/IAppVisitorsConverter.ts new file mode 100644 index 000000000000..575845b57c10 --- /dev/null +++ b/packages/apps/src/converters/IAppVisitorsConverter.ts @@ -0,0 +1,14 @@ +import type { ILivechatVisitor } from '@rocket.chat/core-typings'; + +import type { IAppsVisitor } from '../AppsEngine'; + +export interface IAppVisitorsConverter { + convertById(visitorId: ILivechatVisitor['_id']): Promise; + convertByToken(token: string): Promise; + convertVisitor(visitor: undefined | null): Promise; + convertVisitor(visitor: ILivechatVisitor): Promise; + convertVisitor(visitor: ILivechatVisitor | undefined | null): Promise; + convertAppVisitor(visitor: undefined | null): undefined; + convertAppVisitor(visitor: IAppsVisitor): ILivechatVisitor; + convertAppVisitor(visitor: IAppsVisitor | undefined | null): ILivechatVisitor | undefined; +} diff --git a/packages/apps/src/converters/index.ts b/packages/apps/src/converters/index.ts new file mode 100644 index 000000000000..61560afb5a4e --- /dev/null +++ b/packages/apps/src/converters/index.ts @@ -0,0 +1,11 @@ +export * from './IAppConvertersMap'; +export * from './IAppDepartmentsConverter'; +export * from './IAppMessagesConverter'; +export * from './IAppRolesConverter'; +export * from './IAppRoomsConverter'; +export * from './IAppSettingsConverter'; +export * from './IAppThreadsConverter'; +export * from './IAppUploadsConverter'; +export * from './IAppUsersConverter'; +export * from './IAppVideoConferencesConverter'; +export * from './IAppVisitorsConverter'; diff --git a/packages/apps/src/index.ts b/packages/apps/src/index.ts new file mode 100644 index 000000000000..e137fa3cf007 --- /dev/null +++ b/packages/apps/src/index.ts @@ -0,0 +1,4 @@ +export * from './converters'; +export * from './AppsEngine'; +export * from './IAppServerNotifier'; +export * from './IAppServerOrchestrator'; diff --git a/packages/apps/tsconfig.json b/packages/apps/tsconfig.json new file mode 100644 index 000000000000..52e9dd8c4976 --- /dev/null +++ b/packages/apps/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.server.json", + "compilerOptions": { + "declaration": true, + "rootDir": "./src", + "outDir": "./dist" + }, + "include": ["./src/**/*"] +} diff --git a/yarn.lock b/yarn.lock index 5a9c7311afaa..431dee85f2b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8331,7 +8331,7 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/apps-engine@npm:1.41.0": +"@rocket.chat/apps-engine@npm:1.41.0, @rocket.chat/apps-engine@npm:^1.41.0": version: 1.41.0 resolution: "@rocket.chat/apps-engine@npm:1.41.0" dependencies: @@ -8349,6 +8349,21 @@ __metadata: languageName: node linkType: hard +"@rocket.chat/apps@workspace:^, @rocket.chat/apps@workspace:packages/apps": + version: 0.0.0-use.local + resolution: "@rocket.chat/apps@workspace:packages/apps" + dependencies: + "@rocket.chat/apps-engine": ^1.41.0 + "@rocket.chat/core-typings": "workspace:^" + "@rocket.chat/model-typings": "workspace:^" + "@types/jest": ~29.5.7 + eslint: ~8.45.0 + jest: ~29.6.4 + ts-jest: ~29.1.1 + typescript: ~5.3.2 + languageName: unknown + linkType: soft + "@rocket.chat/authorization-service@workspace:ee/apps/authorization-service": version: 0.0.0-use.local resolution: "@rocket.chat/authorization-service@workspace:ee/apps/authorization-service" @@ -9146,6 +9161,7 @@ __metadata: "@rocket.chat/account-utils": "workspace:^" "@rocket.chat/agenda": "workspace:^" "@rocket.chat/api-client": "workspace:^" + "@rocket.chat/apps": "workspace:^" "@rocket.chat/apps-engine": 1.41.0 "@rocket.chat/base64": "workspace:^" "@rocket.chat/cas-validate": "workspace:^" @@ -9253,6 +9269,7 @@ __metadata: "@types/ldapjs": ^2.2.5 "@types/less": ~3.0.5 "@types/lodash": ^4.14.200 + "@types/lodash.clonedeep": ^4.5.9 "@types/lodash.debounce": ^4.0.8 "@types/lodash.get": ^4.4.8 "@types/mailparser": ^3.4.3 @@ -13615,6 +13632,15 @@ __metadata: languageName: node linkType: hard +"@types/lodash.clonedeep@npm:^4.5.9": + version: 4.5.9 + resolution: "@types/lodash.clonedeep@npm:4.5.9" + dependencies: + "@types/lodash": "*" + checksum: ef85512b7dce7a4f981a818ae44d11982907e1f26b5b26bedf0957c35e8591eb8e1d24fa31ca851d4b40e0a1ee88563853d762412691fe5f357e8335cead2325 + languageName: node + linkType: hard + "@types/lodash.debounce@npm:^4.0.8": version: 4.0.8 resolution: "@types/lodash.debounce@npm:4.0.8" From efb77c09e2b0a5492492de7a87dc0d69297a6888 Mon Sep 17 00:00:00 2001 From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Mon, 4 Mar 2024 14:36:31 -0300 Subject: [PATCH 084/207] chore: move utilitary functions out of ee folder (#31207) --- apps/meteor/client/components/voip/room/VoipRoomForeword.tsx | 2 +- .../meteor/{ee => }/client/lib/voip/parseOutboundPhoneNumber.ts | 0 apps/meteor/client/providers/CallProvider/CallProvider.tsx | 2 +- .../sidebar/footer/voip/hooks/useOmnichannelContactLabel.ts | 2 +- .../client/views/omnichannel/directory/calls/CallTableRow.tsx | 2 +- .../omnichannel/directory/calls/contextualBar/VoipInfo.tsx | 2 +- .../views/omnichannel/directory/contacts/ContactTable.tsx | 2 +- .../directory/contacts/contextualBar/ContactInfo.tsx | 2 +- .../client/views/room/Header/Omnichannel/VoipRoomHeader.tsx | 2 +- 9 files changed, 8 insertions(+), 8 deletions(-) rename apps/meteor/{ee => }/client/lib/voip/parseOutboundPhoneNumber.ts (100%) diff --git a/apps/meteor/client/components/voip/room/VoipRoomForeword.tsx b/apps/meteor/client/components/voip/room/VoipRoomForeword.tsx index 983c5cdc2c4a..615fee8714aa 100644 --- a/apps/meteor/client/components/voip/room/VoipRoomForeword.tsx +++ b/apps/meteor/client/components/voip/room/VoipRoomForeword.tsx @@ -5,7 +5,7 @@ import type { ReactElement } from 'react'; import React from 'react'; import { getUserAvatarURL } from '../../../../app/utils/client'; -import { parseOutboundPhoneNumber } from '../../../../ee/client/lib/voip/parseOutboundPhoneNumber'; +import { parseOutboundPhoneNumber } from '../../../lib/voip/parseOutboundPhoneNumber'; export const VoipRoomForeword = ({ room }: { room: IVoipRoom }): ReactElement => { const t = useTranslation(); diff --git a/apps/meteor/ee/client/lib/voip/parseOutboundPhoneNumber.ts b/apps/meteor/client/lib/voip/parseOutboundPhoneNumber.ts similarity index 100% rename from apps/meteor/ee/client/lib/voip/parseOutboundPhoneNumber.ts rename to apps/meteor/client/lib/voip/parseOutboundPhoneNumber.ts diff --git a/apps/meteor/client/providers/CallProvider/CallProvider.tsx b/apps/meteor/client/providers/CallProvider/CallProvider.tsx index 0de0a3fda0c3..ce0d9385dc64 100644 --- a/apps/meteor/client/providers/CallProvider/CallProvider.tsx +++ b/apps/meteor/client/providers/CallProvider/CallProvider.tsx @@ -29,13 +29,13 @@ import { createPortal } from 'react-dom'; import type { OutgoingByeRequest } from 'sip.js/lib/core'; 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'; import type { CallContextValue } from '../../contexts/CallContext'; import { CallContext, useIsVoipEnterprise } from '../../contexts/CallContext'; import { useDialModal } from '../../hooks/useDialModal'; import { roomCoordinator } from '../../lib/rooms/roomCoordinator'; import type { QueueAggregator } from '../../lib/voip/QueueAggregator'; +import { parseOutboundPhoneNumber } from '../../lib/voip/parseOutboundPhoneNumber'; import { useVoipSounds } from './hooks/useVoipSounds'; type NetworkState = 'online' | 'offline'; diff --git a/apps/meteor/client/sidebar/footer/voip/hooks/useOmnichannelContactLabel.ts b/apps/meteor/client/sidebar/footer/voip/hooks/useOmnichannelContactLabel.ts index e905f6cf38e6..6b3d59830d1e 100644 --- a/apps/meteor/client/sidebar/footer/voip/hooks/useOmnichannelContactLabel.ts +++ b/apps/meteor/client/sidebar/footer/voip/hooks/useOmnichannelContactLabel.ts @@ -2,7 +2,7 @@ import type { ICallerInfo } from '@rocket.chat/core-typings'; import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; -import { parseOutboundPhoneNumber } from '../../../../../ee/client/lib/voip/parseOutboundPhoneNumber'; +import { parseOutboundPhoneNumber } from '../../../../lib/voip/parseOutboundPhoneNumber'; export const useOmnichannelContactLabel = (caller: ICallerInfo): string => { const getContactBy = useEndpoint('GET', '/v1/omnichannel/contact.search'); diff --git a/apps/meteor/client/views/omnichannel/directory/calls/CallTableRow.tsx b/apps/meteor/client/views/omnichannel/directory/calls/CallTableRow.tsx index 403f4312772f..616baee2b531 100644 --- a/apps/meteor/client/views/omnichannel/directory/calls/CallTableRow.tsx +++ b/apps/meteor/client/views/omnichannel/directory/calls/CallTableRow.tsx @@ -4,9 +4,9 @@ import moment from 'moment'; import type { ReactElement } from 'react'; import React, { useCallback } from 'react'; -import { parseOutboundPhoneNumber } from '../../../../../ee/client/lib/voip/parseOutboundPhoneNumber'; import { GenericTableRow, GenericTableCell } from '../../../../components/GenericTable'; import { useIsCallReady } from '../../../../contexts/CallContext'; +import { parseOutboundPhoneNumber } from '../../../../lib/voip/parseOutboundPhoneNumber'; import { CallDialpadButton } from '../components/CallDialpadButton'; type CallTableRowProps = { diff --git a/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfo.tsx b/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfo.tsx index 65792b6d73e2..ebfa98273e30 100644 --- a/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfo.tsx +++ b/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfo.tsx @@ -6,7 +6,6 @@ import moment from 'moment'; import type { ReactElement } from 'react'; import React, { useMemo } from 'react'; -import { parseOutboundPhoneNumber } from '../../../../../../ee/client/lib/voip/parseOutboundPhoneNumber'; import { ContextualbarIcon, ContextualbarHeader, @@ -18,6 +17,7 @@ import { import InfoPanel from '../../../../../components/InfoPanel'; import { UserStatus } from '../../../../../components/UserStatus'; import { useIsCallReady } from '../../../../../contexts/CallContext'; +import { parseOutboundPhoneNumber } from '../../../../../lib/voip/parseOutboundPhoneNumber'; import AgentInfoDetails from '../../../components/AgentInfoDetails'; import AgentField from '../../components/AgentField'; import { InfoField } from './InfoField'; diff --git a/apps/meteor/client/views/omnichannel/directory/contacts/ContactTable.tsx b/apps/meteor/client/views/omnichannel/directory/contacts/ContactTable.tsx index a6516e082e88..1238732edf66 100644 --- a/apps/meteor/client/views/omnichannel/directory/contacts/ContactTable.tsx +++ b/apps/meteor/client/views/omnichannel/directory/contacts/ContactTable.tsx @@ -5,7 +5,6 @@ import { hashQueryKey } from '@tanstack/react-query'; import type { ReactElement } from 'react'; import React, { useMemo, useState } from 'react'; -import { parseOutboundPhoneNumber } from '../../../../../ee/client/lib/voip/parseOutboundPhoneNumber'; import FilterByText from '../../../../components/FilterByText'; import GenericNoResults from '../../../../components/GenericNoResults'; import { @@ -21,6 +20,7 @@ import { usePagination } from '../../../../components/GenericTable/hooks/usePagi import { useSort } from '../../../../components/GenericTable/hooks/useSort'; import { useIsCallReady } from '../../../../contexts/CallContext'; import { useFormatDate } from '../../../../hooks/useFormatDate'; +import { parseOutboundPhoneNumber } from '../../../../lib/voip/parseOutboundPhoneNumber'; import { CallDialpadButton } from '../components/CallDialpadButton'; import { useCurrentContacts } from './hooks/useCurrentContacts'; 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 9578c6a07b83..f16f70c76446 100644 --- a/apps/meteor/client/views/omnichannel/directory/contacts/contextualBar/ContactInfo.tsx +++ b/apps/meteor/client/views/omnichannel/directory/contacts/contextualBar/ContactInfo.tsx @@ -7,12 +7,12 @@ import { useQuery } from '@tanstack/react-query'; 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'; import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../../../components/Contextualbar'; import { UserStatus } from '../../../../../components/UserStatus'; import { useIsCallReady } from '../../../../../contexts/CallContext'; import { useFormatDate } from '../../../../../hooks/useFormatDate'; +import { parseOutboundPhoneNumber } from '../../../../../lib/voip/parseOutboundPhoneNumber'; import AgentInfoDetails from '../../../components/AgentInfoDetails'; import CustomField from '../../../components/CustomField'; import Field from '../../../components/Field'; diff --git a/apps/meteor/client/views/room/Header/Omnichannel/VoipRoomHeader.tsx b/apps/meteor/client/views/room/Header/Omnichannel/VoipRoomHeader.tsx index c4c5a63a00c1..042379b9fde6 100644 --- a/apps/meteor/client/views/room/Header/Omnichannel/VoipRoomHeader.tsx +++ b/apps/meteor/client/views/room/Header/Omnichannel/VoipRoomHeader.tsx @@ -5,8 +5,8 @@ import type { FC } 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'; +import { parseOutboundPhoneNumber } from '../../../../lib/voip/parseOutboundPhoneNumber'; import type { RoomHeaderProps } from '../RoomHeader'; import RoomHeader from '../RoomHeader'; import { BackButton } from './BackButton'; From cac5c26afcc482f2b0719b6ed8a6cc448f4ee126 Mon Sep 17 00:00:00 2001 From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Mon, 4 Mar 2024 17:09:51 -0300 Subject: [PATCH 085/207] chore: update ts version on recently merged package (#31885) --- packages/apps/package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/apps/package.json b/packages/apps/package.json index ab37d0d388b0..33c46a314d21 100644 --- a/packages/apps/package.json +++ b/packages/apps/package.json @@ -7,7 +7,7 @@ "eslint": "~8.45.0", "jest": "~29.6.4", "ts-jest": "~29.1.1", - "typescript": "~5.3.2" + "typescript": "~5.3.3" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/yarn.lock b/yarn.lock index 431dee85f2b0..1a88f52feaff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8360,7 +8360,7 @@ __metadata: eslint: ~8.45.0 jest: ~29.6.4 ts-jest: ~29.1.1 - typescript: ~5.3.2 + typescript: ~5.3.3 languageName: unknown linkType: soft From a0117345a964c9e8cdb4df13f0bba5e56eeb6674 Mon Sep 17 00:00:00 2001 From: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Date: Tue, 5 Mar 2024 09:41:57 -0300 Subject: [PATCH 086/207] chore: Deprecate `insertOrUpdateUser` method (#31884) --- .changeset/cuddly-rocks-fix.md | 5 +++++ apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts | 3 +++ 2 files changed, 8 insertions(+) create mode 100644 .changeset/cuddly-rocks-fix.md diff --git a/.changeset/cuddly-rocks-fix.md b/.changeset/cuddly-rocks-fix.md new file mode 100644 index 000000000000..ced0904df59b --- /dev/null +++ b/.changeset/cuddly-rocks-fix.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": minor +--- + +Deprecate `insertOrUpdateUser` Meteor method diff --git a/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts b/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts index d00f6d30b314..5a8b1c8d1a58 100644 --- a/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts +++ b/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts @@ -4,6 +4,7 @@ import { Meteor } from 'meteor/meteor'; import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; import { saveUser } from '../functions/saveUser'; +import { methodDeprecationLogger } from '../lib/deprecationWarningLogger'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -14,6 +15,8 @@ declare module '@rocket.chat/ui-contexts' { Meteor.methods({ insertOrUpdateUser: twoFactorRequired(async (userData) => { + methodDeprecationLogger.method('insertOrUpdateUser', '8.0.0'); + check(userData, Object); if (!Meteor.userId()) { From b0aace714711a68f30b93067726324d3e18d13ee Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Tue, 5 Mar 2024 19:02:35 +0530 Subject: [PATCH 087/207] fix: livechat agent status set to available after reactivation (#31651) --- .changeset/nice-ducks-shout.md | 5 ++ .../livechat/server/hooks/afterUserActions.ts | 2 +- .../app/livechat/server/lib/LivechatTyped.ts | 8 +++ apps/meteor/tests/data/livechat/users.ts | 9 ++- apps/meteor/tests/end-to-end/api/01-users.js | 56 +++++++++++++++++++ 5 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 .changeset/nice-ducks-shout.md diff --git a/.changeset/nice-ducks-shout.md b/.changeset/nice-ducks-shout.md new file mode 100644 index 000000000000..fc8ce46dcace --- /dev/null +++ b/.changeset/nice-ducks-shout.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed auto-availability of reactivated livechat agents; they now stay 'Not Available' until manually set to 'Available' diff --git a/apps/meteor/app/livechat/server/hooks/afterUserActions.ts b/apps/meteor/app/livechat/server/hooks/afterUserActions.ts index 6fe7f1db7479..2d0120c93c82 100644 --- a/apps/meteor/app/livechat/server/hooks/afterUserActions.ts +++ b/apps/meteor/app/livechat/server/hooks/afterUserActions.ts @@ -39,7 +39,7 @@ const handleDeactivateUser = async (user: IUser) => { const handleActivateUser = async (user: IUser) => { if (isAgent(user) && user.username) { - await LivechatTyped.addAgent(user.username); + await LivechatTyped.afterAgentUserActivated(user); } }; diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 79d225626ea1..fb1098db4272 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -1682,6 +1682,14 @@ class LivechatClass { return false; } + async afterAgentUserActivated(user: IUser) { + if (!user.roles.includes('livechat-agent')) { + throw new Error('invalid-user-role'); + } + await Users.setOperator(user._id, true); + callbacks.runAsync('livechat.onNewAgentCreated', user._id); + } + async addManager(username: string) { check(username, String); diff --git a/apps/meteor/tests/data/livechat/users.ts b/apps/meteor/tests/data/livechat/users.ts index 38fb176faaa4..3a21cbee923c 100644 --- a/apps/meteor/tests/data/livechat/users.ts +++ b/apps/meteor/tests/data/livechat/users.ts @@ -1,5 +1,5 @@ import { faker } from "@faker-js/faker"; -import type { IUser } from "@rocket.chat/core-typings"; +import type { ILivechatAgent, IUser } from "@rocket.chat/core-typings"; import { IUserCredentialsHeader, password } from "../user"; import { createUser, login } from "../users.helper"; import { createAgent, makeAgentAvailable } from "./rooms"; @@ -24,6 +24,13 @@ export const createBotAgent = async (): Promise<{ export const getRandomVisitorToken = (): string => faker.string.alphanumeric(17); +export const getAgent = async (userId: string): Promise => { + const { body } = await request.get(api(`livechat/users/agent/${userId}`)) + .set(credentials) + .expect(200); + return body.user; +} + export const removeAgent = async (userId: string): Promise => { await request.delete(api(`livechat/users/agent/${userId}`)) .set(credentials) 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 eaafc97527a3..1aa30d3322bb 100644 --- a/apps/meteor/tests/end-to-end/api/01-users.js +++ b/apps/meteor/tests/end-to-end/api/01-users.js @@ -9,6 +9,8 @@ import { getCredentials, api, request, credentials, apiEmail, apiUsername, log, import { MAX_BIO_LENGTH, MAX_NICKNAME_LENGTH } from '../../data/constants.ts'; import { customFieldText, clearCustomFields, setCustomFields } from '../../data/custom-fields.js'; import { imgURL } from '../../data/interactions'; +import { createAgent, makeAgentAvailable } from '../../data/livechat/rooms'; +import { removeAgent, getAgent } from '../../data/livechat/users'; import { updatePermission, updateSetting } from '../../data/permissions.helper'; import { createRoom, deleteRoom } from '../../data/rooms.helper'; import { adminEmail, preferences, password, adminUsername } from '../../data/user'; @@ -3114,6 +3116,21 @@ describe('[Users]', function () { describe('[/users.setActiveStatus]', () => { let user; + let agent; + let agentUser; + + before(async () => { + agentUser = await createUser(); + const agentUserCredentials = await login(agentUser.username, password); + await createAgent(agentUser.username); + await makeAgentAvailable(agentUserCredentials); + + agent = { + user: agentUser, + credentials: agentUserCredentials, + }; + }); + before((done) => { const username = `user.test.${Date.now()}`; const email = `${username}@rocket.chat`; @@ -3156,6 +3173,12 @@ describe('[Users]', function () { .end(() => updatePermission('edit-other-user-active-status', ['admin']).then(done)); user = undefined; }); + + after(async () => { + await removeAgent(agent.user._id); + await deleteUser(agent.user); + }); + it('should set other user active status to false when the logged user has the necessary permission(edit-other-user-active-status)', (done) => { request .post(api('users.setActiveStatus')) @@ -3484,6 +3507,39 @@ describe('[Users]', function () { await deleteUser(testUser); }); + it('should make agents not-available when the user is deactivated', async () => { + await makeAgentAvailable(agent.credentials); + await request + .post(api('users.setActiveStatus')) + .set(credentials) + .send({ + activeStatus: false, + userId: agent.user._id, + }) + .expect('Content-Type', 'application/json') + .expect(200); + + const agentInfo = await getAgent(agent.user._id); + expect(agentInfo).to.have.property('statusLivechat', 'not-available'); + }); + + it('should not make agents available when the user is activated', async () => { + let agentInfo = await getAgent(agent.user._id); + expect(agentInfo).to.have.property('statusLivechat', 'not-available'); + + await request + .post(api('users.setActiveStatus')) + .set(credentials) + .send({ + activeStatus: true, + userId: agent.user._id, + }) + .expect('Content-Type', 'application/json') + .expect(200); + + agentInfo = await getAgent(agent.user._id); + expect(agentInfo).to.have.property('statusLivechat', 'not-available'); + }); }); describe('[/users.deactivateIdle]', () => { From 0f0d63150c283ab3353232abf252cca5a08de5f6 Mon Sep 17 00:00:00 2001 From: Gustavo Reis Bauer Date: Tue, 5 Mar 2024 14:09:42 -0300 Subject: [PATCH 088/207] fix: New users are not automatically inserted in auto-join rooms inside default teams (#31720) Co-authored-by: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> --- .changeset/calm-penguins-do.md | 5 + .../functions/addUserToDefaultChannels.ts | 8 +- .../server/functions/getDefaultChannels.ts | 25 +++ apps/meteor/tests/data/rooms.helper.js | 17 +- apps/meteor/tests/data/uploads.helper.ts | 1 + apps/meteor/tests/end-to-end/api/01-users.js | 159 +++++++++++++++++- .../end-to-end/apps/05-video-conferences.ts | 1 + 7 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 .changeset/calm-penguins-do.md create mode 100644 apps/meteor/app/lib/server/functions/getDefaultChannels.ts diff --git a/.changeset/calm-penguins-do.md b/.changeset/calm-penguins-do.md new file mode 100644 index 000000000000..6a83fae9030d --- /dev/null +++ b/.changeset/calm-penguins-do.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Now we are considering channels with auto-join inside teams on user creation diff --git a/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts b/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts index 835f59419ad5..563022ea5a6a 100644 --- a/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts +++ b/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts @@ -1,15 +1,15 @@ import { Message } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; -import { Subscriptions, Rooms } from '@rocket.chat/models'; +import { Subscriptions } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; import { getSubscriptionAutotranslateDefaultConfig } from '../../../../server/lib/getSubscriptionAutotranslateDefaultConfig'; +import { getDefaultChannels } from './getDefaultChannels'; export const addUserToDefaultChannels = async function (user: IUser, silenced?: boolean): Promise { await callbacks.run('beforeJoinDefaultChannels', user); - const defaultRooms = await Rooms.findByDefaultAndTypes(true, ['c', 'p'], { - projection: { usernames: 0 }, - }).toArray(); + const defaultRooms = await getDefaultChannels(); + for await (const room of defaultRooms) { if (!(await Subscriptions.findOneByRoomIdAndUserId(room._id, user._id, { projection: { _id: 1 } }))) { const autoTranslateConfig = getSubscriptionAutotranslateDefaultConfig(user); diff --git a/apps/meteor/app/lib/server/functions/getDefaultChannels.ts b/apps/meteor/app/lib/server/functions/getDefaultChannels.ts new file mode 100644 index 000000000000..e47518dd604f --- /dev/null +++ b/apps/meteor/app/lib/server/functions/getDefaultChannels.ts @@ -0,0 +1,25 @@ +import type { IRoom } from '@rocket.chat/core-typings'; +import { Rooms } from '@rocket.chat/models'; + +export async function getDefaultChannels(): Promise { + const defaultRooms = await Rooms.findByDefaultAndTypes(true, ['c', 'p'], { + projection: { usernames: 0 }, + }).toArray(); + const roomsThatAreGoingToBeJoined = new Set(defaultRooms.map((room) => room._id)); + + // If any of those are teams, we need to get all the channels that have the auto-join flag as well + const teamRooms = defaultRooms.filter((room) => room.teamMain && room.teamId); + if (teamRooms.length > 0) { + for await (const teamRoom of teamRooms) { + const defaultTeamRooms = await Rooms.findDefaultRoomsForTeam(teamRoom.teamId).toArray(); + + const defaultTeamRoomsThatWereNotAlreadyAdded = defaultTeamRooms.filter((channel) => !roomsThatAreGoingToBeJoined.has(channel._id)); + + defaultTeamRoomsThatWereNotAlreadyAdded.forEach((channel) => roomsThatAreGoingToBeJoined.add(channel._id)); + // Add the channels to the defaultRooms list + defaultRooms.push(...defaultTeamRoomsThatWereNotAlreadyAdded); + } + } + + return defaultRooms; +} diff --git a/apps/meteor/tests/data/rooms.helper.js b/apps/meteor/tests/data/rooms.helper.js index 78e07f110039..c28f763c00f9 100644 --- a/apps/meteor/tests/data/rooms.helper.js +++ b/apps/meteor/tests/data/rooms.helper.js @@ -1,6 +1,6 @@ import { api, credentials, request } from './api-data'; -export const createRoom = ({ name, type, username, token, agentId, members, credentials: customCredentials, voipCallDirection = 'inbound' }) => { +export const createRoom = ({ name, type, username, token, agentId, members, credentials: customCredentials, extraData, voipCallDirection = 'inbound' }) => { if (!type) { throw new Error('"type" is required in "createRoom.ts" test helper'); } @@ -31,6 +31,7 @@ export const createRoom = ({ name, type, username, token, agentId, members, cred .send({ ...params, ...(members && { members }), + ...(extraData && { extraData }), }); }; @@ -65,3 +66,17 @@ function actionRoom({ action, type, roomId }) { export const deleteRoom = ({ type, roomId }) => actionRoom({ action: 'delete', type, roomId }); export const closeRoom = ({ type, roomId }) => actionRoom({ action: 'close', type, roomId }); + +export const setRoomConfig = ({ roomId, favorite, isDefault }) => { + return request + .post(api('rooms.saveRoomSettings')) + .set(credentials) + .send({ + rid: roomId, + default: isDefault, + favorite: favorite ? { + defaultValue: true, + favorite: false + } : undefined + }); +}; diff --git a/apps/meteor/tests/data/uploads.helper.ts b/apps/meteor/tests/data/uploads.helper.ts index 8eb1e7931965..eabd49e11a3b 100644 --- a/apps/meteor/tests/data/uploads.helper.ts +++ b/apps/meteor/tests/data/uploads.helper.ts @@ -45,6 +45,7 @@ export async function testFileUploads(filesEndpoint: 'channels.files' | 'groups. name: null, username: null, members: null, + extraData: null, }); return roomResponse.body.room; 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 1aa30d3322bb..204d1413155f 100644 --- a/apps/meteor/tests/end-to-end/api/01-users.js +++ b/apps/meteor/tests/end-to-end/api/01-users.js @@ -12,7 +12,8 @@ import { imgURL } from '../../data/interactions'; import { createAgent, makeAgentAvailable } from '../../data/livechat/rooms'; import { removeAgent, getAgent } from '../../data/livechat/users'; import { updatePermission, updateSetting } from '../../data/permissions.helper'; -import { createRoom, deleteRoom } from '../../data/rooms.helper'; +import { createRoom, deleteRoom, setRoomConfig } from '../../data/rooms.helper'; +import { createTeam, deleteTeam } from '../../data/teams.helper'; import { adminEmail, preferences, password, adminUsername } from '../../data/user'; import { createUser, login, deleteUser, getUserStatus, getUserByUsername } from '../../data/users.helper.js'; @@ -279,6 +280,162 @@ describe('[Users]', function () { .end(done); }); }); + + describe('auto join default channels', () => { + let defaultTeamRoomId; + let defaultTeamId; + let group; + let user; + let userCredentials; + let user2; + let user3; + let userNoDefault; + const teamName = `defaultTeam_${Date.now()}`; + + before(async () => { + const defaultTeam = await createTeam(credentials, teamName, 0); + defaultTeamRoomId = defaultTeam.roomId; + defaultTeamId = defaultTeam._id; + }); + + before(async () => { + const { body } = await createRoom({ + name: `defaultGroup_${Date.now()}`, + type: 'p', + credentials, + extraData: { + broadcast: false, + encrypted: false, + teamId: defaultTeamId, + topic: '', + }, + }); + group = body.group; + }); + + after(() => + Promise.all([ + deleteRoom({ roomId: group._id, type: 'p' }), + deleteTeam(credentials, teamName), + deleteUser(user), + deleteUser(user2), + deleteUser(user3), + deleteUser(userNoDefault), + ]), + ); + + it('should not create subscriptions to non default teams or rooms even if joinDefaultChannels is true', async () => { + userNoDefault = await createUser({ joinDefaultChannels: true }); + const noDefaultUserCredentials = await login(userNoDefault.username, password); + await request + .get(api('subscriptions.getOne')) + .set(noDefaultUserCredentials) + .query({ roomId: defaultTeamRoomId }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('subscription').that.is.null; + }); + + await request + .get(api('subscriptions.getOne')) + .set(noDefaultUserCredentials) + .query({ roomId: group._id }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('subscription').that.is.null; + }); + }); + + it('should create a subscription for a default team room if joinDefaultChannels is true', async () => { + await setRoomConfig({ roomId: defaultTeamRoomId, favorite: true, isDefault: true }); + + user = await createUser({ joinDefaultChannels: true }); + userCredentials = await login(user.username, password); + await request + .get(api('subscriptions.getOne')) + .set(userCredentials) + .query({ roomId: defaultTeamRoomId }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('subscription'); + expect(res.body.subscription).to.have.property('rid', defaultTeamRoomId); + }); + }); + + it('should NOT create a subscription for non auto-join rooms inside a default team', async () => { + await request + .get(api('subscriptions.getOne')) + .set(userCredentials) + .query({ roomId: group._id }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('subscription').that.is.null; + }); + }); + + it('should create a subscription for the user in all the auto join rooms of the team', async () => { + await request.post(api('teams.updateRoom')).set(credentials).send({ + roomId: group._id, + isDefault: true, + }); + + user2 = await createUser({ joinDefaultChannels: true }); + const user2Credentials = await login(user2.username, password); + + await request + .get(api('subscriptions.getOne')) + .set(user2Credentials) + .query({ roomId: group._id }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('subscription'); + expect(res.body.subscription).to.have.property('rid', group._id); + }); + }); + + it('should create a subscription for a default room inside a non default team', async () => { + await setRoomConfig({ roomId: defaultTeamRoomId, isDefault: false }); + await setRoomConfig({ roomId: group._id, favorite: true, isDefault: true }); + + user3 = await createUser({ joinDefaultChannels: true }); + const user3Credentials = await login(user3.username, password); + + // New user should be subscribed to the default room inside a team + await request + .get(api('subscriptions.getOne')) + .set(user3Credentials) + .query({ roomId: group._id }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('subscription'); + expect(res.body.subscription).to.have.property('rid', group._id); + }); + + // New user should not be subscribed to the parent team + await request + .get(api('subscriptions.getOne')) + .set(user3Credentials) + .query({ roomId: defaultTeamRoomId }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('subscription').that.is.null; + }); + }); + }); }); describe('[/users.register]', () => { diff --git a/apps/meteor/tests/end-to-end/apps/05-video-conferences.ts b/apps/meteor/tests/end-to-end/apps/05-video-conferences.ts index 579e9894ab7b..fb7f42ccdabd 100644 --- a/apps/meteor/tests/end-to-end/apps/05-video-conferences.ts +++ b/apps/meteor/tests/end-to-end/apps/05-video-conferences.ts @@ -25,6 +25,7 @@ describe('Apps - Video Conferences', function () { agentId: undefined, members: undefined, credentials: undefined, + extraData: undefined, }); roomId = res.body.group._id; From 7cb4f07f57f5fd08f19f1a052168c98196722790 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 5 Mar 2024 17:32:08 -0300 Subject: [PATCH 089/207] fix: presence for mobiles with duplicated connections (#31895) --- .changeset/wicked-months-laugh.md | 5 +++ apps/meteor/ee/server/startup/presence.ts | 9 ++++- apps/meteor/tests/end-to-end/api/01-users.js | 35 ++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 .changeset/wicked-months-laugh.md diff --git a/.changeset/wicked-months-laugh.md b/.changeset/wicked-months-laugh.md new file mode 100644 index 000000000000..684b4180a257 --- /dev/null +++ b/.changeset/wicked-months-laugh.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fix users presence stuck as online after connecting using mobile apps diff --git a/apps/meteor/ee/server/startup/presence.ts b/apps/meteor/ee/server/startup/presence.ts index ea3cd789f6f3..3e8daad4b259 100644 --- a/apps/meteor/ee/server/startup/presence.ts +++ b/apps/meteor/ee/server/startup/presence.ts @@ -30,9 +30,16 @@ Meteor.startup(() => { }); Accounts.onLogin((login: any): void => { - if (login.type !== 'resume') { + if (login.type !== 'resume' || !login.connection.id) { return; } + + // validate if it is a real WS connection and is still open + const session = Meteor.server.sessions.get(login.connection.id); + if (!session) { + return; + } + void (async function () { await Presence.newConnection(login.user._id, login.connection.id, nodeId); updateConns(); 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 204d1413155f..1103f8a3cbc5 100644 --- a/apps/meteor/tests/end-to-end/api/01-users.js +++ b/apps/meteor/tests/end-to-end/api/01-users.js @@ -686,6 +686,41 @@ describe('[Users]', function () { }) .end(done); }); + + describe('Logging in with type: "resume"', () => { + let user; + let userCredentials; + + before(async () => { + user = await createUser({ joinDefaultChannels: false }); + userCredentials = await login(user.username, password); + }); + + after(async () => deleteUser(user)); + + it('should return "offline" after a login type "resume" via REST', async () => { + await request + .post(api('login')) + .send({ + resume: userCredentials['X-Auth-Token'], + }) + .expect('Content-Type', 'application/json') + .expect(200); + + await request + .get(api('users.getPresence')) + .set(credentials) + .query({ + userId: user._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('presence', 'offline'); + }); + }); + }); }); describe('[/users.presence]', () => { From 43d1b0f835d34a05386c1dc9b735502ab5af0a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Tue, 5 Mar 2024 19:03:01 -0300 Subject: [PATCH 090/207] chore: replace `PaletteStyleTag` in favor of fuselage component (#31839) --- .../root/MainLayout/LayoutWithSidebar.tsx | 10 +- apps/meteor/package.json | 4 +- ee/packages/pdf-worker/package.json | 2 +- ee/packages/ui-theming/package.json | 2 +- .../ui-theming/src/PaletteStyleTag.tsx | 27 --- .../ui-theming/src/SidebarPaletteStyleTag.tsx | 13 -- ee/packages/ui-theming/src/codeBlockStyles.ts | 74 ------ ee/packages/ui-theming/src/palette.ts | 211 ------------------ ee/packages/ui-theming/src/paletteDark.ts | 211 ------------------ .../ui-theming/src/paletteHighContrast.ts | 211 ------------------ .../.storybook/DocsContainer.tsx | 42 ++++ .../fuselage-ui-kit/.storybook/helpers.ts | 4 + .../fuselage-ui-kit/.storybook/preview.tsx | 44 ++-- packages/fuselage-ui-kit/package.json | 2 +- packages/gazzodown/package.json | 4 +- packages/livechat/package.json | 2 +- packages/ui-avatar/package.json | 2 +- packages/ui-client/package.json | 2 +- packages/ui-composer/package.json | 2 +- packages/ui-video-conf/package.json | 2 +- packages/uikit-playground/package.json | 4 +- yarn.lock | 46 ++-- 22 files changed, 106 insertions(+), 815 deletions(-) delete mode 100644 ee/packages/ui-theming/src/PaletteStyleTag.tsx delete mode 100644 ee/packages/ui-theming/src/SidebarPaletteStyleTag.tsx delete mode 100644 ee/packages/ui-theming/src/codeBlockStyles.ts delete mode 100644 ee/packages/ui-theming/src/palette.ts delete mode 100644 ee/packages/ui-theming/src/paletteDark.ts delete mode 100644 ee/packages/ui-theming/src/paletteHighContrast.ts create mode 100644 packages/fuselage-ui-kit/.storybook/DocsContainer.tsx create mode 100644 packages/fuselage-ui-kit/.storybook/helpers.ts diff --git a/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx b/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx index 1edd6966d065..b12a035d32cd 100644 --- a/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx +++ b/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx @@ -1,7 +1,6 @@ -import { Box } from '@rocket.chat/fuselage'; +import { Box, PaletteStyleTag } from '@rocket.chat/fuselage'; 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 { useThemeMode } from '@rocket.chat/ui-theming/src/hooks/useThemeMode'; import type { ReactElement, ReactNode } from 'react'; import React, { useEffect, useRef } from 'react'; @@ -10,6 +9,7 @@ import AccessibilityShortcut from './AccessibilityShortcut'; const LayoutWithSidebar = ({ children }: { children: ReactNode }): ReactElement => { const { isEmbedded: embeddedLayout } = useLayout(); + const [, , theme] = useThemeMode(); const modal = useCurrentModal(); const currentRoutePath = useCurrentRoutePath(); @@ -48,8 +48,8 @@ const LayoutWithSidebar = ({ children }: { children: ReactNode }): ReactElement aria-hidden={Boolean(modal)} > - - + + {!removeSidenav && }
            { - if (theme === 'dark') { - return darkPalette; - } - if (theme === 'high-contrast') { - return paletteHighContrast; - } - return defaultPalette; - }; - const palette = convertToCss(getPalette(), '.rcx-content--main, .rcx-tile'); - - return <>{createPortal(theme === 'dark' ? palette + codeBlock : palette, useCreateStyleContainer('main-palette'))}; -}); diff --git a/ee/packages/ui-theming/src/SidebarPaletteStyleTag.tsx b/ee/packages/ui-theming/src/SidebarPaletteStyleTag.tsx deleted file mode 100644 index 3b1ab5500960..000000000000 --- a/ee/packages/ui-theming/src/SidebarPaletteStyleTag.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { ReactElement } from 'react'; -import React, { memo } from 'react'; -import { createPortal } from 'react-dom'; - -import { convertToCss } from './helpers/convertToCss'; -import { useCreateStyleContainer } from './hooks/useCreateStyleContainer'; -import { darkPalette } from './paletteDark'; - -export const SidebarPaletteStyleTag = memo(function SidebarPaletteStyleTag(): ReactElement | null { - const palette = convertToCss({ ...darkPalette }, '.rcx-sidebar--main'); - - return <>{createPortal(palette, useCreateStyleContainer('sidebar-palette'))}; -}); diff --git a/ee/packages/ui-theming/src/codeBlockStyles.ts b/ee/packages/ui-theming/src/codeBlockStyles.ts deleted file mode 100644 index 2c6b4bf33be0..000000000000 --- a/ee/packages/ui-theming/src/codeBlockStyles.ts +++ /dev/null @@ -1,74 +0,0 @@ -export const codeBlock = `pre code.hljs { - display: block; - overflow-x: auto; - padding: 1em; -} -code.hljs { - padding: 3px 5px; -} -.hljs { - background: #1d1f21; - color: #c5c8c6; -} -.hljs span::selection, -.hljs::selection { - background: #373b41; -} -.hljs span::-moz-selection, -.hljs::-moz-selection { - background: #373b41; -} -.hljs-name, -.hljs-title { - color: #f0c674; -} -.hljs-comment, -.hljs-meta, -.hljs-meta .hljs-keyword { - color: #707880; -} -.hljs-deletion, -.hljs-link, -.hljs-literal, -.hljs-number, -.hljs-symbol { - color: #c66; -} -.hljs-addition, -.hljs-doctag, -.hljs-regexp, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-string { - color: #b5bd68; -} -.hljs-attribute, -.hljs-code, -.hljs-selector-id { - color: #b294bb; -} -.hljs-bullet, -.hljs-keyword, -.hljs-selector-tag, -.hljs-tag { - color: #81a2be; -} -.hljs-subst, -.hljs-template-tag, -.hljs-template-variable, -.hljs-variable { - color: #8abeb7; -} -.hljs-built_in, -.hljs-quote, -.hljs-section, -.hljs-selector-class, -.hljs-type { - color: #de935f; -} -.hljs-emphasis { - font-style: italic; -} -.hljs-strong { - font-weight: 700; -}`; diff --git a/ee/packages/ui-theming/src/palette.ts b/ee/packages/ui-theming/src/palette.ts deleted file mode 100644 index b2d9e9955dbf..000000000000 --- a/ee/packages/ui-theming/src/palette.ts +++ /dev/null @@ -1,211 +0,0 @@ -export const palette = [ - { - category: 'Stroke', - description: "Use as component's outline, stroke, dividers", - list: [ - { name: 'stroke-extra-light', token: 'N250', color: '#EBECEF' }, - { name: 'stroke-light', token: 'N500', color: '#CBCED1' }, - { name: 'stroke-medium', token: 'N600', color: '#9EA2A8' }, - { name: 'stroke-dark', token: 'N700', color: '#6C727A' }, - { name: 'stroke-extra-dark', token: 'N800', color: '#2F343D' }, - { name: 'stroke-extra-light-highlight', token: 'P200', color: '#D1EBFE' }, - { name: 'stroke-highlight', token: 'P500', color: '#156FF5' }, - { name: 'stroke-extra-light-error', token: 'D200', color: '#FFC1C9' }, - { name: 'stroke-error', token: 'D500', color: '#EC0D2A' }, - ], - }, - { - category: 'Surface', - description: 'Use as a container on top of the background', - list: [ - { name: 'surface-light', token: 'white', color: '#FFFFFF' }, - { name: 'surface-tint', token: 'N100', color: '#F7F8FA' }, - { name: 'surface-room', token: 'white', color: '#FFFFFF' }, - { name: 'surface-neutral', token: 'N400', color: '#E4E7EA' }, - { name: 'surface-disabled', token: 'N100', color: '#F7F8FA' }, - { name: 'surface-hover', token: 'N200', color: '#F2F3F5' }, - { name: 'surface-selected', token: '', color: '#D7DBE0' }, - { name: 'surface-dark', token: 'N900', color: '#1F2329' }, - { name: 'surface-featured', token: '', color: '#5F1477' }, - { name: 'surface-featured-hover', token: '', color: '#4A105D' }, - { name: 'surface-overlay', token: '', color: 'rgba(47, 52, 61, 0.5)' }, - { name: 'surface-sidebar', token: 'N400', color: '#E4E7EA' }, - ], - }, - { - category: 'Shadow', - description: 'Use as a shadow color', - list: [ - { name: 'shadow-highlight', token: '', color: '#D1EBFE' }, - { name: 'shadow-danger', token: '', color: '#FFE9EC' }, - ], - }, - { - category: 'Font', - description: 'These should be applied according to surfaces', - list: [ - { name: 'font-white', token: 'white', color: '#FFFFFF' }, - { name: 'font-disabled', token: 'N500', color: '#CBCED1' }, - { name: 'font-annotation', token: 'N600', color: '#9EA2A8' }, - { name: 'font-hint', token: 'N700', color: '#6C737A' }, - { name: 'font-secondary-info', token: 'N700', color: '#6C737A' }, - { name: 'font-default', token: 'N800', color: '#2F343D' }, - { name: 'font-titles-labels', token: 'N900', color: '#1F2329' }, - { name: 'font-info', token: 'P600', color: '#095AD2' }, - { name: 'font-danger', token: 'D600', color: '#D40C26' }, - { name: 'font-pure-black', token: '', color: '#2F343D' }, - { name: 'font-pure-white', token: '', color: '#FFFFFF' }, - ], - }, - { - category: 'Status', - description: 'Status Background', - list: [ - { name: 'status-background-info', token: 'P200', color: '#D1EBFE' }, - { name: 'status-background-success', token: 'S500', color: '#C0F6E4' }, - { name: 'status-background-danger', token: 'D200', color: '#FFC1C9' }, - { name: 'status-background-warning', token: 'W200', color: '#FFECAD' }, - { name: 'status-background-warning-2', token: 'W100', color: '#FFF8E0' }, - { name: 'status-background-service-1', token: 'S1-200', color: '#FAD1B0' }, - { name: 'status-background-service-2', token: 'S2-200', color: '#EDD0F7' }, - { name: 'status-background-service-3', token: 'S2-700', color: '#5F1477' }, - ], - }, - { - description: 'Status Font', - list: [ - { name: 'status-font-on-info', token: 'P600', color: '#095AD2' }, - { name: 'status-font-on-success', token: 'S800', color: '#148660' }, - { name: 'status-font-on-danger', token: 'D800', color: '#9B1325' }, - { name: 'status-font-on-warning', token: 'W900', color: '#8E6300' }, - { name: 'status-font-on-warning-2', token: 'N800', color: '#2F343D' }, - { name: 'status-font-on-service-1', token: 'S1-800', color: '#974809' }, - { name: 'status-font-on-service-2 ', token: 'S2-600', color: '#7F1B9F' }, - { name: 'status-font-on-service-3 ', token: 'white', color: '#FFFFFF' }, - ], - }, - { - category: 'Badge', - description: 'Badge Background', - list: [ - { name: 'badge-background-level-0', token: '', color: '#E4E7EA' }, - { name: 'badge-background-level-1', token: 'N700', color: '#6C737A' }, - { name: 'badge-background-level-2', token: '', color: '#156FF5' }, - { name: 'badge-background-level-3', token: '', color: '#F38C39' }, - { name: 'badge-background-level-4', token: '', color: '#EC0D2A' }, - ], - }, - { - category: 'Status Bullet', - description: 'Used to show user status', - list: [ - { name: 'status-bullet-online', token: '', color: '#148660' }, - { name: 'status-bullet-away', token: '', color: '#AC892F' }, - { name: 'status-bullet-busy', token: '', color: '#D40C26' }, - { name: 'status-bullet-disabled', token: '', color: '#F38C39' }, - { name: 'status-bullet-offline', token: '', color: '#6C737A' }, - { name: 'status-bullet-loading', token: '', color: '#6C737A' }, - ], - }, - { - category: 'Elevation', - description: 'Elevation border and shadow levels', - list: [ - { name: 'shadow-elevation-border', token: '', color: '#EBECEF' }, - { name: 'shadow-elevation-1', token: '', color: 'rgba(47, 52, 61, 0.1)' }, - { name: 'shadow-elevation-2x', token: '', color: 'rgba(47, 52, 61, 0.08)' }, - { name: 'shadow-elevation-2y', token: '', color: 'rgba(47, 52, 61, 0.12)' }, - ], - }, - { - category: 'Button', - description: 'Primary Background', - list: [ - { name: 'button-background-primary-default', token: 'P500', color: '#156FF5' }, - { name: 'button-background-primary-hover', token: 'P600', color: '#095AD2' }, - { name: 'button-background-primary-press', token: 'P700', color: '#10529E' }, - { name: 'button-background-primary-focus', token: 'P500', color: '#156FF5' }, - { name: 'button-background-primary-keyfocus', token: 'P500', color: '#156FF5' }, - { name: 'button-background-primary-disabled', token: 'P200', color: '#D1EBFE' }, - ], - }, - { - description: 'Secondary Background', - list: [ - { name: 'button-background-secondary-default', token: 'N400', color: '#E4E7EA' }, - { name: 'button-background-secondary-hover', token: 'N500', color: '#CBCED1' }, - { name: 'button-background-secondary-press', token: 'N600', color: '#9EA2A8' }, - { name: 'button-background-secondary-focus', token: 'N400', color: '#E4E7EA' }, - { name: 'button-background-secondary-keyfocus', token: 'N400', color: '#E4E7EA' }, - { name: 'button-background-secondary-disabled', token: 'N300', color: '#EEEFF1' }, - ], - }, - { - description: 'Secondary Danger Background', - list: [ - { name: 'button-background-secondary-danger-default', token: 'N400', color: '#E4E7EA' }, - { name: 'button-background-secondary-danger-hover', token: 'N500', color: '#CBCED1' }, - { name: 'button-background-secondary-danger-press', token: 'N600', color: '#9EA2A8' }, - { name: 'button-background-secondary-danger-focus', token: 'N400', color: '#E4E7EA' }, - { name: 'button-background-secondary-danger-keyfocus', token: 'N400', color: '#E4E7EA' }, - { name: 'button-background-secondary-danger-disabled', token: 'N300', color: '#EEEFF1' }, - ], - }, - { - description: 'Danger Background', - list: [ - { name: 'button-background-danger-default', token: 'D500', color: '#EC0D2A' }, - { name: 'button-background-danger-hover', token: 'D600', color: '#D40C26' }, - { name: 'button-background-danger-press', token: 'D700', color: '#BB0B21' }, - { name: 'button-background-danger-focus', token: 'D500', color: '#EC0D2A' }, - { name: 'button-background-danger-keyfocus', token: 'D500', color: '#EC0D2A' }, - { name: 'button-background-danger-disabled', token: 'D200', color: '#FFC1C9' }, - ], - }, - { - description: 'Success Background', - list: [ - { name: 'button-background-success-default', token: '', color: '#148660' }, - { name: 'button-background-success-hover', token: 'S900', color: '#106D4F' }, - { name: 'button-background-success-press', token: 'S1000', color: '#0D5940' }, - { name: 'button-background-success-focus', token: '', color: '#148660' }, - { name: 'button-background-success-keyfocus', token: '', color: '#148660' }, - { name: 'button-background-success-disabled', token: 'S200', color: '#C0F6E4' }, - ], - }, - { - description: 'Font', - list: [ - { name: 'button-font-on-primary', token: 'white', color: '#FFFFFF' }, - { name: 'button-font-on-primary-disabled', token: 'white', color: '#FFFFFF' }, - { name: 'button-font-on-secondary', token: 'N900', color: '#1F2329' }, - { name: 'button-font-on-secondary-disabled', token: 'N600', color: '#CBCED1' }, - { name: 'button-font-on-secondary-danger', token: '', color: '#BB0B21' }, - { - name: 'button-font-on-secondary-danger-disabled', - token: 'D300', - color: '#F98F9D', - }, - { name: 'button-font-on-danger', token: 'white', color: '#FFFFFF' }, - { name: 'button-font-on-danger-disabled', token: 'white', color: '#FFFFFF' }, - { name: 'button-font-on-success', token: '', color: '#FFFFFF' }, - { name: 'button-font-on-success-disabled', token: 'white', color: '#FFFFFF' }, - ], - }, -]; - -export const defaultPalette = { - ...palette.reduce( - (rec, group) => ({ - ...rec, - ...group.list.reduce( - (rec, item) => ({ - ...rec, - [item.name]: item.color, - }), - {} as Record, - ), - }), - {} as Record, - ), -}; diff --git a/ee/packages/ui-theming/src/paletteDark.ts b/ee/packages/ui-theming/src/paletteDark.ts deleted file mode 100644 index e36133650a6c..000000000000 --- a/ee/packages/ui-theming/src/paletteDark.ts +++ /dev/null @@ -1,211 +0,0 @@ -export const palette = [ - { - category: 'Stroke', - description: "Use as component's outline, stroke, dividers", - list: [ - { name: 'stroke-extra-light', token: '', color: '#333842' }, - { name: 'stroke-light', token: '', color: '#404754' }, - { name: 'stroke-medium', token: '', color: '#4B5362' }, - { name: 'stroke-dark', token: 'N600', color: '#9EA2A8' }, - { name: 'stroke-extra-dark', token: 'N400', color: '#CBCED1' }, - { name: 'stroke-extra-light-highlight', token: '', color: '#87CBFC' }, - { name: 'stroke-highlight', token: '', color: '#6292DA' }, - { name: 'stroke-extra-light-error', token: '', color: '#F49AA6' }, - { name: 'stroke-error', token: '', color: '#BB3E4E' }, - ], - }, - { - category: 'Surface', - description: 'Use as a container on top of the background', - list: [ - { name: 'surface-light', token: 'N900', color: '#262931' }, - { name: 'surface-tint', token: '', color: '#1F2329' }, - { name: 'surface-room', token: '', color: '#1F2329' }, - { name: 'surface-neutral', token: '', color: '#2D3039' }, - { name: 'surface-disabled', token: 'N800', color: '#24272E' }, - { name: 'surface-hover', token: '', color: '#1A1E23' }, - { name: 'surface-selected', token: '', color: '#4C5362' }, - { name: 'surface-dark', token: 'N400', color: '#E4E7EA' }, - { name: 'surface-featured', token: '', color: '#5F1477' }, - { name: 'surface-featured-hover', token: '', color: '#4A105D' }, - { name: 'surface-overlay', token: '', color: 'rgba(0, 0, 0, 0.6)' }, - { name: 'surface-sidebar', token: '', color: '#2F343D' }, - ], - }, - { - category: 'Shadow', - description: 'Use as a shadow color', - list: [ - { name: 'shadow-highlight', token: '', color: '#D1EBFE' }, - { name: 'shadow-danger', token: '', color: '#FFE9EC' }, - ], - }, - { - category: 'Font', - description: 'These should be applied according to surfaces', - list: [ - { name: 'font-white', token: 'N800', color: '#2F343D' }, - { name: 'font-disabled', token: '', color: '#60646C' }, - { name: 'font-annotation', token: 'N600', color: '#9EA2A8' }, - { name: 'font-hint', token: 'N600', color: '#9EA2A8' }, - { name: 'font-secondary-info', token: '', color: '#9EA2A8' }, - { name: 'font-default', token: 'N400', color: '#C1C7D0' }, - { name: 'font-titles-labels', token: '', color: '#F2F3F5' }, - { name: 'font-info', token: '', color: '#739EDE' }, - { name: 'font-danger', token: '', color: '#D88892' }, - { name: 'font-pure-black', token: '', color: '#2F343D' }, - { name: 'font-pure-white', token: '', color: '#FFFFFF' }, - ], - }, - { - category: 'Status', - description: 'Status Background', - list: [ - { name: 'status-background-info', token: '', color: '#A8C3EB' }, - { name: 'status-background-success', token: '', color: '#C1EBDD' }, - { name: 'status-background-warning', token: '', color: '#FEEFBE' }, - { name: 'status-background-warning-2', token: '', color: '#3C3625' }, - { name: 'status-background-danger', token: '', color: '#FFBDC5' }, - { name: 'status-background-service-1', token: '', color: '#FCE3CF' }, - { name: 'status-background-service-2', token: '', color: '#EDD0F7' }, - { name: 'status-background-service-3', token: '', color: '#5F1477' }, - ], - }, - { - description: 'Status Font', - list: [ - { name: 'status-font-on-info', token: '', color: '#739EDE' }, - { name: 'status-font-on-success', token: '', color: '#58AD90' }, - { name: 'status-font-on-warning', token: '', color: '#C7AA66' }, - { name: 'status-font-on-warning-2', token: '', color: '#FFFFFF' }, - { name: 'status-font-on-danger', token: '', color: '#D88892' }, - { name: 'status-font-on-service-1', token: '', color: '#CA9163' }, - { name: 'status-font-on-service-2 ', token: '', color: '#C393D2' }, - { name: 'status-font-on-service-3 ', token: '', color: '#FFFFFF' }, - ], - }, - { - category: 'Badge', - description: 'Badge Background', - list: [ - { name: 'badge-background-level-0', token: '', color: '#404754' }, - { name: 'badge-background-level-1', token: '', color: '#484C51' }, - { name: 'badge-background-level-2', token: '', color: '#2C65BA' }, - { name: 'badge-background-level-3', token: '', color: '#A9642D' }, - { name: 'badge-background-level-4', token: '', color: '#BB3E4E' }, - ], - }, - { - category: 'Status Bullet', - description: 'Used to show user status', - list: [ - { name: 'status-bullet-online', token: '', color: '#1CBF89' }, - { name: 'status-bullet-away', token: '', color: '#B08C30' }, - { name: 'status-bullet-busy', token: '', color: '#C75765' }, - { name: 'status-bullet-disabled', token: '', color: '#CC7F42' }, - { name: 'status-bullet-offline', token: '', color: '#8B9098' }, - { name: 'status-bullet-loading', token: '', color: '#8B9098' }, - ], - }, - { - category: 'Elevation', - description: 'Elevation border and shadow levels', - list: [ - { 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)' }, - ], - }, - { - category: 'Button', - description: 'Primary Background', - list: [ - { name: 'button-background-primary-default', token: '', color: '#095AD2' }, - { name: 'button-background-primary-hover', token: '', color: '#10529E' }, - { name: 'button-background-primary-press', token: '', color: '#01336B' }, - { name: 'button-background-primary-focus', token: '', color: '#095AD2' }, - { name: 'button-background-primary-keyfocus', token: '', color: '#095AD2' }, - { name: 'button-background-primary-disabled', token: '', color: '#012247' }, - ], - }, - { - description: 'Secondary Background', - list: [ - { 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: '#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' }, - ], - }, - { - description: 'Danger Background', - list: [ - { name: 'button-background-danger-default', token: '', color: '#BB3E4E' }, - { name: 'button-background-danger-hover', token: '', color: '#95323F' }, - { name: 'button-background-danger-press', token: '', color: '#822C37' }, - { name: 'button-background-danger-focus', token: '', color: '#BB3E4E' }, - { name: 'button-background-danger-keyfocus', token: '', color: '#BB3E4E' }, - { name: 'button-background-danger-disabled', token: '', color: '#3D2126' }, - ], - }, - { - description: 'Success Background', - list: [ - { name: 'button-background-success-default', token: '', color: '#1D7256' }, - { name: 'button-background-success-hover', token: '', color: '#175943' }, - { name: 'button-background-success-press', token: '', color: '#134937' }, - { name: 'button-background-success-focus', token: '', color: '#1D7256' }, - { name: 'button-background-success-keyfocus', token: '', color: '#1D7256' }, - { name: 'button-background-success-disabled', token: '', color: '#1E4B40' }, - ], - }, - { - description: 'Font', - list: [ - { name: 'button-font-on-primary', token: 'white', color: '#FFFFFF' }, - { name: 'button-font-on-secondary', token: 'N400', color: '#E4E7EA' }, - { name: 'button-font-on-secondary-danger', token: '', color: '#FFC1C9' }, - { name: 'button-font-on-danger', token: 'white', color: '#FFFFFF' }, - { name: 'button-font-on-success', token: 'white', color: '#FFFFFF' }, - { name: 'button-font-on-primary-disabled', token: 'N700', color: '#6C727A' }, - { name: 'button-font-on-secondary-disabled', token: 'N700', color: '#6C727A' }, - { - name: 'button-font-on-secondary-danger-disabled', - token: '', - color: '#6B0513', - }, - { name: 'button-font-on-danger-disabled', token: '', color: '#757575' }, - { name: 'button-font-on-success-disabled', token: '', color: '#757575' }, - ], - }, -]; - -export const darkPalette = { - ...palette.reduce( - (rec, group) => ({ - ...rec, - ...group.list.reduce( - (rec, item) => ({ - ...rec, - [item.name]: item.color, - }), - {} as Record, - ), - }), - {} as Record, - ), -}; diff --git a/ee/packages/ui-theming/src/paletteHighContrast.ts b/ee/packages/ui-theming/src/paletteHighContrast.ts deleted file mode 100644 index 1ec7bcf793de..000000000000 --- a/ee/packages/ui-theming/src/paletteHighContrast.ts +++ /dev/null @@ -1,211 +0,0 @@ -export const palette = [ - { - category: 'Stroke', - description: "Use as component's outline, stroke, dividers", - list: [ - { name: 'stroke-extra-light', token: 'N250', color: '#EBECEF' }, - { name: 'stroke-light', token: 'N500', color: '#CBCED1' }, - { name: 'stroke-medium', token: 'N600', color: '#9EA2A8' }, - { name: 'stroke-dark', token: 'N700', color: '#6C727A' }, - { name: 'stroke-extra-dark', token: 'N800', color: '#2F343D' }, - { name: 'stroke-extra-light-highlight', token: 'P200', color: '#D1EBFE' }, - { name: 'stroke-highlight', token: 'P500', color: '#156FF5' }, - { name: 'stroke-extra-light-error', token: 'D200', color: '#FFC1C9' }, - { name: 'stroke-error', token: 'D500', color: '#EC0D2A' }, - ], - }, - { - category: 'Surface', - description: 'Use as a container on top of the background', - list: [ - { name: 'surface-light', token: 'white', color: '#FFFFFF' }, - { name: 'surface-tint', token: 'N100', color: '#F7F8FA' }, - { name: 'surface-room', token: 'white', color: '#FFFFFF' }, - { name: 'surface-neutral', token: 'N400', color: '#E4E7EA' }, - { name: 'surface-disabled', token: 'N100', color: '#F7F8FA' }, - { name: 'surface-hover', token: 'N200', color: '#F2F3F5' }, - { name: 'surface-selected', token: 'N400', color: '#D7DBE0' }, - { name: 'surface-dark', token: 'N900', color: '#1F2329' }, - { name: 'surface-featured', token: '', color: '#5F1477' }, - { name: 'surface-featured-hover', token: '', color: '#4A105D' }, - { name: 'surface-overlay', token: '', color: 'rgba(47, 52, 61, 0.5)' }, - { name: 'surface-sidebar', token: 'N400', color: '#E4E7EA' }, - ], - }, - { - category: 'Shadow', - description: 'Use as a shadow color', - list: [ - { name: 'shadow-highlight', token: '', color: '#D1EBFE' }, - { name: 'shadow-danger', token: '', color: '#FFE9EC' }, - ], - }, - { - category: 'Font', - description: 'These should be applied according to surfaces', - list: [ - { name: 'font-white', token: 'white', color: '#FFFFFF' }, - { name: 'font-disabled', token: '', color: '#CBCED1' }, - { name: 'font-annotation', token: '', color: '#1F2329' }, - { name: 'font-hint', token: '', color: '#1F2329' }, - { name: 'font-secondary-info', token: '', color: '#1F2329' }, - { name: 'font-default', token: '', color: '#1F2329' }, - { name: 'font-titles-labels', token: '', color: '#1F2329' }, - { name: 'font-info', token: '', color: '#01336B' }, - { name: 'font-danger', token: '', color: '#9B1325' }, - { name: 'font-pure-black', token: '', color: '#1F2329' }, - { name: 'font-pure-white', token: '', color: '#FFFFFF' }, - ], - }, - { - category: 'Status', - description: 'Status Background', - list: [ - { name: 'status-background-info', token: 'P200', color: '#D1EBFE' }, - { name: 'status-background-success', token: 'S500', color: '#C0F6E4' }, - { name: 'status-background-danger', token: 'D200', color: '#FFC1C9' }, - { name: 'status-background-warning', token: 'W200', color: '#FFECAD' }, - { name: 'status-background-warning-2', token: 'W100', color: '#FFF8E0' }, - { name: 'status-background-service-1', token: 'S1-200', color: '#FAD1B0' }, - { name: 'status-background-service-2', token: 'S2-200', color: '#EDD0F7' }, - { name: 'status-background-service-3', token: 'S2-700', color: '#5F1477' }, - ], - }, - { - description: 'Status Font', - list: [ - { name: 'status-font-on-info', token: '', color: '#10529E' }, - { name: 'status-font-on-success', token: '', color: '#0D5940' }, - { name: 'status-font-on-danger', token: 'D800', color: '#6B0513' }, - { name: 'status-font-on-warning', token: 'W900', color: '#573D00' }, - { name: 'status-font-on-warning-2', token: 'N800', color: '#2F343D' }, - { name: 'status-font-on-service-1', token: 'S1-800', color: '#5B2C06' }, - { name: 'status-font-on-service-2 ', token: 'S2-600', color: '#5F1477' }, - { name: 'status-font-on-service-3 ', token: 'white', color: '#FFFFFF' }, - ], - }, - { - category: 'Badge', - description: 'Badge Background', - list: [ - { name: 'badge-background-level-0', token: '', color: '#E4E7EA' }, - { name: 'badge-background-level-1', token: '', color: '#2F343D' }, - { name: 'badge-background-level-2', token: '', color: '#10529E' }, - { name: 'badge-background-level-3', token: '', color: '#713607' }, - { name: 'badge-background-level-4', token: '', color: '#9B1325' }, - ], - }, - { - category: 'Status Bullet', - description: 'Used to show user status', - list: [ - { name: 'status-bullet-online', token: '', color: '#0D5940' }, - { name: 'status-bullet-away', token: '', color: '#573D00' }, - { name: 'status-bullet-busy', token: '', color: '#9B1325' }, - { name: 'status-bullet-disabled', token: '', color: '#BD5A0B' }, - { name: 'status-bullet-offline', token: '', color: '#1F2329' }, - { name: 'status-bullet-loading', token: '', color: '#1F2329' }, - ], - }, - { - category: 'Elevation', - description: 'Elevation border and shadow levels', - list: [ - { name: 'shadow-elevation-border', token: '', color: '#EBECEF' }, - { name: 'shadow-elevation-1', token: '', color: 'rgba(47, 52, 61, 0.1)' }, - { name: 'shadow-elevation-2x', token: '', color: 'rgba(47, 52, 61, 0.08)' }, - { name: 'shadow-elevation-2y', token: '', color: 'rgba(47, 52, 61, 0.12)' }, - ], - }, - { - category: 'Button', - description: 'Primary Background', - list: [ - { name: 'button-background-primary-default', token: '', color: '#10529E' }, - { name: 'button-background-primary-hover', token: '', color: '#01336B' }, - { name: 'button-background-primary-press', token: '', color: '#012247' }, - { name: 'button-background-primary-focus', token: '', color: '#10529E' }, - { name: 'button-background-primary-keyfocus', token: '', color: '#10529E' }, - { name: 'button-background-primary-disabled', token: '', color: '#D1EBFE' }, - ], - }, - { - description: 'Secondary Background', - list: [ - { name: 'button-background-secondary-default', token: 'N400', color: '#E4E7EA' }, - { name: 'button-background-secondary-hover', token: 'N500', color: '#CBCED1' }, - { name: 'button-background-secondary-press', token: '', color: '#9EA2A8' }, - { name: 'button-background-secondary-focus', token: 'N400', color: '#E4E7EA' }, - { name: 'button-background-secondary-keyfocus', token: 'N400', color: '#E4E7EA' }, - { name: 'button-background-secondary-disabled', token: 'N300', color: '#EEEFF1' }, - ], - }, - { - description: 'Secondary Danger Background', - list: [ - { name: 'button-background-secondary-danger-default', token: '', color: '#E4E7EA' }, - { name: 'button-background-secondary-danger-hover', token: '', color: '#CBCED1' }, - { name: 'button-background-secondary-danger-press', token: '', color: '#9EA2A8' }, - { name: 'button-background-secondary-danger-focus', token: 'N400', color: '#E4E7EA' }, - { name: 'button-background-secondary-danger-keyfocus', token: 'N400', color: '#E4E7EA' }, - { name: 'button-background-secondary-danger-disabled', token: '', color: '#EEEFF1' }, - ], - }, - { - description: 'Danger Background', - list: [ - { name: 'button-background-danger-default', token: '', color: '#9B1325' }, - { name: 'button-background-danger-hover', token: '', color: '#8B0719' }, - { name: 'button-background-danger-press', token: '', color: '#8B0719' }, - { name: 'button-background-danger-focus', token: '', color: '#9B1325' }, - { name: 'button-background-danger-keyfocus', token: '', color: '#9B1325' }, - { name: 'button-background-danger-disabled', token: 'D200', color: '#FFC1C9' }, - ], - }, - { - description: 'Success Background', - list: [ - { name: 'button-background-success-default', token: '', color: '#148660' }, - { name: 'button-background-success-hover', token: 'S900', color: '#106D4F' }, - { name: 'button-background-success-press', token: 'S1000', color: '#0D5940' }, - { name: 'button-background-success-focus', token: '', color: '#148660' }, - { name: 'button-background-success-keyfocus', token: '', color: '#148660' }, - { name: 'button-background-success-disabled', token: 'S200', color: '#C0F6E4' }, - ], - }, - { - description: 'Font', - list: [ - { name: 'button-font-on-primary', token: 'white', color: '#FFFFFF' }, - { name: 'button-font-on-primary-disabled', token: '', color: '#FFFFFF' }, - { name: 'button-font-on-secondary', token: '', color: '#1F2329' }, - { name: 'button-font-on-secondary-disabled', token: '', color: '#CBCED1' }, - { name: 'button-font-on-danger', token: 'white', color: '#FFFFFF' }, - { name: 'button-font-on-danger-disabled', token: '', color: '#FFFFFF' }, - { name: 'button-font-on-secondary-danger', token: '', color: '#8B0719' }, - { - name: 'button-font-on-secondary-danger-disabled', - token: '', - color: '#F98F9D', - }, - { name: 'button-font-on-success', token: '', color: '#FFFFFF' }, - { name: 'button-font-on-success-disabled', token: 'white', color: '#FFFFFF' }, - ], - }, -]; - -export const paletteHighContrast = { - ...palette.reduce( - (rec, group) => ({ - ...rec, - ...group.list.reduce( - (rec, item) => ({ - ...rec, - [item.name]: item.color, - }), - {} as Record, - ), - }), - {} as Record, - ), -}; diff --git a/packages/fuselage-ui-kit/.storybook/DocsContainer.tsx b/packages/fuselage-ui-kit/.storybook/DocsContainer.tsx new file mode 100644 index 000000000000..39f4c0cccb0b --- /dev/null +++ b/packages/fuselage-ui-kit/.storybook/DocsContainer.tsx @@ -0,0 +1,42 @@ +import { DocsContainer as BaseContainer } from '@storybook/addon-docs/blocks'; +import { themes } from '@storybook/theming'; +import type { ComponentProps } from 'react'; +import { useDarkMode } from 'storybook-dark-mode'; + +import { surface } from './helpers'; + +export const DocsContainer = ({ + children, + context, +}: ComponentProps) => { + const dark = useDarkMode(); + + return ( + { + const storyContext = context.storyById(id); + return { + ...storyContext, + parameters: { + ...storyContext?.parameters, + docs: { + ...storyContext?.parameters?.docs, + theme: dark + ? { + ...themes.dark, + appContentBg: surface.main, + barBg: surface.main, + } + : themes.light, + }, + }, + }; + }, + }} + > + {children} + + ); +}; diff --git a/packages/fuselage-ui-kit/.storybook/helpers.ts b/packages/fuselage-ui-kit/.storybook/helpers.ts new file mode 100644 index 000000000000..aee5f8ad0969 --- /dev/null +++ b/packages/fuselage-ui-kit/.storybook/helpers.ts @@ -0,0 +1,4 @@ +export const surface = { + sidebar: '#2F343D', + main: '#262931', +}; diff --git a/packages/fuselage-ui-kit/.storybook/preview.tsx b/packages/fuselage-ui-kit/.storybook/preview.tsx index 82ba8a060ef6..9982de43f6bb 100644 --- a/packages/fuselage-ui-kit/.storybook/preview.tsx +++ b/packages/fuselage-ui-kit/.storybook/preview.tsx @@ -1,17 +1,12 @@ -import { codeBlock } from '@rocket.chat/ui-theming/src/codeBlockStyles'; -import { convertToCss } from '@rocket.chat/ui-theming/src/helpers/convertToCss'; -import { filterOnlyChangedColors } from '@rocket.chat/ui-theming/src/helpers/filterOnlyChangedColors'; -import { useCreateStyleContainer } from '@rocket.chat/ui-theming/src/hooks/useCreateStyleContainer'; -import { defaultPalette } from '@rocket.chat/ui-theming/src/palette'; -import { darkPalette } from '@rocket.chat/ui-theming/src/paletteDark'; +import { PaletteStyleTag } from '@rocket.chat/fuselage'; import { type Parameters } from '@storybook/addons'; import { type DecoratorFn } from '@storybook/react'; import { themes } from '@storybook/theming'; -import { createElement } from 'react'; -import { createPortal } from 'react-dom'; import { useDarkMode } from 'storybook-dark-mode'; import manifest from '../package.json'; +import { DocsContainer } from './DocsContainer'; +import { surface } from './helpers'; import logo from './logo.svg'; import '@rocket.chat/fuselage/dist/fuselage.css'; @@ -32,9 +27,15 @@ export const parameters: Parameters = { storySort: ([, a], [, b]) => a.kind.localeCompare(b.kind), }, layout: 'fullscreen', + docs: { + container: DocsContainer, + }, darkMode: { dark: { ...themes.dark, + appBg: surface.sidebar, + appContentBg: surface.main, + barBg: surface.main, brandTitle: manifest.name, brandImage: logo, brandUrl: manifest.homepage, @@ -49,23 +50,14 @@ export const parameters: Parameters = { }; export const decorators: DecoratorFn[] = [ - (fn) => - createElement(function RocketChatDarkMode() { - const dark = useDarkMode(); - - const palette = convertToCss( - filterOnlyChangedColors(defaultPalette, dark ? darkPalette : {}), - 'body' - ); + (fn) => { + const dark = useDarkMode(); - return ( - <> - {createPortal( - dark ? palette + codeBlock : palette, - useCreateStyleContainer('main-palette') - )} - {fn()} - - ); - }), + return ( + <> + + {fn()} + + ); + }, ]; diff --git a/packages/fuselage-ui-kit/package.json b/packages/fuselage-ui-kit/package.json index 068e559f101a..a9a9e7a7fd99 100644 --- a/packages/fuselage-ui-kit/package.json +++ b/packages/fuselage-ui-kit/package.json @@ -63,7 +63,7 @@ "@babel/preset-typescript": "~7.22.15", "@rocket.chat/apps-engine": "1.41.0", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.48.0", + "@rocket.chat/fuselage": "^0.49.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/icons": "^0.33.0", diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index b95a374fe96c..78226d19012b 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -6,8 +6,8 @@ "@babel/core": "~7.22.20", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.48.0", - "@rocket.chat/fuselage-tokens": "~0.32.0", + "@rocket.chat/fuselage": "^0.49.0", + "@rocket.chat/fuselage-tokens": "^0.33.0", "@rocket.chat/message-parser": "workspace:^", "@rocket.chat/styled": "~0.31.25", "@rocket.chat/ui-client": "workspace:^", diff --git a/packages/livechat/package.json b/packages/livechat/package.json index c5bbb001cd1d..9ba1be9afb56 100644 --- a/packages/livechat/package.json +++ b/packages/livechat/package.json @@ -31,7 +31,7 @@ "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/ddp-client": "workspace:^", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage-tokens": "~0.32.0", + "@rocket.chat/fuselage-tokens": "^0.33.0", "@rocket.chat/logo": "^0.31.29", "@storybook/addon-essentials": "~6.5.16", "@storybook/addon-postcss": "~2.0.0", diff --git a/packages/ui-avatar/package.json b/packages/ui-avatar/package.json index 7bf05ddaf253..3d4468366c72 100644 --- a/packages/ui-avatar/package.json +++ b/packages/ui-avatar/package.json @@ -4,7 +4,7 @@ "private": true, "devDependencies": { "@babel/core": "~7.22.20", - "@rocket.chat/fuselage": "^0.48.0", + "@rocket.chat/fuselage": "^0.49.0", "@rocket.chat/ui-contexts": "workspace:^", "@types/babel__core": "~7.20.3", "@types/react": "~17.0.69", diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index 29518b2febe9..d50cb7738f40 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.48.0", + "@rocket.chat/fuselage": "^0.49.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/mock-providers": "workspace:^", diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json index bc63d18be2b3..65035ce9a77c 100644 --- a/packages/ui-composer/package.json +++ b/packages/ui-composer/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.48.0", + "@rocket.chat/fuselage": "^0.49.0", "@rocket.chat/icons": "^0.33.0", "@storybook/addon-actions": "~6.5.16", "@storybook/addon-docs": "~6.5.16", diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index 2bdc366985b0..2079018cb029 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.48.0", + "@rocket.chat/fuselage": "^0.49.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/styled": "~0.31.25", diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index 4bad525b77a9..26b65d9bcbda 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -15,11 +15,11 @@ "@codemirror/tooltip": "^0.19.16", "@lezer/highlight": "^1.1.6", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.48.0", + "@rocket.chat/fuselage": "^0.49.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/fuselage-toastbar": "^0.31.25", - "@rocket.chat/fuselage-tokens": "~0.32.0", + "@rocket.chat/fuselage-tokens": "^0.33.0", "@rocket.chat/fuselage-ui-kit": "workspace:~", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/logo": "^0.31.29", diff --git a/yarn.lock b/yarn.lock index 1a88f52feaff..72c94c3be6d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8715,10 +8715,10 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/fuselage-tokens@npm:^0.32.0, @rocket.chat/fuselage-tokens@npm:~0.32.0": - version: 0.32.0 - resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0" - checksum: 8da7836877ba93462f90d13de6d3d3add8b2758b58c7988e14a8f0deffd1ceef0547f26d4c60a7ddc881e21e3327b5a04cbf17336e5ca8ab9c19789d8e6af3c0 +"@rocket.chat/fuselage-tokens@npm:^0.33.0": + version: 0.33.0 + resolution: "@rocket.chat/fuselage-tokens@npm:0.33.0" + checksum: fee164884da145fdc1e121f4c96c46d32106cd04ed5d1901634552cc3a0d512f9e41cf7905bac0dc9d4212a3d984db4ff23c3227005ba347782a72851cbc7277 languageName: node linkType: hard @@ -8732,7 +8732,7 @@ __metadata: "@babel/preset-typescript": ~7.22.15 "@rocket.chat/apps-engine": 1.41.0 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.48.0 + "@rocket.chat/fuselage": ^0.49.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/gazzodown": "workspace:^" @@ -8787,13 +8787,13 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/fuselage@npm:^0.48.0": - version: 0.48.0 - resolution: "@rocket.chat/fuselage@npm:0.48.0" +"@rocket.chat/fuselage@npm:^0.49.0": + version: 0.49.0 + resolution: "@rocket.chat/fuselage@npm:0.49.0" dependencies: "@rocket.chat/css-in-js": ^0.31.25 "@rocket.chat/css-supports": ^0.31.25 - "@rocket.chat/fuselage-tokens": ^0.32.0 + "@rocket.chat/fuselage-tokens": ^0.33.0 "@rocket.chat/memo": ^0.31.25 "@rocket.chat/styled": ^0.31.25 invariant: ^2.2.4 @@ -8807,7 +8807,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: d82121b6e6d0fa6ea204d8cbd2cc32460c4ca3585c05a1a1d908250c9150ed52edfd0acc75520cb29c6bd81a8671dc7d91f427913bcc4364a14b4eacd4bd2bf4 + checksum: 06adb80ca8399f5ca01364a433dcab9fbd3da819e109b6eff764f7c0ffa19fc533c6ff023fc23b6beba1a389f6b4d483450a5d747bc59987b1355508fa81d6c0 languageName: node linkType: hard @@ -8818,8 +8818,8 @@ __metadata: "@babel/core": ~7.22.20 "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.48.0 - "@rocket.chat/fuselage-tokens": ~0.32.0 + "@rocket.chat/fuselage": ^0.49.0 + "@rocket.chat/fuselage-tokens": ^0.33.0 "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/styled": ~0.31.25 "@rocket.chat/ui-client": "workspace:^" @@ -8967,7 +8967,7 @@ __metadata: "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/ddp-client": "workspace:^" "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage-tokens": ~0.32.0 + "@rocket.chat/fuselage-tokens": ^0.33.0 "@rocket.chat/gazzodown": "workspace:^" "@rocket.chat/logo": ^0.31.29 "@rocket.chat/message-parser": "workspace:^" @@ -9174,11 +9174,11 @@ __metadata: "@rocket.chat/favicon": "workspace:^" "@rocket.chat/forked-matrix-appservice-bridge": ^4.0.2 "@rocket.chat/forked-matrix-bot-sdk": ^0.6.0-beta.3 - "@rocket.chat/fuselage": ^0.48.0 + "@rocket.chat/fuselage": ^0.49.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/fuselage-toastbar": ~0.31.25 - "@rocket.chat/fuselage-tokens": ~0.32.0 + "@rocket.chat/fuselage-tokens": ^0.33.0 "@rocket.chat/fuselage-ui-kit": "workspace:^" "@rocket.chat/gazzodown": "workspace:^" "@rocket.chat/i18n": "workspace:^" @@ -9688,7 +9688,7 @@ __metadata: dependencies: "@react-pdf/renderer": ^3.1.14 "@rocket.chat/core-typings": "workspace:^" - "@rocket.chat/fuselage-tokens": ~0.32.0 + "@rocket.chat/fuselage-tokens": ^0.33.0 "@storybook/addon-essentials": ~6.5.16 "@storybook/react": ~6.5.16 "@testing-library/jest-dom": ^5.16.5 @@ -10054,7 +10054,7 @@ __metadata: resolution: "@rocket.chat/ui-avatar@workspace:packages/ui-avatar" dependencies: "@babel/core": ~7.22.20 - "@rocket.chat/fuselage": ^0.48.0 + "@rocket.chat/fuselage": ^0.49.0 "@rocket.chat/ui-contexts": "workspace:^" "@types/babel__core": ~7.20.3 "@types/react": ~17.0.69 @@ -10080,7 +10080,7 @@ __metadata: "@babel/core": ~7.22.20 "@react-aria/toolbar": ^3.0.0-beta.1 "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.48.0 + "@rocket.chat/fuselage": ^0.49.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/mock-providers": "workspace:^" @@ -10133,7 +10133,7 @@ __metadata: "@babel/core": ~7.22.20 "@react-aria/toolbar": ^3.0.0-beta.1 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.48.0 + "@rocket.chat/fuselage": ^0.49.0 "@rocket.chat/icons": ^0.33.0 "@storybook/addon-actions": ~6.5.16 "@storybook/addon-docs": ~6.5.16 @@ -10225,7 +10225,7 @@ __metadata: resolution: "@rocket.chat/ui-theming@workspace:ee/packages/ui-theming" dependencies: "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.48.0 + "@rocket.chat/fuselage": ^0.49.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/ui-contexts": "workspace:~" @@ -10268,7 +10268,7 @@ __metadata: "@rocket.chat/css-in-js": ~0.31.25 "@rocket.chat/emitter": ~0.31.25 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.48.0 + "@rocket.chat/fuselage": ^0.49.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/styled": ~0.31.25 @@ -10313,11 +10313,11 @@ __metadata: "@codemirror/tooltip": ^0.19.16 "@lezer/highlight": ^1.1.6 "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.48.0 + "@rocket.chat/fuselage": ^0.49.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/fuselage-toastbar": ^0.31.25 - "@rocket.chat/fuselage-tokens": ~0.32.0 + "@rocket.chat/fuselage-tokens": ^0.33.0 "@rocket.chat/fuselage-ui-kit": "workspace:~" "@rocket.chat/icons": ^0.33.0 "@rocket.chat/logo": ^0.31.29 From 939a6fa35f9dede2188417c92cb14ef81ee6e091 Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Wed, 6 Mar 2024 10:12:05 -0300 Subject: [PATCH 091/207] feat: Add `link` action to composer toolbar (#31679) Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com> --- .changeset/khaki-oranges-wink.md | 6 ++ .../messageBox/AddLinkComposerActionModal.tsx | 65 +++++++++++++++++++ .../client/messageBox/messageBoxFormatting.ts | 64 +++++++++++++----- .../FormattingToolbarDropdown.tsx | 5 +- .../MessageBoxFormattingToolbar.tsx | 9 ++- .../meteor/tests/e2e/message-composer.spec.ts | 22 +++++-- packages/i18n/src/locales/en.i18n.json | 2 + 7 files changed, 150 insertions(+), 23 deletions(-) create mode 100644 .changeset/khaki-oranges-wink.md create mode 100644 apps/meteor/app/ui-message/client/messageBox/AddLinkComposerActionModal.tsx diff --git a/.changeset/khaki-oranges-wink.md b/.changeset/khaki-oranges-wink.md new file mode 100644 index 000000000000..d03418158399 --- /dev/null +++ b/.changeset/khaki-oranges-wink.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/i18n": minor +--- + +Added a new formatter shortcut to add hyperlinks to a message diff --git a/apps/meteor/app/ui-message/client/messageBox/AddLinkComposerActionModal.tsx b/apps/meteor/app/ui-message/client/messageBox/AddLinkComposerActionModal.tsx new file mode 100644 index 000000000000..420bf93df66d --- /dev/null +++ b/apps/meteor/app/ui-message/client/messageBox/AddLinkComposerActionModal.tsx @@ -0,0 +1,65 @@ +import { Field, FieldGroup, TextInput, FieldLabel, FieldRow, Box } from '@rocket.chat/fuselage'; +import { useUniqueId } from '@rocket.chat/fuselage-hooks'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import React, { useEffect } from 'react'; +import { useForm, Controller } from 'react-hook-form'; + +import GenericModal from '../../../../client/components/GenericModal'; + +type AddLinkComposerActionModalProps = { + selectedText?: string; + onConfirm: (url: string, text: string) => void; + onClose: () => void; +}; + +const AddLinkComposerActionModal = ({ selectedText, onClose, onConfirm }: AddLinkComposerActionModalProps) => { + const t = useTranslation(); + const textField = useUniqueId(); + const urlField = useUniqueId(); + + const { handleSubmit, setFocus, control } = useForm({ + mode: 'onBlur', + defaultValues: { + text: selectedText || '', + url: '', + }, + }); + + useEffect(() => { + setFocus(selectedText ? 'url' : 'text'); + }, [selectedText, setFocus]); + + const onClickConfirm = ({ url, text }: { url: string; text: string }) => { + onConfirm(url, text); + }; + + const submit = handleSubmit(onClickConfirm); + + return ( + void submit(e)} {...props} />} + title={t('Add_link')} + > + + + {t('Text')} + + } /> + + + + {t('URL')} + + } /> + + + + + ); +}; + +export default AddLinkComposerActionModal; diff --git a/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts b/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts index 8c170f894aa4..84ca6dcc1035 100644 --- a/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts +++ b/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts @@ -1,24 +1,34 @@ import type { Keys as IconName } from '@rocket.chat/icons'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; +import type { ComposerAPI } from '../../../../client/lib/chats/ChatAPI'; +import { imperativeModal } from '../../../../client/lib/imperativeModal'; import { settings } from '../../../settings/client'; +import AddLinkComposerActionModal from './AddLinkComposerActionModal'; -export type FormattingButton = - | { - label: TranslationKey; - icon: IconName; - pattern: string; - // text?: () => string | undefined; - command?: string; - link?: string; - condition?: () => boolean; - } - | { - label: TranslationKey; - text: () => string | undefined; - link: string; - condition?: () => boolean; - }; +type FormattingButtonDefault = { label: TranslationKey; condition?: () => boolean }; + +type TextButton = { + text: () => string | undefined; + link: string; +} & FormattingButtonDefault; + +type PatternButton = { + icon: IconName; + pattern: string; + // text?: () => string | undefined; + command?: string; + link?: string; +} & FormattingButtonDefault; + +type PromptButton = { + prompt: (composer: ComposerAPI) => void; + icon: IconName; +} & FormattingButtonDefault; + +export type FormattingButton = PatternButton | PromptButton | TextButton; + +export const isPromptButton = (button: FormattingButton): button is PromptButton => 'prompt' in button; export const formattingButtons: ReadonlyArray = [ { @@ -48,6 +58,28 @@ export const formattingButtons: ReadonlyArray = [ icon: 'multiline', pattern: '```\n{{text}}\n``` ', }, + { + label: 'Link', + icon: 'link', + prompt: (composerApi: ComposerAPI) => { + const { selection } = composerApi; + + const selectedText = composerApi.substring(selection.start, selection.end); + + const onClose = () => { + imperativeModal.close(); + composerApi.focus(); + }; + + const onConfirm = (url: string, text: string) => { + onClose(); + composerApi.replaceText(`[${text}](${url})`, selection); + composerApi.setCursorToEnd(); + }; + + imperativeModal.open({ component: AddLinkComposerActionModal, props: { onConfirm, selectedText, onClose } }); + }, + }, { label: 'KaTeX' as TranslationKey, icon: 'katex', diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/FormattingToolbarDropdown.tsx b/apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/FormattingToolbarDropdown.tsx index 21eb5b94f6d6..b2ba79792e04 100644 --- a/apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/FormattingToolbarDropdown.tsx +++ b/apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/FormattingToolbarDropdown.tsx @@ -1,7 +1,7 @@ import { useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; -import type { FormattingButton } from '../../../../../../app/ui-message/client/messageBox/messageBoxFormatting'; +import { isPromptButton, type FormattingButton } from '../../../../../../app/ui-message/client/messageBox/messageBoxFormatting'; import GenericMenu from '../../../../../components/GenericMenu/GenericMenu'; import type { GenericMenuItemProps } from '../../../../../components/GenericMenu/GenericMenuItem'; import type { ComposerAPI } from '../../../../../lib/chats/ChatAPI'; @@ -21,6 +21,9 @@ const FormattingToolbarDropdown = ({ composer, items, disabled }: FormattingTool window.open(formatter.link, '_blank', 'rel=noreferrer noopener'); return; } + if (isPromptButton(formatter)) { + return formatter.prompt(composer); + } composer.wrapSelection(formatter.pattern); }; diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/MessageBoxFormattingToolbar.tsx b/apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/MessageBoxFormattingToolbar.tsx index afe8a58ec72a..79b210182d77 100644 --- a/apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/MessageBoxFormattingToolbar.tsx +++ b/apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/MessageBoxFormattingToolbar.tsx @@ -3,6 +3,7 @@ import { useTranslation } from '@rocket.chat/ui-contexts'; import React, { memo } from 'react'; import type { FormattingButton } from '../../../../../../app/ui-message/client/messageBox/messageBoxFormatting'; +import { isPromptButton } from '../../../../../../app/ui-message/client/messageBox/messageBoxFormatting'; import type { ComposerAPI } from '../../../../../lib/chats/ChatAPI'; import FormattingToolbarDropdown from './FormattingToolbarDropdown'; @@ -24,7 +25,9 @@ const MessageBoxFormattingToolbar = ({ items, variant = 'large', composer, disab <> {'icon' in featuredFormatter && ( composer.wrapSelection(featuredFormatter.pattern)} + onClick={() => + isPromptButton(featuredFormatter) ? featuredFormatter.prompt(composer) : composer.wrapSelection(featuredFormatter.pattern) + } icon={featuredFormatter.icon} disabled={disabled} /> @@ -45,6 +48,10 @@ const MessageBoxFormattingToolbar = ({ items, variant = 'large', composer, disab data-id={formatter.label} title={t(formatter.label)} onClick={(): void => { + if (isPromptButton(formatter)) { + formatter.prompt(composer); + return; + } if ('link' in formatter) { window.open(formatter.link, '_blank', 'rel=noreferrer noopener'); return; diff --git a/apps/meteor/tests/e2e/message-composer.spec.ts b/apps/meteor/tests/e2e/message-composer.spec.ts index 9ed3e42b941d..8b8888c040a2 100644 --- a/apps/meteor/tests/e2e/message-composer.spec.ts +++ b/apps/meteor/tests/e2e/message-composer.spec.ts @@ -1,3 +1,5 @@ +import { faker } from '@faker-js/faker'; + import { Users } from './fixtures/userStates'; import { HomeChannel } from './page-objects'; import { createTargetChannel } from './utils'; @@ -23,21 +25,18 @@ test.describe.serial('message-composer', () => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.content.sendMessage('hello composer'); - await expect(poHomeChannel.composerToolbarActions).toHaveCount(11); + await expect(poHomeChannel.composerToolbarActions).toHaveCount(12); }); test('should have only the main formatter and the main action', async ({ page }) => { await page.setViewportSize({ width: 768, height: 600 }); - await poHomeChannel.sidenav.openChat(targetChannel); - await poHomeChannel.content.sendMessage('hello composer'); await expect(poHomeChannel.composerToolbarActions).toHaveCount(5); }); test('should navigate on toolbar using arrow keys', async ({ page }) => { await poHomeChannel.sidenav.openChat(targetChannel); - await poHomeChannel.content.sendMessage('hello composer'); await page.keyboard.press('Tab'); await page.keyboard.press('ArrowRight'); @@ -50,11 +49,24 @@ test.describe.serial('message-composer', () => { test('should move the focus away from toolbar using tab key', async ({ page }) => { await poHomeChannel.sidenav.openChat(targetChannel); - await poHomeChannel.content.sendMessage('hello composer'); await page.keyboard.press('Tab'); await page.keyboard.press('Tab'); await expect(poHomeChannel.composerToolbar.getByRole('button', { name: 'Emoji' })).not.toBeFocused(); }); + + test('should add a link to the selected text', async ({ page }) => { + const url = faker.internet.url(); + await poHomeChannel.sidenav.openChat(targetChannel); + + await page.keyboard.type('hello composer'); + await page.keyboard.press('Control+A'); // on Windows and Linux + await page.keyboard.press('Meta+A'); // on macOS + await poHomeChannel.composerToolbar.getByRole('button', { name: 'Link' }).click() + await page.keyboard.type(url); + await page.keyboard.press('Enter'); + + await expect(poHomeChannel.composer).toHaveValue(`[hello composer](${url})`); + }); }); diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 271907a68caa..95aadb64a5d5 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -312,6 +312,7 @@ "Add_files_from": "Add files from", "Add_manager": "Add manager", "Add_monitor": "Add monitor", + "Add_link": "Add link", "Add_Reaction": "Add reaction", "Add_Role": "Add Role", "Add_Sender_To_ReplyTo": "Add Sender to Reply-To", @@ -5108,6 +5109,7 @@ "test-push-notifications": "Test push notifications", "test-push-notifications_description": "Permission to test push notifications", "Texts": "Texts", + "Text": "Text", "Thank_you_for_your_feedback": "Thank you for your feedback", "The_application_name_is_required": "The application name is required", "The_application_will_be_able_to": "<1>{{appName}} will be able to:", From 85fe51a381375538f3aa657d3a680847f5abf4e2 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 6 Mar 2024 10:29:54 -0300 Subject: [PATCH 092/207] chore: improve useJumpToMessage (#31907) --- .../message/variants/RoomMessage.tsx | 6 +- .../message/variants/ThreadMessage.tsx | 6 +- .../MessageList/hooks/useJumpToMessage.ts | 81 ++++++++++--------- 3 files changed, 49 insertions(+), 44 deletions(-) diff --git a/apps/meteor/client/components/message/variants/RoomMessage.tsx b/apps/meteor/client/components/message/variants/RoomMessage.tsx index 2ad7deedafc3..c17eb89c4596 100644 --- a/apps/meteor/client/components/message/variants/RoomMessage.tsx +++ b/apps/meteor/client/components/message/variants/RoomMessage.tsx @@ -4,7 +4,7 @@ import { useToggle } from '@rocket.chat/fuselage-hooks'; import { MessageAvatar } from '@rocket.chat/ui-avatar'; import { useUserId } from '@rocket.chat/ui-contexts'; import type { ComponentProps, ReactElement } from 'react'; -import React, { useRef, memo } from 'react'; +import React, { memo } from 'react'; import type { MessageActionContext } from '../../../../app/ui-utils/client/lib/MessageAction'; import { useIsMessageHighlight } from '../../../views/room/MessageList/contexts/MessageHighlightContext'; @@ -51,7 +51,6 @@ const RoomMessage = ({ const editing = useIsMessageHighlight(message._id); const [displayIgnoredMessage, toggleDisplayIgnoredMessage] = useToggle(false); const ignored = (ignoredUser || message.ignored) && !displayIgnoredMessage; - const messageRef = useRef(null); const { openUserCard, triggerProps } = useUserCard(); const selecting = useIsSelecting(); @@ -59,7 +58,8 @@ const RoomMessage = ({ const selected = useIsSelectedMessage(message._id); useCountSelected(); - useJumpToMessage(message._id, messageRef); + + const messageRef = useJumpToMessage(message._id); return ( ): void => { +export const useJumpToMessage = (messageId: IMessage['_id']) => { const jumpToMessageParam = useMessageListJumpToMessageParam(); const scroll = useMessageListScroll(); const router = useRouter(); - useLayoutEffect(() => { - if (jumpToMessageParam !== messageId || !messageRef.current || !scroll) { - return; - } - - setTimeout(() => { - scroll((wrapper) => { - if (!wrapper || !messageRef.current) { - return; - } - const containerRect = wrapper.getBoundingClientRect(); - const messageRect = messageRef.current.getBoundingClientRect(); - - const offset = messageRect.top - containerRect.top; - const scrollPosition = wrapper.scrollTop; - const newScrollPosition = scrollPosition + offset - SCROLL_EXTRA_OFFSET; - - return { top: newScrollPosition, behavior: 'smooth' }; - }); - - const search = router.getSearchParameters(); - delete search.msg; - router.navigate( - { - pathname: router.getLocationPathname(), - search, - }, - { replace: true }, - ); - - setHighlightMessage(messageId); - setTimeout(clearHighlightMessage, 2000); - }, 500); - }, [messageId, jumpToMessageParam, messageRef, scroll, router]); + const ref = useCallback( + (node: HTMLElement | null) => { + if (!node || !scroll) { + return; + } + setTimeout(() => { + scroll((wrapper) => { + if (!wrapper || !node) { + return; + } + const containerRect = wrapper.getBoundingClientRect(); + const messageRect = node.getBoundingClientRect(); + + const offset = messageRect.top - containerRect.top; + const scrollPosition = wrapper.scrollTop; + const newScrollPosition = scrollPosition + offset - SCROLL_EXTRA_OFFSET; + + return { top: newScrollPosition, behavior: 'smooth' }; + }); + + const { msg: _, ...search } = router.getSearchParameters(); + + router.navigate( + { + pathname: router.getLocationPathname(), + search, + }, + { replace: true }, + ); + + setHighlightMessage(messageId); + setTimeout(clearHighlightMessage, 2000); + }, 500); + }, + [messageId, router, scroll], + ); + + if (jumpToMessageParam !== messageId) { + return undefined; + } + + return ref; }; From 2c1b2c42f9bc85b2bdaeb4d50b1237f5c1a80fd2 Mon Sep 17 00:00:00 2001 From: Sy Sagar <93787385+SySagar@users.noreply.github.com> Date: Wed, 6 Mar 2024 20:11:35 +0530 Subject: [PATCH 093/207] fix: Jump to message only works once (#31662) --- .changeset/gold-tigers-rhyme.md | 5 +++++ .../client/lib/utils/setMessageJumpQueryStringParameter.ts | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .changeset/gold-tigers-rhyme.md diff --git a/.changeset/gold-tigers-rhyme.md b/.changeset/gold-tigers-rhyme.md new file mode 100644 index 000000000000..c5d0a3645aef --- /dev/null +++ b/.changeset/gold-tigers-rhyme.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +fix: Jump to message only works once diff --git a/apps/meteor/client/lib/utils/setMessageJumpQueryStringParameter.ts b/apps/meteor/client/lib/utils/setMessageJumpQueryStringParameter.ts index 74055930ef3b..6e94fe790235 100644 --- a/apps/meteor/client/lib/utils/setMessageJumpQueryStringParameter.ts +++ b/apps/meteor/client/lib/utils/setMessageJumpQueryStringParameter.ts @@ -1,13 +1,15 @@ import type { IMessage } from '@rocket.chat/core-typings'; +import type { LocationPathname } from '@rocket.chat/ui-contexts'; import { router } from '../../providers/RouterProvider'; export const setMessageJumpQueryStringParameter = async (msg: IMessage['_id'] | null) => { const { msg: _, ...search } = router.getSearchParameters(); + const locationPathname = new URL(window.location.href).pathname as LocationPathname; router.navigate( { - pathname: router.getLocationPathname(), + pathname: locationPathname, search: msg ? { ...search, msg } : search, }, { replace: true }, From 95d9088f5e6e45b9217e1c09302284cc4816244e Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 6 Mar 2024 13:04:28 -0300 Subject: [PATCH 094/207] chore: remove useScrollMessageList hook (#31901) --- .../message/list/MessageListContext.tsx | 7 ++-- .../views/room/MessageList/MessageList.tsx | 10 ++--- .../MessageList/hooks/useJumpToMessage.ts | 17 ++++---- .../providers/MessageListProvider.tsx | 8 ++-- .../client/views/room/body/RoomBody.tsx | 17 +++----- .../body/hooks/useRestoreScrollPosition.ts | 40 +++++++++++-------- .../Threads/components/ThreadMessageList.tsx | 4 +- .../views/room/hooks/useScrollMessageList.ts | 29 -------------- 8 files changed, 50 insertions(+), 82 deletions(-) delete mode 100644 apps/meteor/client/views/room/hooks/useScrollMessageList.ts diff --git a/apps/meteor/client/components/message/list/MessageListContext.tsx b/apps/meteor/client/components/message/list/MessageListContext.tsx index 82cca7377bb6..542cca6bfc0a 100644 --- a/apps/meteor/client/components/message/list/MessageListContext.tsx +++ b/apps/meteor/client/components/message/list/MessageListContext.tsx @@ -25,7 +25,7 @@ export type MessageListContextValue = { showColors: boolean; jumpToMessageParam?: string; username: string | undefined; - scrollMessageList?: (callback: (wrapper: HTMLDivElement | null) => ScrollToOptions | void) => void; + messageListRef?: React.RefObject; }; export const MessageListContext = createContext({ @@ -42,8 +42,8 @@ export const MessageListContext = createContext({ showRealName: false, showUsername: false, showColors: false, - scrollMessageList: () => undefined, username: undefined, + messageListRef: { current: null }, }); export const useShowTranslated: MessageListContextValue['useShowTranslated'] = (...args) => @@ -60,9 +60,10 @@ export const useMessageListShowUsername = (): MessageListContextValue['showUsern export const useMessageListHighlights = (): MessageListContextValue['highlights'] => useContext(MessageListContext).highlights; export const useMessageListJumpToMessageParam = (): MessageListContextValue['jumpToMessageParam'] => useContext(MessageListContext).jumpToMessageParam; -export const useMessageListScroll = (): MessageListContextValue['scrollMessageList'] => useContext(MessageListContext).scrollMessageList; export const useUserHasReacted: MessageListContextValue['useUserHasReacted'] = (message: IMessage) => useContext(MessageListContext).useUserHasReacted(message); export const useOpenEmojiPicker: MessageListContextValue['useOpenEmojiPicker'] = (...args) => useContext(MessageListContext).useOpenEmojiPicker(...args); + +export const useMessageListRef = (): MessageListContextValue['messageListRef'] => useContext(MessageListContext).messageListRef; diff --git a/apps/meteor/client/views/room/MessageList/MessageList.tsx b/apps/meteor/client/views/room/MessageList/MessageList.tsx index b39981d44bb7..a7d635ae4255 100644 --- a/apps/meteor/client/views/room/MessageList/MessageList.tsx +++ b/apps/meteor/client/views/room/MessageList/MessageList.tsx @@ -2,7 +2,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { isThreadMessage } from '@rocket.chat/core-typings'; import { useSetting, useUserPreference } from '@rocket.chat/ui-contexts'; import type { ComponentProps } from 'react'; -import React, { Fragment, forwardRef } from 'react'; +import React, { Fragment } from 'react'; import { MessageTypes } from '../../../../app/ui-utils/client'; import { useRoomSubscription } from '../contexts/RoomContext'; @@ -15,10 +15,10 @@ import MessageListProvider from './providers/MessageListProvider'; type MessageListProps = { rid: IRoom['_id']; - scrollMessageList: ComponentProps['scrollMessageList']; + messageListRef: ComponentProps['messageListRef']; }; -export const MessageList = forwardRef(function MessageList({ rid, scrollMessageList }: MessageListProps) { +export const MessageList = function MessageList({ rid, messageListRef }: MessageListProps) { const messages = useMessages({ rid }); const subscription = useRoomSubscription(); const showUserAvatar = !!useUserPreference('displayAvatars'); @@ -26,7 +26,7 @@ export const MessageList = forwardRef(function MessageList({ rid, scrollMessageL const firstUnreadMessageId = useFirstUnreadMessageId(); return ( - + {messages.map((message, index, { [index - 1]: previous }) => { const sequential = isMessageSequential(message, previous, messageGroupingPeriod); @@ -52,4 +52,4 @@ export const MessageList = forwardRef(function MessageList({ rid, scrollMessageL ); -}); +}; diff --git a/apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts b/apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts index b875a6abca69..2ec888f51d92 100644 --- a/apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts +++ b/apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts @@ -2,7 +2,7 @@ import type { IMessage } from '@rocket.chat/core-typings'; import { useRouter } from '@rocket.chat/ui-contexts'; import { useCallback } from 'react'; -import { useMessageListJumpToMessageParam, useMessageListScroll } from '../../../../components/message/list/MessageListContext'; +import { useMessageListJumpToMessageParam, useMessageListRef } from '../../../../components/message/list/MessageListContext'; import { setHighlightMessage, clearHighlightMessage } from '../providers/messageHighlightSubscription'; // this is an arbitrary value so that there's a gap between the header and the message; @@ -10,7 +10,7 @@ const SCROLL_EXTRA_OFFSET = 60; export const useJumpToMessage = (messageId: IMessage['_id']) => { const jumpToMessageParam = useMessageListJumpToMessageParam(); - const scroll = useMessageListScroll(); + const listRef = useMessageListRef(); const router = useRouter(); const ref = useCallback( @@ -19,10 +19,8 @@ export const useJumpToMessage = (messageId: IMessage['_id']) => { return; } setTimeout(() => { - scroll((wrapper) => { - if (!wrapper || !node) { - return; - } + if (listRef?.current) { + const wrapper = listRef.current; const containerRect = wrapper.getBoundingClientRect(); const messageRect = node.getBoundingClientRect(); @@ -30,11 +28,10 @@ export const useJumpToMessage = (messageId: IMessage['_id']) => { const scrollPosition = wrapper.scrollTop; const newScrollPosition = scrollPosition + offset - SCROLL_EXTRA_OFFSET; - return { top: newScrollPosition, behavior: 'smooth' }; - }); + wrapper.scrollTo({ top: newScrollPosition, behavior: 'smooth' }); + } const { msg: _, ...search } = router.getSearchParameters(); - router.navigate( { pathname: router.getLocationPathname(), @@ -47,7 +44,7 @@ export const useJumpToMessage = (messageId: IMessage['_id']) => { setTimeout(clearHighlightMessage, 2000); }, 500); }, - [messageId, router, scroll], + [listRef, messageId, router], ); if (jumpToMessageParam !== messageId) { diff --git a/apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx b/apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx index 925d5787e396..ab484ca3d982 100644 --- a/apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx +++ b/apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx @@ -15,14 +15,14 @@ import { useLoadSurroundingMessages } from '../hooks/useLoadSurroundingMessages' type MessageListProviderProps = { children: ReactNode; - scrollMessageList?: MessageListContextValue['scrollMessageList']; + messageListRef?: React.RefObject; attachmentDimension?: { width?: number; height?: number; }; }; -const MessageListProvider: VFC = ({ children, scrollMessageList, attachmentDimension }) => { +const MessageListProvider: VFC = ({ children, messageListRef, attachmentDimension }) => { const room = useRoom(); if (!room) { @@ -79,7 +79,7 @@ const MessageListProvider: VFC = ({ children, scrollMe showRoles, showRealName, showUsername, - scrollMessageList, + messageListRef, jumpToMessageParam: msgParameter, ...(katexEnabled && { katex: { @@ -120,7 +120,7 @@ const MessageListProvider: VFC = ({ children, scrollMe reactToMessage, showColors, msgParameter, - scrollMessageList, + messageListRef, chat?.emojiPicker, ], ); diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index d6fb6e8cbfe1..6411a1f805a5 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -41,7 +41,6 @@ import { useRoomToolbox } from '../contexts/RoomToolboxContext'; import { useUserCard } from '../contexts/UserCardContext'; import { useDateScroll } from '../hooks/useDateScroll'; import { useMessageListNavigation } from '../hooks/useMessageListNavigation'; -import { useScrollMessageList } from '../hooks/useScrollMessageList'; import { useDateListController } from '../providers/DateListProvider'; import DropTargetOverlay from './DropTargetOverlay'; import JumpToRecentMessageButton from './JumpToRecentMessageButton'; @@ -107,15 +106,11 @@ const RoomBody = (): ReactElement => { return false; }, []); - const scrollMessageList = useScrollMessageList(wrapperRef); - const sendToBottom = useCallback(() => { - scrollMessageList((wrapper) => { - return { left: 30, top: wrapper?.scrollHeight }; - }); + wrapperRef.current?.scrollTo({ left: 30, top: wrapperRef.current?.scrollHeight }); setHasNewMessages(false); - }, [scrollMessageList]); + }, []); const sendToBottomIfNecessary = useCallback(() => { if (atBottomRef.current === true) { @@ -428,7 +423,7 @@ const RoomBody = (): ReactElement => { }; }, [room._id, sendToBottom]); - useRestoreScrollPosition(room._id, scrollMessageList, sendToBottom); + const restoreScrollPositionRef = useRestoreScrollPosition(room._id); useEffect(() => { const wrapper = wrapperRef.current; @@ -546,7 +541,7 @@ const RoomBody = (): ReactElement => { const { messageListRef, messageListProps } = useMessageListNavigation(); - const ref = useMergedRefs(callbackRef, wrapperRef); + const ref = useMergedRefs(callbackRef, wrapperRef, restoreScrollPositionRef); return ( <> @@ -558,7 +553,7 @@ const RoomBody = (): ReactElement => { onClick={hideFlexTab && handleCloseFlexTab} >
            -
            +
            {uploads.map((upload) => ( @@ -642,7 +637,7 @@ const RoomBody = (): ReactElement => { )} ) : null} - + {hasMoreNextMessages ? (
          • {isLoadingMoreMessages ? : null}
          • ) : null} diff --git a/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts b/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts index 549961526f42..993402406a2b 100644 --- a/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts +++ b/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts @@ -1,23 +1,29 @@ import type { IRoom } from '@rocket.chat/core-typings'; -import { useEffect } from 'react'; +import { useCallback } from 'react'; -import type { MessageListContextValue } from '../../../../components/message/list/MessageListContext'; import { RoomManager } from '../../../../lib/RoomManager'; -export function useRestoreScrollPosition( - roomId: IRoom['_id'], - scrollMessageList: Exclude, - sendToBottom: () => void, -) { - useEffect(() => { - const store = RoomManager.getStore(roomId); +export function useRestoreScrollPosition(roomId: IRoom['_id']) { + const ref = useCallback( + (node: HTMLElement | null) => { + if (!node) { + return; + } + const store = RoomManager.getStore(roomId); - if (store?.scroll && !store.atBottom) { - scrollMessageList(() => { - return { left: 30, top: store.scroll }; - }); - } else { - sendToBottom(); - } - }, [roomId, scrollMessageList, sendToBottom]); + if (store?.scroll && !store.atBottom) { + node.scrollTo({ + left: 30, + top: store.scroll, + }); + } else { + node.scrollTo({ + top: node.scrollHeight, + }); + } + }, + [roomId], + ); + + return ref; } diff --git a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx index 77c2854cd7ce..c515f88aaae6 100644 --- a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx +++ b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx @@ -16,7 +16,6 @@ import LoadingMessagesIndicator from '../../../body/LoadingMessagesIndicator'; import { useDateScroll } from '../../../hooks/useDateScroll'; import { useFirstUnreadMessageId } from '../../../hooks/useFirstUnreadMessageId'; import { useMessageListNavigation } from '../../../hooks/useMessageListNavigation'; -import { useScrollMessageList } from '../../../hooks/useScrollMessageList'; import { useLegacyThreadMessageJump } from '../hooks/useLegacyThreadMessageJump'; import { useLegacyThreadMessageListScrolling } from '../hooks/useLegacyThreadMessageListScrolling'; import { useLegacyThreadMessages } from '../hooks/useLegacyThreadMessages'; @@ -66,7 +65,6 @@ const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElemen const firstUnreadMessageId = useFirstUnreadMessageId(); const messageGroupingPeriod = Number(useSetting('Message_GroupingPeriod')); - const scrollMessageList = useScrollMessageList(listWrapperScrollRef); const { messageListRef, messageListProps } = useMessageListNavigation(); const listRef = useMergedRefs(listScrollRef, listJumpRef, messageListRef); @@ -100,7 +98,7 @@ const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElemen ) : ( - + {[mainMessage, ...messages].map((message, index, { [index - 1]: previous }) => { const sequential = isMessageSequential(message, previous, messageGroupingPeriod); const newDay = isMessageNewDay(message, previous); diff --git a/apps/meteor/client/views/room/hooks/useScrollMessageList.ts b/apps/meteor/client/views/room/hooks/useScrollMessageList.ts deleted file mode 100644 index 4b324dbcb0fa..000000000000 --- a/apps/meteor/client/views/room/hooks/useScrollMessageList.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { ComponentProps, RefObject } from 'react'; -import { useCallback } from 'react'; - -import type MessageListProvider from '../MessageList/providers/MessageListProvider'; - -export const useScrollMessageList = ( - wrapperRef: RefObject, -): Exclude['scrollMessageList'], undefined> => { - // Passing a callback instead of the values so that the wrapper is exposed - return useCallback( - (callback) => { - const wrapper = wrapperRef.current; - - if (!wrapper) { - return; - } - - const options = callback(wrapperRef.current); - - // allow for bailout - if (!options) { - return; - } - - wrapper.scrollTo(options); - }, - [wrapperRef], - ); -}; From f9ee3cd775c28eb42908f9ea2660382c3421cafb Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 6 Mar 2024 13:15:54 -0300 Subject: [PATCH 095/207] chore: create useListIsAtBottom hook (#31902) --- .../client/views/room/body/RoomBody.tsx | 69 ++++--------------- .../room/body/hooks/useListIsAtBottom.ts | 29 ++++++++ 2 files changed, 42 insertions(+), 56 deletions(-) create mode 100644 apps/meteor/client/views/room/body/hooks/useListIsAtBottom.ts diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index 6411a1f805a5..0ed818bf62d9 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -52,6 +52,7 @@ import UnreadMessagesIndicator from './UnreadMessagesIndicator'; import UploadProgressIndicator from './UploadProgressIndicator'; import { useFileUploadDropTarget } from './hooks/useFileUploadDropTarget'; import { useGoToHomeOnRemoved } from './hooks/useGoToHomeOnRemoved'; +import { useListIsAtBottom } from './hooks/useListIsAtBottom'; import { useReadMessageWindowEvents } from './hooks/useReadMessageWindowEvents'; import { useRestoreScrollPosition } from './hooks/useRestoreScrollPosition'; import { useRetentionPolicy } from './hooks/useRetentionPolicy'; @@ -80,8 +81,8 @@ const RoomBody = (): ReactElement => { const wrapperRef = useRef(null); const messagesBoxRef = useRef(null); - const atBottomRef = useRef(true); - const lastScrollTopRef = useRef(0); + + const { atBottomRef, ref: isAtBottomCallbackRef } = useListIsAtBottom(); const chat = useChat(); const { openUserCard, triggerProps } = useUserCard(); @@ -116,23 +117,19 @@ const RoomBody = (): ReactElement => { if (atBottomRef.current === true) { sendToBottom(); } - }, [sendToBottom]); - - const checkIfScrollIsAtBottom = useCallback(() => { - atBottomRef.current = _isAtBottom(100); - }, [_isAtBottom]); + }, [atBottomRef, sendToBottom]); const handleNewMessageButtonClick = useCallback(() => { atBottomRef.current = true; sendToBottomIfNecessary(); chat.composer?.focus(); - }, [chat, sendToBottomIfNecessary]); + }, [atBottomRef, chat.composer, sendToBottomIfNecessary]); const handleJumpToRecentButtonClick = useCallback(() => { atBottomRef.current = true; RoomHistoryManager.clear(room._id); RoomHistoryManager.getMoreIfIsEmpty(room._id); - }, [room._id]); + }, [atBottomRef, room._id]); const [unread, setUnreadCount] = useUnreadMessages(room); @@ -358,25 +355,26 @@ const RoomBody = (): ReactElement => { }); }); + let lastScrollTopRef = 0; const handleWrapperScroll = withThrottling({ wait: 100 })((event) => { const roomLeader = messagesBoxRef.current?.querySelector('.room-leader'); if (roomLeader) { - if (event.target.scrollTop < lastScrollTopRef.current) { + if (event.target.scrollTop < lastScrollTopRef) { setHideLeaderHeader(false); } else if (_isAtBottom(100) === false && event.target.scrollTop > parseFloat(getComputedStyle(roomLeader).height)) { setHideLeaderHeader(true); } } - lastScrollTopRef.current = event.target.scrollTop; + lastScrollTopRef = event.target.scrollTop; const height = event.target.clientHeight; const isLoading = RoomHistoryManager.isLoading(room._id); const hasMore = RoomHistoryManager.hasMore(room._id); const hasMoreNext = RoomHistoryManager.hasMoreNext(room._id); if ((isLoading === false && hasMore === true) || hasMoreNext === true) { - if (hasMore === true && lastScrollTopRef.current <= height / 3) { + if (hasMore === true && lastScrollTopRef <= height / 3) { RoomHistoryManager.getMore(room._id); - } else if (hasMoreNext === true && Math.ceil(lastScrollTopRef.current) >= event.target.scrollHeight - height) { + } else if (hasMoreNext === true && Math.ceil(lastScrollTopRef) >= event.target.scrollHeight - height) { RoomHistoryManager.getMoreNext(room._id, atBottomRef); } } @@ -389,7 +387,7 @@ const RoomBody = (): ReactElement => { wrapper.removeEventListener('scroll', updateUnreadCount); wrapper.removeEventListener('scroll', handleWrapperScroll); }; - }, [_isAtBottom, list, room._id, setUnreadCount]); + }, [_isAtBottom, atBottomRef, list, room._id, setUnreadCount]); useEffect(() => { const wrapper = wrapperRef.current; @@ -425,47 +423,6 @@ const RoomBody = (): ReactElement => { const restoreScrollPositionRef = useRestoreScrollPosition(room._id); - useEffect(() => { - const wrapper = wrapperRef.current; - - if (!wrapper) { - return; - } - - const handleWheel = withThrottling({ wait: 100 })(() => { - checkIfScrollIsAtBottom(); - }); - - const handleTouchStart = (): void => { - atBottomRef.current = false; - }; - - let timer1s: ReturnType | undefined; - let timer2s: ReturnType | undefined; - - const handleTouchEnd = (): void => { - checkIfScrollIsAtBottom(); - timer1s = setTimeout(() => checkIfScrollIsAtBottom(), 1000); - timer2s = setTimeout(() => checkIfScrollIsAtBottom(), 2000); - }; - - // wrapper.addEventListener('mousewheel', handleWheel); - // wrapper.addEventListener('wheel', handleWheel); - wrapper.addEventListener('scroll', handleWheel); - // wrapper.addEventListener('touchstart', handleTouchStart); - // wrapper.addEventListener('touchend', handleTouchEnd); - - return (): void => { - if (timer1s) clearTimeout(timer1s); - if (timer2s) clearTimeout(timer2s); - wrapper.removeEventListener('mousewheel', handleWheel); - wrapper.removeEventListener('wheel', handleWheel); - wrapper.removeEventListener('scroll', handleWheel); - wrapper.removeEventListener('touchstart', handleTouchStart); - wrapper.removeEventListener('touchend', handleTouchEnd); - }; - }, [checkIfScrollIsAtBottom]); - const handleComposerResize = useCallback((): void => { sendToBottomIfNecessary(); }, [sendToBottomIfNecessary]); @@ -541,7 +498,7 @@ const RoomBody = (): ReactElement => { const { messageListRef, messageListProps } = useMessageListNavigation(); - const ref = useMergedRefs(callbackRef, wrapperRef, restoreScrollPositionRef); + const ref = useMergedRefs(callbackRef, wrapperRef, restoreScrollPositionRef, isAtBottomCallbackRef); return ( <> diff --git a/apps/meteor/client/views/room/body/hooks/useListIsAtBottom.ts b/apps/meteor/client/views/room/body/hooks/useListIsAtBottom.ts new file mode 100644 index 000000000000..a37155b4cb54 --- /dev/null +++ b/apps/meteor/client/views/room/body/hooks/useListIsAtBottom.ts @@ -0,0 +1,29 @@ +import { useCallback, useRef } from 'react'; + +import { isAtBottom } from '../../../../../app/ui/client/views/app/lib/scrolling'; +import { withThrottling } from '../../../../../lib/utils/highOrderFunctions'; + +export const useListIsAtBottom = () => { + const atBottomRef = useRef(true); + + const ref = useCallback((node: HTMLElement | null) => { + if (!node) { + return; + } + + node.addEventListener( + 'scroll', + withThrottling({ wait: 100 })(() => { + atBottomRef.current = isAtBottom(node, 100); + }), + { + passive: true, + }, + ); + }, []); + + return { + atBottomRef, + ref, + }; +}; From e2fbfa1aad4a52bed7dfc89cbd7c9b2f6adaf36b Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 6 Mar 2024 14:45:03 -0300 Subject: [PATCH 096/207] chore: create RoomBody useFileUpload hook (#31912) --- .../client/views/room/body/RoomBody.tsx | 27 ++++--------- .../views/room/body/hooks/useFileUpload.ts | 40 +++++++++++++++++++ 2 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 apps/meteor/client/views/room/body/hooks/useFileUpload.ts diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index 0ed818bf62d9..56f7dcebe765 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -14,7 +14,6 @@ import { } from '@rocket.chat/ui-contexts'; import type { MouseEventHandler, ReactElement, UIEvent } from 'react'; import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { useSyncExternalStore } from 'use-sync-external-store/shim'; import { ChatMessage, RoomRoles } from '../../../../app/models/client'; import { RoomHistoryManager } from '../../../../app/ui-utils/client'; @@ -27,7 +26,6 @@ import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout'; import { useFormatDate } from '../../../hooks/useFormatDate'; import { useReactiveQuery } from '../../../hooks/useReactiveQuery'; import { RoomManager } from '../../../lib/RoomManager'; -import type { Upload } from '../../../lib/chats/Upload'; import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; import { setMessageJumpQueryStringParameter } from '../../../lib/utils/setMessageJumpQueryStringParameter'; import Announcement from '../Announcement'; @@ -50,7 +48,7 @@ import RetentionPolicyWarning from './RetentionPolicyWarning'; import RoomForeword from './RoomForeword/RoomForeword'; import UnreadMessagesIndicator from './UnreadMessagesIndicator'; import UploadProgressIndicator from './UploadProgressIndicator'; -import { useFileUploadDropTarget } from './hooks/useFileUploadDropTarget'; +import { useFileUpload } from './hooks/useFileUpload'; import { useGoToHomeOnRemoved } from './hooks/useGoToHomeOnRemoved'; import { useListIsAtBottom } from './hooks/useListIsAtBottom'; import { useReadMessageWindowEvents } from './hooks/useReadMessageWindowEvents'; @@ -91,7 +89,12 @@ const RoomBody = (): ReactElement => { throw new Error('No ChatContext provided'); } - const [fileUploadTriggerProps, fileUploadOverlayProps] = useFileUploadDropTarget(); + const { + uploads, + handleUploadFiles, + handleUploadProgressClose, + targeDrop: [fileUploadTriggerProps, fileUploadOverlayProps], + } = useFileUpload(); const _isAtBottom = useCallback((scrollThreshold = 0) => { const wrapper = wrapperRef.current; @@ -133,8 +136,6 @@ const RoomBody = (): ReactElement => { const [unread, setUnreadCount] = useUnreadMessages(room); - const uploads = useSyncExternalStore(chat.uploads.subscribe, chat.uploads.get); - const { hasMorePreviousMessages, hasMoreNextMessages, isLoadingMoreMessages } = useRoomMessages(); const allowAnonymousRead = useSetting('Accounts_AllowAnonymousRead') as boolean | undefined; @@ -208,13 +209,6 @@ const RoomBody = (): ReactElement => { setUnreadCount(0); }, [chat.readStateManager, setUnreadCount]); - const handleUploadProgressClose = useCallback( - (id: Upload['id']) => { - chat.uploads.cancel(id); - }, - [chat], - ); - const retentionPolicy = useRetentionPolicy(room); useGoToHomeOnRemoved(room, user?._id); @@ -435,13 +429,6 @@ const RoomBody = (): ReactElement => { chat.messageEditing.toNextMessage(); }, [chat.messageEditing]); - const handleUploadFiles = useCallback( - (files: readonly File[]): void => { - chat.flows.uploadFiles(files); - }, - [chat], - ); - const replyMID = useSearchParameter('reply'); useEffect(() => { diff --git a/apps/meteor/client/views/room/body/hooks/useFileUpload.ts b/apps/meteor/client/views/room/body/hooks/useFileUpload.ts new file mode 100644 index 000000000000..088e4b198cba --- /dev/null +++ b/apps/meteor/client/views/room/body/hooks/useFileUpload.ts @@ -0,0 +1,40 @@ +import { useCallback, useEffect } from 'react'; +import { useSyncExternalStore } from 'use-sync-external-store/shim'; + +import type { Upload } from '../../../../lib/chats/Upload'; +import { useChat } from '../../contexts/ChatContext'; +import { useFileUploadDropTarget } from './useFileUploadDropTarget'; + +export const useFileUpload = () => { + const chat = useChat(); + if (!chat) { + throw new Error('No ChatContext provided'); + } + + useEffect(() => { + chat.uploads.wipeFailedOnes(); + }, [chat]); + + const uploads = useSyncExternalStore(chat.uploads.subscribe, chat.uploads.get); + + const handleUploadProgressClose = useCallback( + (id: Upload['id']) => { + chat.uploads.cancel(id); + }, + [chat], + ); + + const handleUploadFiles = useCallback( + (files: readonly File[]): void => { + chat.flows.uploadFiles(files); + }, + [chat], + ); + + return { + uploads, + handleUploadProgressClose, + handleUploadFiles, + targeDrop: useFileUploadDropTarget(), + }; +}; From 3d1f75565e07d7abc60962f6f52fec4c36ad8db0 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 6 Mar 2024 15:27:59 -0300 Subject: [PATCH 097/207] chore: move store scroll position to useRestoreScrollPosition (#31902) (#31908) --- .../client/views/room/body/RoomBody.tsx | 33 ------------------- .../body/hooks/useRestoreScrollPosition.ts | 25 +++++++++++++- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index 56f7dcebe765..73b633ab16d9 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -25,7 +25,6 @@ import { CustomScrollbars } from '../../../components/CustomScrollbars'; import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout'; import { useFormatDate } from '../../../hooks/useFormatDate'; import { useReactiveQuery } from '../../../hooks/useReactiveQuery'; -import { RoomManager } from '../../../lib/RoomManager'; import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; import { setMessageJumpQueryStringParameter } from '../../../lib/utils/setMessageJumpQueryStringParameter'; import Announcement from '../Announcement'; @@ -383,38 +382,6 @@ const RoomBody = (): ReactElement => { }; }, [_isAtBottom, atBottomRef, list, room._id, setUnreadCount]); - useEffect(() => { - const wrapper = wrapperRef.current; - - if (!wrapper) { - return; - } - - const store = RoomManager.getStore(room._id); - - const handleWrapperScroll = withThrottling({ wait: 30 })(() => { - store?.update({ scroll: wrapper.scrollTop, atBottom: isAtBottom(wrapper, 50) }); - }); - - const afterMessageGroup = (): void => { - if (store?.scroll && !store.atBottom) { - wrapper.scrollTop = store.scroll; - } else { - sendToBottom(); - } - wrapper.removeEventListener('MessageGroup', afterMessageGroup); - }; - - wrapper.addEventListener('scroll', handleWrapperScroll); - - wrapper.addEventListener('MessageGroup', afterMessageGroup); - - return () => { - wrapper.removeEventListener('MessageGroup', afterMessageGroup); - wrapper.removeEventListener('scroll', handleWrapperScroll); - }; - }, [room._id, sendToBottom]); - const restoreScrollPositionRef = useRestoreScrollPosition(room._id); const handleComposerResize = useCallback((): void => { diff --git a/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts b/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts index 993402406a2b..e9811bc5b98b 100644 --- a/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts +++ b/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts @@ -1,6 +1,10 @@ import type { IRoom } from '@rocket.chat/core-typings'; +import { useMergedRefs } from '@rocket.chat/fuselage-hooks'; +import type { RefObject } from 'react'; import { useCallback } from 'react'; +import { isAtBottom } from '../../../../../app/ui/client/views/app/lib/scrolling'; +import { withThrottling } from '../../../../../lib/utils/highOrderFunctions'; import { RoomManager } from '../../../../lib/RoomManager'; export function useRestoreScrollPosition(roomId: IRoom['_id']) { @@ -25,5 +29,24 @@ export function useRestoreScrollPosition(roomId: IRoom['_id']) { [roomId], ); - return ref; + const refCallback = useCallback( + (node: HTMLElement | null) => { + if (!node) { + return; + } + + const store = RoomManager.getStore(roomId); + + const handleWrapperScroll = withThrottling({ wait: 100 })(() => { + store?.update({ scroll: node.scrollTop, atBottom: isAtBottom(node, 50) }); + }); + + node.addEventListener('scroll', handleWrapperScroll, { + passive: true, + }); + }, + [roomId], + ); + + return useMergedRefs(refCallback, ref) as unknown as RefObject; } From af4c81a99bd3b76241556c315e1238ccb7c6b22b Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 6 Mar 2024 15:38:04 -0300 Subject: [PATCH 098/207] chore: create RoomBody useQuoteMessageByUrl hook (#31910) --- .../client/views/room/body/RoomBody.tsx | 29 ++----------------- .../room/body/hooks/useQuoteMessageByUrl.ts | 28 ++++++++++++++++++ 2 files changed, 31 insertions(+), 26 deletions(-) create mode 100644 apps/meteor/client/views/room/body/hooks/useQuoteMessageByUrl.ts diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index 73b633ab16d9..2ee633d09d8a 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -2,16 +2,7 @@ import type { IMessage, IUser } from '@rocket.chat/core-typings'; import { isEditedMessage } from '@rocket.chat/core-typings'; import { Box, Bubble } from '@rocket.chat/fuselage'; import { useMergedRefs } from '@rocket.chat/fuselage-hooks'; -import { - usePermission, - useRole, - useRouter, - useSearchParameter, - useSetting, - useTranslation, - useUser, - useUserPreference, -} from '@rocket.chat/ui-contexts'; +import { usePermission, useRole, useRouter, useSetting, useTranslation, useUser, useUserPreference } from '@rocket.chat/ui-contexts'; import type { MouseEventHandler, ReactElement, UIEvent } from 'react'; import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; @@ -54,6 +45,7 @@ import { useReadMessageWindowEvents } from './hooks/useReadMessageWindowEvents'; import { useRestoreScrollPosition } from './hooks/useRestoreScrollPosition'; import { useRetentionPolicy } from './hooks/useRetentionPolicy'; import { useUnreadMessages } from './hooks/useUnreadMessages'; +import { useQuoteMessageByUrl } from './hooks/useQuoteMessageByUrl'; const RoomBody = (): ReactElement => { const formatDate = useFormatDate(); @@ -396,22 +388,6 @@ const RoomBody = (): ReactElement => { chat.messageEditing.toNextMessage(); }, [chat.messageEditing]); - const replyMID = useSearchParameter('reply'); - - useEffect(() => { - if (!replyMID) { - return; - } - - chat.data.getMessageByID(replyMID).then((message) => { - if (!message) { - return; - } - - chat.composer?.quoteMessage(message); - }); - }, [chat.data, chat.composer, replyMID]); - useEffect(() => { chat.uploads.wipeFailedOnes(); }, [chat]); @@ -449,6 +425,7 @@ const RoomBody = (): ReactElement => { ); useReadMessageWindowEvents(); + useQuoteMessageByUrl(); const { messageListRef, messageListProps } = useMessageListNavigation(); diff --git a/apps/meteor/client/views/room/body/hooks/useQuoteMessageByUrl.ts b/apps/meteor/client/views/room/body/hooks/useQuoteMessageByUrl.ts new file mode 100644 index 000000000000..6895a1c2fabd --- /dev/null +++ b/apps/meteor/client/views/room/body/hooks/useQuoteMessageByUrl.ts @@ -0,0 +1,28 @@ +import { useSearchParameter } from '@rocket.chat/ui-contexts'; +import { useEffect } from 'react'; + +import { useChat } from '../../contexts/ChatContext'; + +export const useQuoteMessageByUrl = () => { + const replyMID = useSearchParameter('reply'); + + const chat = useChat(); + + if (!chat) { + throw new Error('No ChatContext provided'); + } + + useEffect(() => { + if (!replyMID) { + return; + } + + chat.data.getMessageByID(replyMID).then((message) => { + if (!message) { + return; + } + + chat.composer?.quoteMessage(message); + }); + }, [chat.data, chat.composer, replyMID]); +}; From 3ca8f0d59c9431d68c1b42d0d9980a24db4470df Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 6 Mar 2024 15:49:57 -0300 Subject: [PATCH 099/207] chore: create Roombody useLeaderBanner hook (#31911) --- .../client/views/room/body/RoomBody.tsx | 17 +++----- .../views/room/body/hooks/useLeaderBanner.ts | 39 +++++++++++++++++++ 2 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 apps/meteor/client/views/room/body/hooks/useLeaderBanner.ts diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index 2ee633d09d8a..b6277c3419e4 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -40,12 +40,13 @@ import UnreadMessagesIndicator from './UnreadMessagesIndicator'; import UploadProgressIndicator from './UploadProgressIndicator'; import { useFileUpload } from './hooks/useFileUpload'; import { useGoToHomeOnRemoved } from './hooks/useGoToHomeOnRemoved'; +import { useLeaderBanner } from './hooks/useLeaderBanner'; import { useListIsAtBottom } from './hooks/useListIsAtBottom'; +import { useQuoteMessageByUrl } from './hooks/useQuoteMessageByUrl'; import { useReadMessageWindowEvents } from './hooks/useReadMessageWindowEvents'; import { useRestoreScrollPosition } from './hooks/useRestoreScrollPosition'; import { useRetentionPolicy } from './hooks/useRetentionPolicy'; import { useUnreadMessages } from './hooks/useUnreadMessages'; -import { useQuoteMessageByUrl } from './hooks/useQuoteMessageByUrl'; const RoomBody = (): ReactElement => { const formatDate = useFormatDate(); @@ -61,7 +62,6 @@ const RoomBody = (): ReactElement => { const { callbackRef, listStyle, bubbleDate, showBubble, style: bubbleDateStyle } = useDateScroll(); const [lastMessageDate, setLastMessageDate] = useState(); - const [hideLeaderHeader, setHideLeaderHeader] = useState(false); const [hasNewMessages, setHasNewMessages] = useState(false); const hideFlexTab = useUserPreference('hideFlexTab') || undefined; @@ -69,13 +69,14 @@ const RoomBody = (): ReactElement => { const displayAvatars = useUserPreference('displayAvatars'); const wrapperRef = useRef(null); - const messagesBoxRef = useRef(null); const { atBottomRef, ref: isAtBottomCallbackRef } = useListIsAtBottom(); const chat = useChat(); const { openUserCard, triggerProps } = useUserCard(); + const { messagesBoxRef, hideLeaderHeader, ref: leaderBannerRefCallback } = useLeaderBanner(); + if (!chat) { throw new Error('No ChatContext provided'); } @@ -342,14 +343,6 @@ const RoomBody = (): ReactElement => { let lastScrollTopRef = 0; const handleWrapperScroll = withThrottling({ wait: 100 })((event) => { - const roomLeader = messagesBoxRef.current?.querySelector('.room-leader'); - if (roomLeader) { - if (event.target.scrollTop < lastScrollTopRef) { - setHideLeaderHeader(false); - } else if (_isAtBottom(100) === false && event.target.scrollTop > parseFloat(getComputedStyle(roomLeader).height)) { - setHideLeaderHeader(true); - } - } lastScrollTopRef = event.target.scrollTop; const height = event.target.clientHeight; const isLoading = RoomHistoryManager.isLoading(room._id); @@ -441,7 +434,7 @@ const RoomBody = (): ReactElement => { onClick={hideFlexTab && handleCloseFlexTab} >
            -
            +
            {uploads.map((upload) => ( diff --git a/apps/meteor/client/views/room/body/hooks/useLeaderBanner.ts b/apps/meteor/client/views/room/body/hooks/useLeaderBanner.ts new file mode 100644 index 000000000000..973ddbd31d23 --- /dev/null +++ b/apps/meteor/client/views/room/body/hooks/useLeaderBanner.ts @@ -0,0 +1,39 @@ +import { useCallback, useRef, useState } from 'react'; + +import { isAtBottom } from '../../../../../app/ui/client/views/app/lib/scrolling'; +import { withThrottling } from '../../../../../lib/utils/highOrderFunctions'; + +export const useLeaderBanner = () => { + const [hideLeaderHeader, setHideLeaderHeader] = useState(false); + + const messagesBoxRef = useRef(null); + + const ref = useCallback((node: HTMLElement | null) => { + if (!node) { + return; + } + let lastScrollTopRef = 0; + + node.addEventListener( + 'scroll', + withThrottling({ wait: 100 })((event) => { + const roomLeader = messagesBoxRef.current?.querySelector('.room-leader'); + if (roomLeader) { + if (event.target.scrollTop < lastScrollTopRef) { + setHideLeaderHeader(false); + } else if (isAtBottom(node, 100) === false && event.target.scrollTop > parseFloat(getComputedStyle(roomLeader).height)) { + setHideLeaderHeader(true); + } + } + lastScrollTopRef = event.target.scrollTop; + }), + { passive: true }, + ); + }, []); + + return { + messagesBoxRef, + hideLeaderHeader, + ref, + }; +}; From 727402cdff29aaca4d59b44fa3f75c2ce14c1a67 Mon Sep 17 00:00:00 2001 From: tiran133 Date: Thu, 7 Mar 2024 04:50:19 +1000 Subject: [PATCH 100/207] fix: Fixes SAML Role mapping of custom roles (#31790) --- .changeset/curly-impalas-wave.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/curly-impalas-wave.md diff --git a/.changeset/curly-impalas-wave.md b/.changeset/curly-impalas-wave.md new file mode 100644 index 000000000000..242c2ae62df8 --- /dev/null +++ b/.changeset/curly-impalas-wave.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/meteor': patch +--- + +fix: Fixes SAML Role mapping of custom roles +fixed: here https://github.com/RocketChat/Rocket.Chat/pull/31830 From 088bca712a53153a870a544830cf74be6c646208 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Wed, 6 Mar 2024 14:48:59 -0600 Subject: [PATCH 101/207] refactor: Delay queue-worker start until first work is received (#31539) --- .../src/OmnichannelTranscript.ts | 7 +++++-- .../omnichannel-services/src/QueueWorker.ts | 17 ++++++++++++++--- .../src/externals/mongo-message-queue.d.ts | 2 ++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/ee/packages/omnichannel-services/src/OmnichannelTranscript.ts b/ee/packages/omnichannel-services/src/OmnichannelTranscript.ts index 0014a50f1f7f..02816b7aebc3 100644 --- a/ee/packages/omnichannel-services/src/OmnichannelTranscript.ts +++ b/ee/packages/omnichannel-services/src/OmnichannelTranscript.ts @@ -10,7 +10,7 @@ import { Settings as settingsService, } from '@rocket.chat/core-services'; import type { IOmnichannelTranscriptService } from '@rocket.chat/core-services'; -import type { IMessage, IUser, IRoom, IUpload, ILivechatVisitor, ILivechatAgent } from '@rocket.chat/core-typings'; +import type { IMessage, IUser, IRoom, IUpload, ILivechatVisitor, ILivechatAgent, IOmnichannelRoom } from '@rocket.chat/core-typings'; import { isQuoteAttachment, isFileAttachment, isFileImageAttachment } from '@rocket.chat/core-typings'; import type { Logger } from '@rocket.chat/logger'; import { parse } from '@rocket.chat/message-parser'; @@ -93,7 +93,10 @@ export class OmnichannelTranscript extends ServiceClass implements IOmnichannelT async requestTranscript({ details }: { details: WorkDetails }): Promise { this.log.info(`Requesting transcript for room ${details.rid} by user ${details.userId}`); - const room = await LivechatRooms.findOneById(details.rid); + const room = await LivechatRooms.findOneById>(details.rid, { + projection: { _id: 1, open: 1, v: 1, pdfTranscriptRequested: 1 }, + }); + if (!room) { throw new Error('room-not-found'); } diff --git a/ee/packages/omnichannel-services/src/QueueWorker.ts b/ee/packages/omnichannel-services/src/QueueWorker.ts index b217586d3a83..23604c46eca4 100644 --- a/ee/packages/omnichannel-services/src/QueueWorker.ts +++ b/ee/packages/omnichannel-services/src/QueueWorker.ts @@ -17,12 +17,15 @@ export class QueueWorker extends ServiceClass implements IQueueWorkerService { private logger: Logger; + private queueStarted = false; + constructor(private readonly db: Db, loggerClass: typeof Logger) { super(); // eslint-disable-next-line new-cap this.logger = new loggerClass('QueueWorker'); this.queue = new MessageQueue(); + this.queue.pollingInterval = 5000; } isServiceNotFoundMessage(message: string): boolean { @@ -34,13 +37,11 @@ export class QueueWorker extends ServiceClass implements IQueueWorkerService { } async created(): Promise { - this.logger.info('Starting queue worker'); this.queue.databasePromise = () => { return Promise.resolve(this.db); }; try { - await this.registerWorkers(); await this.createIndexes(); } catch (e) { this.logger.fatal(e, 'Fatal error occurred when registering workers'); @@ -95,12 +96,14 @@ export class QueueWorker extends ServiceClass implements IQueueWorkerService { } // Registers the actual workers, the actions lib will try to fetch elements to work on - private async registerWorkers(): Promise { + private registerWorkers(): void { this.logger.info('Registering workers of type "work"'); this.queue.registerWorker('work', this.workerCallback.bind(this)); this.logger.info('Registering workers of type "workComplete"'); this.queue.registerWorker('workComplete', this.workerCallback.bind(this)); + + this.queueStarted = true; } private matchServiceCall(service: string): boolean { @@ -117,6 +120,10 @@ export class QueueWorker extends ServiceClass implements IQueueWorkerService { // This is a "generic" job that allows you to call any service async queueWork>(queue: Actions, to: string, data: T): Promise { this.logger.info(`Queueing work for ${to}`); + if (!this.queueStarted) { + this.registerWorkers(); + } + if (!this.matchServiceCall(to)) { // We don't want to queue calls to invalid service names throw new Error(`Invalid service name ${to}`); @@ -140,4 +147,8 @@ export class QueueWorker extends ServiceClass implements IQueueWorkerService { ]) .toArray(); } + + async isQueueStarted(): Promise { + return this.queueStarted; + } } diff --git a/ee/packages/omnichannel-services/src/externals/mongo-message-queue.d.ts b/ee/packages/omnichannel-services/src/externals/mongo-message-queue.d.ts index ea7b6f4ed971..aa59f2012cd0 100644 --- a/ee/packages/omnichannel-services/src/externals/mongo-message-queue.d.ts +++ b/ee/packages/omnichannel-services/src/externals/mongo-message-queue.d.ts @@ -30,5 +30,7 @@ declare module 'mongo-message-queue' { enqueueAndProcess(type: Actions, message: T, options?: { nextReceivableTime: Date; priority: number }): Promise; stopPolling(): void; + + pollingInterval?: number; } } From 901e39362c2c9cbb921f49807ec55b1ae81a1ca7 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 6 Mar 2024 18:02:11 -0300 Subject: [PATCH 102/207] chore: create RoomBody useHandleUnread hook (#31914) --- .../client/views/room/body/RoomBody.tsx | 154 +++-------------- .../room/body/UnreadMessagesIndicator.tsx | 2 +- .../views/room/body/hooks/useLeaderBanner.ts | 10 +- .../room/body/hooks/useUnreadMessages.ts | 160 +++++++++++++++++- 4 files changed, 189 insertions(+), 137 deletions(-) diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index b6277c3419e4..253ad791e697 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -2,22 +2,20 @@ import type { IMessage, IUser } from '@rocket.chat/core-typings'; import { isEditedMessage } from '@rocket.chat/core-typings'; import { Box, Bubble } from '@rocket.chat/fuselage'; import { useMergedRefs } from '@rocket.chat/fuselage-hooks'; -import { usePermission, useRole, useRouter, useSetting, useTranslation, useUser, useUserPreference } from '@rocket.chat/ui-contexts'; +import { usePermission, useRole, useSetting, useTranslation, useUser, useUserPreference } from '@rocket.chat/ui-contexts'; import type { MouseEventHandler, ReactElement, UIEvent } from 'react'; import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { ChatMessage, RoomRoles } from '../../../../app/models/client'; +import { RoomRoles } from '../../../../app/models/client'; import { RoomHistoryManager } from '../../../../app/ui-utils/client'; import { isAtBottom } from '../../../../app/ui/client/views/app/lib/scrolling'; import { callbacks } from '../../../../lib/callbacks'; import { isTruthy } from '../../../../lib/isTruthy'; -import { withDebouncing, withThrottling } from '../../../../lib/utils/highOrderFunctions'; +import { withThrottling } from '../../../../lib/utils/highOrderFunctions'; import { CustomScrollbars } from '../../../components/CustomScrollbars'; import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout'; import { useFormatDate } from '../../../hooks/useFormatDate'; import { useReactiveQuery } from '../../../hooks/useReactiveQuery'; -import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; -import { setMessageJumpQueryStringParameter } from '../../../lib/utils/setMessageJumpQueryStringParameter'; import Announcement from '../Announcement'; import { MessageList } from '../MessageList'; import MessageListErrorBoundary from '../MessageList/MessageListErrorBoundary'; @@ -46,7 +44,7 @@ import { useQuoteMessageByUrl } from './hooks/useQuoteMessageByUrl'; import { useReadMessageWindowEvents } from './hooks/useReadMessageWindowEvents'; import { useRestoreScrollPosition } from './hooks/useRestoreScrollPosition'; import { useRetentionPolicy } from './hooks/useRetentionPolicy'; -import { useUnreadMessages } from './hooks/useUnreadMessages'; +import { useHandleUnread } from './hooks/useUnreadMessages'; const RoomBody = (): ReactElement => { const formatDate = useFormatDate(); @@ -61,7 +59,6 @@ const RoomBody = (): ReactElement => { const { list } = useDateListController(); const { callbackRef, listStyle, bubbleDate, showBubble, style: bubbleDateStyle } = useDateScroll(); - const [lastMessageDate, setLastMessageDate] = useState(); const [hasNewMessages, setHasNewMessages] = useState(false); const hideFlexTab = useUserPreference('hideFlexTab') || undefined; @@ -70,12 +67,20 @@ const RoomBody = (): ReactElement => { const wrapperRef = useRef(null); + const { + ref: unreadBarRef, + messagesBoxRef: unreadBarMessagesBoxRef, + handleUnreadBarJumpToButtonClick, + handleMarkAsReadButtonClick, + counter: [unread], + } = useHandleUnread(room, subscription); + const { atBottomRef, ref: isAtBottomCallbackRef } = useListIsAtBottom(); const chat = useChat(); const { openUserCard, triggerProps } = useUserCard(); - const { messagesBoxRef, hideLeaderHeader, ref: leaderBannerRefCallback } = useLeaderBanner(); + const { wrapperBoxRef, hideLeaderHeader, innerScrollRef: leaderBannerRefCallback } = useLeaderBanner(); if (!chat) { throw new Error('No ChatContext provided'); @@ -126,8 +131,6 @@ const RoomBody = (): ReactElement => { RoomHistoryManager.getMoreIfIsEmpty(room._id); }, [atBottomRef, room._id]); - const [unread, setUnreadCount] = useUnreadMessages(room); - const { hasMorePreviousMessages, hasMoreNextMessages, isLoadingMoreMessages } = useRoomMessages(); const allowAnonymousRead = useSetting('Accounts_AllowAnonymousRead') as boolean | undefined; @@ -182,25 +185,6 @@ const RoomBody = (): ReactElement => { [openUserCard], ); - const handleUnreadBarJumpToButtonClick = useCallback(() => { - const rid = room._id; - const { firstUnread } = RoomHistoryManager.getRoom(rid); - let message = firstUnread?.get(); - if (!message) { - message = ChatMessage.findOne({ rid, ts: { $gt: unread?.since } }, { sort: { ts: 1 }, limit: 1 }); - } - if (!message) { - return; - } - setMessageJumpQueryStringParameter(message?._id); - setUnreadCount(0); - }, [room._id, unread?.since, setUnreadCount]); - - const handleMarkAsReadButtonClick = useCallback(() => { - chat.readStateManager.markAsRead(); - setUnreadCount(0); - }, [chat.readStateManager, setUnreadCount]); - const retentionPolicy = useRetentionPolicy(room); useGoToHomeOnRemoved(room, user?._id); @@ -249,49 +233,6 @@ const RoomBody = (): ReactElement => { }; }, [sendToBottomIfNecessary]); - const router = useRouter(); - - const debouncedReadMessageRead = useMemo( - () => - withDebouncing({ wait: 500 })(() => { - chat.readStateManager.attemptMarkAsRead(); - }), - [chat.readStateManager], - ); - - useEffect( - () => - router.subscribeToRouteChange(() => { - const routeName = router.getRouteName(); - if (!routeName || !roomCoordinator.isRouteNameKnown(routeName)) { - return; - } - - debouncedReadMessageRead(); - }), - [debouncedReadMessageRead, room._id, router, subscribed, subscription?.alert, subscription?.unread], - ); - - useEffect(() => { - if (!subscribed) { - setUnreadCount(0); - return; - } - - const count = ChatMessage.find({ - rid: room._id, - ts: { $lte: lastMessageDate, $gt: subscription?.ls }, - }).count(); - - setUnreadCount(count); - }, [lastMessageDate, room._id, setUnreadCount, subscribed, subscription?.ls]); - - useEffect(() => { - if (!unread?.count) { - return debouncedReadMessageRead(); - } - }, [debouncedReadMessageRead, room._id, unread?.count]); - useEffect(() => { const wrapper = wrapperRef.current; @@ -299,48 +240,6 @@ const RoomBody = (): ReactElement => { return; } - const getElementFromPoint = (topOffset = 0): Element | undefined => { - const messagesBox = messagesBoxRef.current; - - if (!messagesBox) { - return; - } - - const messagesBoxLeft = messagesBox.getBoundingClientRect().left + window.pageXOffset; - const messagesBoxTop = messagesBox.getBoundingClientRect().top + window.pageYOffset; - const messagesBoxWidth = parseFloat(getComputedStyle(messagesBox).width); - - let element; - if (document.dir === 'rtl') { - element = document.elementFromPoint(messagesBoxLeft + messagesBoxWidth - 1, messagesBoxTop + topOffset + 1); - } else { - element = document.elementFromPoint(messagesBoxLeft + 1, messagesBoxTop + topOffset + 1); - } - - if (element?.classList.contains('rcx-message') || element?.classList.contains('rcx-message--sequential')) { - return element; - } - }; - - const updateUnreadCount = withThrottling({ wait: 300 })(() => { - Tracker.afterFlush(() => { - const lastInvisibleMessageOnScreen = getElementFromPoint(0) || getElementFromPoint(20) || getElementFromPoint(40); - - if (!lastInvisibleMessageOnScreen) { - setUnreadCount(0); - return; - } - - const lastMessage = ChatMessage.findOne(lastInvisibleMessageOnScreen.id); - if (!lastMessage) { - setUnreadCount(0); - return; - } - - setLastMessageDate(lastMessage.ts); - }); - }); - let lastScrollTopRef = 0; const handleWrapperScroll = withThrottling({ wait: 100 })((event) => { lastScrollTopRef = event.target.scrollTop; @@ -358,14 +257,12 @@ const RoomBody = (): ReactElement => { } }); - wrapper.addEventListener('scroll', updateUnreadCount); wrapper.addEventListener('scroll', handleWrapperScroll); return () => { - wrapper.removeEventListener('scroll', updateUnreadCount); wrapper.removeEventListener('scroll', handleWrapperScroll); }; - }, [_isAtBottom, atBottomRef, list, room._id, setUnreadCount]); + }, [_isAtBottom, atBottomRef, list, room._id]); const restoreScrollPositionRef = useRestoreScrollPosition(room._id); @@ -422,19 +319,27 @@ const RoomBody = (): ReactElement => { const { messageListRef, messageListProps } = useMessageListNavigation(); - const ref = useMergedRefs(callbackRef, wrapperRef, restoreScrollPositionRef, isAtBottomCallbackRef); + const ref = useMergedRefs( + callbackRef, + wrapperRef, + restoreScrollPositionRef, + isAtBottomCallbackRef, + unreadBarRef, + leaderBannerRefCallback, + unreadBarMessagesBoxRef, + ); return ( <> {!isLayoutEmbedded && room.announcement && } - +
            -
            +
            {uploads.map((upload) => ( @@ -455,17 +360,14 @@ const RoomBody = (): ReactElement => { )} - {unread && ( + {Boolean(unread) && ( )} -
            +
            { diff --git a/apps/meteor/client/views/room/body/hooks/useLeaderBanner.ts b/apps/meteor/client/views/room/body/hooks/useLeaderBanner.ts index 973ddbd31d23..a899d66f42cb 100644 --- a/apps/meteor/client/views/room/body/hooks/useLeaderBanner.ts +++ b/apps/meteor/client/views/room/body/hooks/useLeaderBanner.ts @@ -6,9 +6,9 @@ import { withThrottling } from '../../../../../lib/utils/highOrderFunctions'; export const useLeaderBanner = () => { const [hideLeaderHeader, setHideLeaderHeader] = useState(false); - const messagesBoxRef = useRef(null); + const wrapperBoxRef = useRef(null); - const ref = useCallback((node: HTMLElement | null) => { + const innerScrollRef = useCallback((node: HTMLElement | null) => { if (!node) { return; } @@ -17,7 +17,7 @@ export const useLeaderBanner = () => { node.addEventListener( 'scroll', withThrottling({ wait: 100 })((event) => { - const roomLeader = messagesBoxRef.current?.querySelector('.room-leader'); + const roomLeader = wrapperBoxRef.current?.querySelector('.room-leader'); if (roomLeader) { if (event.target.scrollTop < lastScrollTopRef) { setHideLeaderHeader(false); @@ -32,8 +32,8 @@ export const useLeaderBanner = () => { }, []); return { - messagesBoxRef, + wrapperBoxRef, hideLeaderHeader, - ref, + innerScrollRef, }; }; diff --git a/apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts b/apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts index dac89be78a34..6ecf5a02d2b9 100644 --- a/apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts +++ b/apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts @@ -1,18 +1,22 @@ -import type { IRoom } from '@rocket.chat/core-typings'; +import type { IRoom, ISubscription } from '@rocket.chat/core-typings'; +import { useRouter } from '@rocket.chat/ui-contexts'; import type { Dispatch, SetStateAction } from 'react'; -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { ChatMessage } from '../../../../../app/models/client'; import { LegacyRoomManager, RoomHistoryManager } from '../../../../../app/ui-utils/client'; +import { withDebouncing, withThrottling } from '../../../../../lib/utils/highOrderFunctions'; import { useReactiveValue } from '../../../../hooks/useReactiveValue'; +import { roomCoordinator } from '../../../../lib/rooms/roomCoordinator'; +import { setMessageJumpQueryStringParameter } from '../../../../lib/utils/setMessageJumpQueryStringParameter'; +import { useChat } from '../../contexts/ChatContext'; interface IUnreadMessages { count: number; since: Date; } -export const useUnreadMessages = ( - room: IRoom, -): readonly [data: IUnreadMessages | undefined, setUnreadCount: Dispatch>] => { +const useUnreadMessages = (room: IRoom): readonly [data: IUnreadMessages | undefined, setUnreadCount: Dispatch>] => { const notLoadedCount = useReactiveValue(useCallback(() => RoomHistoryManager.getRoom(room._id).unreadNotLoaded.get(), [room._id])); const [loadedCount, setLoadedCount] = useState(0); @@ -28,3 +32,149 @@ export const useUnreadMessages = ( return [undefined, setLoadedCount]; }, [count, since]); }; + +export const useHandleUnread = ( + room: IRoom, + subscription?: ISubscription, +): { + ref: (wrapper: HTMLDivElement | null) => void; + messagesBoxRef: React.MutableRefObject; + handleUnreadBarJumpToButtonClick: () => void; + handleMarkAsReadButtonClick: () => void; + counter: readonly [number, Date | undefined]; +} => { + const messagesBoxRef = useRef(null); + + const subscribed = Boolean(subscription); + const [unread, setUnreadCount] = useUnreadMessages(room); + + const [lastMessageDate, setLastMessageDate] = useState(); + + const chat = useChat(); + + if (!chat) { + throw new Error('No ChatContext provided'); + } + const handleUnreadBarJumpToButtonClick = useCallback(() => { + const rid = room._id; + const { firstUnread } = RoomHistoryManager.getRoom(rid); + let message = firstUnread?.get(); + if (!message) { + message = ChatMessage.findOne({ rid, ts: { $gt: unread?.since } }, { sort: { ts: 1 }, limit: 1 }); + } + if (!message) { + return; + } + setMessageJumpQueryStringParameter(message?._id); + setUnreadCount(0); + }, [room._id, unread?.since, setUnreadCount]); + + const handleMarkAsReadButtonClick = useCallback(() => { + chat.readStateManager.markAsRead(); + setUnreadCount(0); + }, [chat.readStateManager, setUnreadCount]); + + useEffect(() => { + if (!subscribed) { + setUnreadCount(0); + return; + } + + const count = ChatMessage.find({ + rid: room._id, + ts: { $lte: lastMessageDate, $gt: subscription?.ls }, + }).count(); + + setUnreadCount(count); + }, [lastMessageDate, room._id, setUnreadCount, subscribed, subscription?.ls]); + + const router = useRouter(); + + const debouncedReadMessageRead = useMemo( + () => + withDebouncing({ wait: 500 })(() => { + chat.readStateManager.attemptMarkAsRead(); + }), + [chat.readStateManager], + ); + + useEffect( + () => + router.subscribeToRouteChange(() => { + const routeName = router.getRouteName(); + if (!routeName || !roomCoordinator.isRouteNameKnown(routeName)) { + return; + } + + debouncedReadMessageRead(); + }), + [debouncedReadMessageRead, room._id, router, subscribed, subscription?.alert, subscription?.unread], + ); + + useEffect(() => { + if (!unread?.count) { + return debouncedReadMessageRead(); + } + }, [debouncedReadMessageRead, room._id, unread?.count]); + + const ref = useCallback( + (wrapper: HTMLDivElement | null) => { + if (!wrapper) { + return; + } + + const getElementFromPoint = (topOffset = 0): Element | undefined => { + const messagesBox = messagesBoxRef.current; + + if (!messagesBox) { + return; + } + + const messagesBoxLeft = messagesBox.getBoundingClientRect().left + window.pageXOffset; + const messagesBoxTop = messagesBox.getBoundingClientRect().top + window.pageYOffset; + const messagesBoxWidth = parseFloat(getComputedStyle(messagesBox).width); + + let element; + if (document.dir === 'rtl') { + element = document.elementFromPoint(messagesBoxLeft + messagesBoxWidth - 2, messagesBoxTop + topOffset + 2); + } else { + element = document.elementFromPoint(messagesBoxLeft + 2, messagesBoxTop + topOffset + 2); + } + + if (element?.classList.contains('rcx-message') || element?.classList.contains('rcx-message--sequential')) { + return element; + } + }; + wrapper.addEventListener( + 'scroll', + withThrottling({ wait: 300 })(() => { + Tracker.afterFlush(() => { + const lastInvisibleMessageOnScreen = getElementFromPoint(0) || getElementFromPoint(20) || getElementFromPoint(40); + + if (!lastInvisibleMessageOnScreen) { + setUnreadCount(0); + return; + } + + const lastMessage = ChatMessage.findOne(lastInvisibleMessageOnScreen.id); + if (!lastMessage) { + setUnreadCount(0); + return; + } + + setLastMessageDate(lastMessage.ts); + }); + }), + ); + }, + [setUnreadCount], + ); + + return { + ref, + messagesBoxRef, + handleUnreadBarJumpToButtonClick, + handleMarkAsReadButtonClick, + counter: [unread?.count ?? 0, unread?.since] as const, + }; +}; From 2a1ad6decca60518c1f00edd6bf4ee1d1074f662 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 6 Mar 2024 19:12:33 -0300 Subject: [PATCH 103/207] chore: create RoomBody useGetMore hook (#31913) --- .../client/views/room/body/RoomBody.tsx | 38 ++----------------- .../views/room/body/hooks/useGetMore.ts | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+), 34 deletions(-) create mode 100644 apps/meteor/client/views/room/body/hooks/useGetMore.ts diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index 253ad791e697..6814320a99f4 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -11,7 +11,6 @@ import { RoomHistoryManager } from '../../../../app/ui-utils/client'; import { isAtBottom } from '../../../../app/ui/client/views/app/lib/scrolling'; import { callbacks } from '../../../../lib/callbacks'; import { isTruthy } from '../../../../lib/isTruthy'; -import { withThrottling } from '../../../../lib/utils/highOrderFunctions'; import { CustomScrollbars } from '../../../components/CustomScrollbars'; import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout'; import { useFormatDate } from '../../../hooks/useFormatDate'; @@ -27,7 +26,6 @@ import { useRoomToolbox } from '../contexts/RoomToolboxContext'; import { useUserCard } from '../contexts/UserCardContext'; import { useDateScroll } from '../hooks/useDateScroll'; import { useMessageListNavigation } from '../hooks/useMessageListNavigation'; -import { useDateListController } from '../providers/DateListProvider'; import DropTargetOverlay from './DropTargetOverlay'; import JumpToRecentMessageButton from './JumpToRecentMessageButton'; import LeaderBar from './LeaderBar'; @@ -37,6 +35,7 @@ import RoomForeword from './RoomForeword/RoomForeword'; import UnreadMessagesIndicator from './UnreadMessagesIndicator'; import UploadProgressIndicator from './UploadProgressIndicator'; import { useFileUpload } from './hooks/useFileUpload'; +import { useGetMore } from './hooks/useGetMore'; import { useGoToHomeOnRemoved } from './hooks/useGoToHomeOnRemoved'; import { useLeaderBanner } from './hooks/useLeaderBanner'; import { useListIsAtBottom } from './hooks/useListIsAtBottom'; @@ -56,7 +55,6 @@ const RoomBody = (): ReactElement => { const admin = useRole('admin'); const subscription = useRoomSubscription(); - const { list } = useDateListController(); const { callbackRef, listStyle, bubbleDate, showBubble, style: bubbleDateStyle } = useDateScroll(); const [hasNewMessages, setHasNewMessages] = useState(false); @@ -233,37 +231,6 @@ const RoomBody = (): ReactElement => { }; }, [sendToBottomIfNecessary]); - useEffect(() => { - const wrapper = wrapperRef.current; - - if (!wrapper) { - return; - } - - let lastScrollTopRef = 0; - const handleWrapperScroll = withThrottling({ wait: 100 })((event) => { - lastScrollTopRef = event.target.scrollTop; - const height = event.target.clientHeight; - const isLoading = RoomHistoryManager.isLoading(room._id); - const hasMore = RoomHistoryManager.hasMore(room._id); - const hasMoreNext = RoomHistoryManager.hasMoreNext(room._id); - - if ((isLoading === false && hasMore === true) || hasMoreNext === true) { - if (hasMore === true && lastScrollTopRef <= height / 3) { - RoomHistoryManager.getMore(room._id); - } else if (hasMoreNext === true && Math.ceil(lastScrollTopRef) >= event.target.scrollHeight - height) { - RoomHistoryManager.getMoreNext(room._id, atBottomRef); - } - } - }); - - wrapper.addEventListener('scroll', handleWrapperScroll); - - return () => { - wrapper.removeEventListener('scroll', handleWrapperScroll); - }; - }, [_isAtBottom, atBottomRef, list, room._id]); - const restoreScrollPositionRef = useRestoreScrollPosition(room._id); const handleComposerResize = useCallback((): void => { @@ -317,6 +284,8 @@ const RoomBody = (): ReactElement => { useReadMessageWindowEvents(); useQuoteMessageByUrl(); + const getMoreRef = useGetMore(room._id, atBottomRef); + const { messageListRef, messageListProps } = useMessageListNavigation(); const ref = useMergedRefs( @@ -327,6 +296,7 @@ const RoomBody = (): ReactElement => { unreadBarRef, leaderBannerRefCallback, unreadBarMessagesBoxRef, + getMoreRef, ); return ( diff --git a/apps/meteor/client/views/room/body/hooks/useGetMore.ts b/apps/meteor/client/views/room/body/hooks/useGetMore.ts new file mode 100644 index 000000000000..61596990f63b --- /dev/null +++ b/apps/meteor/client/views/room/body/hooks/useGetMore.ts @@ -0,0 +1,38 @@ +import type { MutableRefObject } from 'react'; +import { useCallback } from 'react'; + +import { RoomHistoryManager } from '../../../../../app/ui-utils/client'; +import { withThrottling } from '../../../../../lib/utils/highOrderFunctions'; + +export const useGetMore = (rid: string, atBottomRef: MutableRefObject) => { + return useCallback( + (wrapper: HTMLElement | null) => { + if (!wrapper) { + return; + } + + let lastScrollTopRef = 0; + + wrapper.addEventListener( + 'scroll', + withThrottling({ wait: 100 })((event) => { + lastScrollTopRef = event.target.scrollTop; + const height = event.target.clientHeight; + const isLoading = RoomHistoryManager.isLoading(rid); + const hasMore = RoomHistoryManager.hasMore(rid); + const hasMoreNext = RoomHistoryManager.hasMoreNext(rid); + + if ((isLoading === false && hasMore === true) || hasMoreNext === true) { + if (hasMore === true && lastScrollTopRef <= height / 3) { + RoomHistoryManager.getMore(rid); + } else if (hasMoreNext === true && Math.ceil(lastScrollTopRef) >= event.target.scrollHeight - height) { + RoomHistoryManager.getMoreNext(rid, atBottomRef); + atBottomRef.current = false; + } + } + }), + ); + }, + [atBottomRef, rid], + ); +}; From 34fd7e74cd282f6c93b5ef229b1a8bdb2d6bba4d Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 6 Mar 2024 19:49:19 -0300 Subject: [PATCH 104/207] refactor: RoomBody reorganize props, hooks, callbacks, effects in order (#31921) --- .../client/views/room/body/RoomBody.tsx | 253 ++++++------------ .../views/room/body/hooks/useGetMore.ts | 54 ++-- .../room/body/hooks/useHasNewMessages.ts | 83 ++++++ .../views/room/body/hooks/useLeaderBanner.ts | 4 +- .../room/body/hooks/useListIsAtBottom.ts | 68 ++++- .../body/hooks/useRestoreScrollPosition.ts | 4 +- .../room/body/hooks/useUnreadMessages.ts | 8 +- .../Threads/components/ThreadMessageList.tsx | 4 +- .../client/views/room/hooks/useDateScroll.ts | 4 +- 9 files changed, 260 insertions(+), 222 deletions(-) create mode 100644 apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index 6814320a99f4..1918ef0c5275 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -1,15 +1,11 @@ -import type { IMessage, IUser } from '@rocket.chat/core-typings'; -import { isEditedMessage } from '@rocket.chat/core-typings'; +import type { IUser } from '@rocket.chat/core-typings'; import { Box, Bubble } from '@rocket.chat/fuselage'; import { useMergedRefs } from '@rocket.chat/fuselage-hooks'; import { usePermission, useRole, useSetting, useTranslation, useUser, useUserPreference } from '@rocket.chat/ui-contexts'; import type { MouseEventHandler, ReactElement, UIEvent } from 'react'; -import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { memo, useCallback, useMemo, useRef } from 'react'; import { RoomRoles } from '../../../../app/models/client'; -import { RoomHistoryManager } from '../../../../app/ui-utils/client'; -import { isAtBottom } from '../../../../app/ui/client/views/app/lib/scrolling'; -import { callbacks } from '../../../../lib/callbacks'; import { isTruthy } from '../../../../lib/isTruthy'; import { CustomScrollbars } from '../../../components/CustomScrollbars'; import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout'; @@ -37,6 +33,7 @@ import UploadProgressIndicator from './UploadProgressIndicator'; import { useFileUpload } from './hooks/useFileUpload'; import { useGetMore } from './hooks/useGetMore'; import { useGoToHomeOnRemoved } from './hooks/useGoToHomeOnRemoved'; +import { useHasNewMessages } from './hooks/useHasNewMessages'; import { useLeaderBanner } from './hooks/useLeaderBanner'; import { useListIsAtBottom } from './hooks/useListIsAtBottom'; import { useQuoteMessageByUrl } from './hooks/useQuoteMessageByUrl'; @@ -46,6 +43,11 @@ import { useRetentionPolicy } from './hooks/useRetentionPolicy'; import { useHandleUnread } from './hooks/useUnreadMessages'; const RoomBody = (): ReactElement => { + const chat = useChat(); + if (!chat) { + throw new Error('No ChatContext provided'); + } + const formatDate = useFormatDate(); const t = useTranslation(); const isLayoutEmbedded = useEmbeddedLayout(); @@ -54,81 +56,12 @@ const RoomBody = (): ReactElement => { const toolbox = useRoomToolbox(); const admin = useRole('admin'); const subscription = useRoomSubscription(); - - const { callbackRef, listStyle, bubbleDate, showBubble, style: bubbleDateStyle } = useDateScroll(); - - const [hasNewMessages, setHasNewMessages] = useState(false); + const retentionPolicy = useRetentionPolicy(room); const hideFlexTab = useUserPreference('hideFlexTab') || undefined; const hideUsernames = useUserPreference('hideUsernames'); const displayAvatars = useUserPreference('displayAvatars'); - const wrapperRef = useRef(null); - - const { - ref: unreadBarRef, - messagesBoxRef: unreadBarMessagesBoxRef, - handleUnreadBarJumpToButtonClick, - handleMarkAsReadButtonClick, - counter: [unread], - } = useHandleUnread(room, subscription); - - const { atBottomRef, ref: isAtBottomCallbackRef } = useListIsAtBottom(); - - const chat = useChat(); - const { openUserCard, triggerProps } = useUserCard(); - - const { wrapperBoxRef, hideLeaderHeader, innerScrollRef: leaderBannerRefCallback } = useLeaderBanner(); - - if (!chat) { - throw new Error('No ChatContext provided'); - } - - const { - uploads, - handleUploadFiles, - handleUploadProgressClose, - targeDrop: [fileUploadTriggerProps, fileUploadOverlayProps], - } = useFileUpload(); - - const _isAtBottom = useCallback((scrollThreshold = 0) => { - const wrapper = wrapperRef.current; - - if (!wrapper) { - return false; - } - - if (isAtBottom(wrapper, scrollThreshold)) { - setHasNewMessages(false); - return true; - } - return false; - }, []); - - const sendToBottom = useCallback(() => { - wrapperRef.current?.scrollTo({ left: 30, top: wrapperRef.current?.scrollHeight }); - - setHasNewMessages(false); - }, []); - - const sendToBottomIfNecessary = useCallback(() => { - if (atBottomRef.current === true) { - sendToBottom(); - } - }, [atBottomRef, sendToBottom]); - - const handleNewMessageButtonClick = useCallback(() => { - atBottomRef.current = true; - sendToBottomIfNecessary(); - chat.composer?.focus(); - }, [atBottomRef, chat.composer, sendToBottomIfNecessary]); - - const handleJumpToRecentButtonClick = useCallback(() => { - atBottomRef.current = true; - RoomHistoryManager.clear(room._id); - RoomHistoryManager.getMoreIfIsEmpty(room._id); - }, [atBottomRef, room._id]); - const { hasMorePreviousMessages, hasMoreNextMessages, isLoadingMoreMessages } = useRoomMessages(); const allowAnonymousRead = useSetting('Accounts_AllowAnonymousRead') as boolean | undefined; @@ -155,87 +88,59 @@ const RoomBody = (): ReactElement => { const useRealName = useSetting('UI_Use_Real_Name') as boolean; - const { data: roomLeader } = useReactiveQuery(['rooms', room._id, 'leader', { not: user?._id }], () => { - const leaderRoomRole = RoomRoles.findOne({ - 'rid': room._id, - 'roles': 'leader', - 'u._id': { $ne: user?._id }, - }); - - if (!leaderRoomRole) { - return null; - } - - return { - ...leaderRoomRole.u, - name: useRealName ? leaderRoomRole.u.name || leaderRoomRole.u.username : leaderRoomRole.u.username, - }; - }); - - const handleOpenUserCard = useCallback( - (event: UIEvent, username: IUser['username']) => { - if (!username) { - return; - } - - openUserCard(event, username); - }, - [openUserCard], - ); - - const retentionPolicy = useRetentionPolicy(room); + const innerBoxRef = useRef(null); - useGoToHomeOnRemoved(room, user?._id); + const { + wrapperRef: unreadBarWrapperRef, + innerRef: unreadBarInnerRef, + handleUnreadBarJumpToButtonClick, + handleMarkAsReadButtonClick, + counter: [unread], + } = useHandleUnread(room, subscription); - useEffect(() => { - callbacks.add( - 'streamNewMessage', - (msg: IMessage) => { - if (room._id !== msg.rid || isEditedMessage(msg) || msg.tmid) { - return; - } + const { innerRef: dateScrollInnerRef, listStyle, bubbleDate, showBubble, style: bubbleDateStyle } = useDateScroll(); - if (msg.u._id === user?._id) { - sendToBottom(); - return; - } + const { innerRef: isAtBottomInnerRef, atBottomRef, sendToBottom, sendToBottomIfNecessary, isAtBottom } = useListIsAtBottom(); - if (!_isAtBottom()) { - setHasNewMessages(true); - } - }, - callbacks.priority.MEDIUM, - room._id, - ); + const { innerRef: getMoreInnerRef } = useGetMore(room._id, atBottomRef); - return () => { - callbacks.remove('streamNewMessage', room._id); - }; - }, [_isAtBottom, room._id, sendToBottom, user?._id]); + const { wrapperRef: leaderBannerWrapperRef, hideLeaderHeader, innerRef: leaderBannerInnerRef } = useLeaderBanner(); - useEffect(() => { - const messageList = wrapperRef.current?.querySelector('ul'); + const { + uploads, + handleUploadFiles, + handleUploadProgressClose, + targeDrop: [fileUploadTriggerProps, fileUploadOverlayProps], + } = useFileUpload(); - if (!messageList) { - return; - } + const { innerRef: restoreScrollPositionInnerRef } = useRestoreScrollPosition(room._id); - const observer = new ResizeObserver(() => { - sendToBottomIfNecessary(); - }); + const { messageListRef, messageListProps } = useMessageListNavigation(); - observer.observe(messageList); + const innerRef = useMergedRefs( + dateScrollInnerRef, + innerBoxRef, + restoreScrollPositionInnerRef, + isAtBottomInnerRef, + leaderBannerInnerRef, + unreadBarInnerRef, + getMoreInnerRef, - return () => { - observer?.disconnect(); - }; - }, [sendToBottomIfNecessary]); + messageListRef, + ); - const restoreScrollPositionRef = useRestoreScrollPosition(room._id); + const wrapperBoxRefs = useMergedRefs(unreadBarWrapperRef, leaderBannerWrapperRef); - const handleComposerResize = useCallback((): void => { - sendToBottomIfNecessary(); - }, [sendToBottomIfNecessary]); + const { handleNewMessageButtonClick, handleJumpToRecentButtonClick, handleComposerResize, hasNewMessages } = useHasNewMessages( + room._id, + user?._id, + atBottomRef, + { + sendToBottom, + sendToBottomIfNecessary, + isAtBottom, + }, + ); const handleNavigateToPreviousMessage = useCallback((): void => { chat.messageEditing.toPreviousMessage(); @@ -245,10 +150,6 @@ const RoomBody = (): ReactElement => { chat.messageEditing.toNextMessage(); }, [chat.messageEditing]); - useEffect(() => { - chat.uploads.wipeFailedOnes(); - }, [chat]); - const handleCloseFlexTab: MouseEventHandler = useCallback( (e): void => { /* @@ -281,23 +182,39 @@ const RoomBody = (): ReactElement => { [toolbox], ); + const { openUserCard, triggerProps } = useUserCard(); + + const handleOpenUserCard = useCallback( + (event: UIEvent, username: IUser['username']) => { + if (!username) { + return; + } + + openUserCard(event, username); + }, + [openUserCard], + ); + + useGoToHomeOnRemoved(room, user?._id); useReadMessageWindowEvents(); useQuoteMessageByUrl(); - const getMoreRef = useGetMore(room._id, atBottomRef); + const { data: roomLeader } = useReactiveQuery(['rooms', room._id, 'leader', { not: user?._id }], () => { + const leaderRoomRole = RoomRoles.findOne({ + 'rid': room._id, + 'roles': 'leader', + 'u._id': { $ne: user?._id }, + }); - const { messageListRef, messageListProps } = useMessageListNavigation(); + if (!leaderRoomRole) { + return null; + } - const ref = useMergedRefs( - callbackRef, - wrapperRef, - restoreScrollPositionRef, - isAtBottomCallbackRef, - unreadBarRef, - leaderBannerRefCallback, - unreadBarMessagesBoxRef, - getMoreRef, - ); + return { + ...leaderRoomRole.u, + name: useRealName ? leaderRoomRole.u.name || leaderRoomRole.u.username : leaderRoomRole.u.username, + }; + }); return ( <> @@ -309,7 +226,7 @@ const RoomBody = (): ReactElement => { onClick={hideFlexTab && handleCloseFlexTab} >
            -
            +
            {uploads.map((upload) => ( @@ -370,14 +287,8 @@ const RoomBody = (): ReactElement => { .join(' ')} > - -
              + +
                {canPreview ? ( <> {hasMorePreviousMessages ? ( @@ -390,7 +301,7 @@ const RoomBody = (): ReactElement => { )} ) : null} - + {hasMoreNextMessages ? (
              • {isLoadingMoreMessages ? : null}
              • ) : null} diff --git a/apps/meteor/client/views/room/body/hooks/useGetMore.ts b/apps/meteor/client/views/room/body/hooks/useGetMore.ts index 61596990f63b..c2f182e5131f 100644 --- a/apps/meteor/client/views/room/body/hooks/useGetMore.ts +++ b/apps/meteor/client/views/room/body/hooks/useGetMore.ts @@ -5,34 +5,36 @@ import { RoomHistoryManager } from '../../../../../app/ui-utils/client'; import { withThrottling } from '../../../../../lib/utils/highOrderFunctions'; export const useGetMore = (rid: string, atBottomRef: MutableRefObject) => { - return useCallback( - (wrapper: HTMLElement | null) => { - if (!wrapper) { - return; - } + return { + innerRef: useCallback( + (wrapper: HTMLElement | null) => { + if (!wrapper) { + return; + } - let lastScrollTopRef = 0; + let lastScrollTopRef = 0; - wrapper.addEventListener( - 'scroll', - withThrottling({ wait: 100 })((event) => { - lastScrollTopRef = event.target.scrollTop; - const height = event.target.clientHeight; - const isLoading = RoomHistoryManager.isLoading(rid); - const hasMore = RoomHistoryManager.hasMore(rid); - const hasMoreNext = RoomHistoryManager.hasMoreNext(rid); + wrapper.addEventListener( + 'scroll', + withThrottling({ wait: 100 })((event) => { + lastScrollTopRef = event.target.scrollTop; + const height = event.target.clientHeight; + const isLoading = RoomHistoryManager.isLoading(rid); + const hasMore = RoomHistoryManager.hasMore(rid); + const hasMoreNext = RoomHistoryManager.hasMoreNext(rid); - if ((isLoading === false && hasMore === true) || hasMoreNext === true) { - if (hasMore === true && lastScrollTopRef <= height / 3) { - RoomHistoryManager.getMore(rid); - } else if (hasMoreNext === true && Math.ceil(lastScrollTopRef) >= event.target.scrollHeight - height) { - RoomHistoryManager.getMoreNext(rid, atBottomRef); - atBottomRef.current = false; + if ((isLoading === false && hasMore === true) || hasMoreNext === true) { + if (hasMore === true && lastScrollTopRef <= height / 3) { + RoomHistoryManager.getMore(rid); + } else if (hasMoreNext === true && Math.ceil(lastScrollTopRef) >= event.target.scrollHeight - height) { + RoomHistoryManager.getMoreNext(rid, atBottomRef); + atBottomRef.current = false; + } } - } - }), - ); - }, - [atBottomRef, rid], - ); + }), + ); + }, + [atBottomRef, rid], + ), + }; }; diff --git a/apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts b/apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts new file mode 100644 index 000000000000..653f7b212fa6 --- /dev/null +++ b/apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts @@ -0,0 +1,83 @@ +import type { IMessage } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; +import type { MutableRefObject } from 'react'; +import { useCallback, useEffect, useState } from 'react'; + +import { RoomHistoryManager } from '../../../../../app/ui-utils/client'; +import { callbacks } from '../../../../../lib/callbacks'; +import { useChat } from '../../contexts/ChatContext'; + +export const useHasNewMessages = ( + rid: string, + uid: string | undefined, + atBottomRef: MutableRefObject, + { + sendToBottom, + sendToBottomIfNecessary, + isAtBottom, + }: { + sendToBottom: () => void; + sendToBottomIfNecessary: () => void; + isAtBottom: (threshold?: number) => boolean; + }, +) => { + const chat = useChat(); + + if (!chat) { + throw new Error('No ChatContext provided'); + } + + const [hasNewMessages, setHasNewMessages] = useState(false); + + const handleNewMessageButtonClick = useCallback(() => { + atBottomRef.current = true; + sendToBottomIfNecessary(); + setHasNewMessages(false); + chat.composer?.focus(); + }, [atBottomRef, chat.composer, sendToBottomIfNecessary]); + + const handleJumpToRecentButtonClick = useCallback(() => { + atBottomRef.current = true; + RoomHistoryManager.clear(rid); + RoomHistoryManager.getMoreIfIsEmpty(rid); + }, [atBottomRef, rid]); + + const handleComposerResize = useCallback((): void => { + sendToBottomIfNecessary(); + setHasNewMessages(false); + }, [sendToBottomIfNecessary]); + + useEffect(() => { + callbacks.add( + 'streamNewMessage', + (msg: IMessage) => { + if (rid !== msg.rid || isEditedMessage(msg) || msg.tmid) { + return; + } + + if (msg.u._id === uid) { + sendToBottom(); + setHasNewMessages(false); + return; + } + + if (!isAtBottom()) { + setHasNewMessages(true); + } + }, + callbacks.priority.MEDIUM, + rid, + ); + + return () => { + callbacks.remove('streamNewMessage', rid); + }; + }, [isAtBottom, rid, sendToBottom, uid]); + + return { + handleNewMessageButtonClick, + handleJumpToRecentButtonClick, + handleComposerResize, + hasNewMessages, + }; +}; diff --git a/apps/meteor/client/views/room/body/hooks/useLeaderBanner.ts b/apps/meteor/client/views/room/body/hooks/useLeaderBanner.ts index a899d66f42cb..a37748633557 100644 --- a/apps/meteor/client/views/room/body/hooks/useLeaderBanner.ts +++ b/apps/meteor/client/views/room/body/hooks/useLeaderBanner.ts @@ -32,8 +32,8 @@ export const useLeaderBanner = () => { }, []); return { - wrapperBoxRef, + wrapperRef: wrapperBoxRef, hideLeaderHeader, - innerScrollRef, + innerRef: innerScrollRef, }; }; diff --git a/apps/meteor/client/views/room/body/hooks/useListIsAtBottom.ts b/apps/meteor/client/views/room/body/hooks/useListIsAtBottom.ts index a37155b4cb54..221582988ef9 100644 --- a/apps/meteor/client/views/room/body/hooks/useListIsAtBottom.ts +++ b/apps/meteor/client/views/room/body/hooks/useListIsAtBottom.ts @@ -1,29 +1,69 @@ +import { useMergedRefs } from '@rocket.chat/fuselage-hooks'; import { useCallback, useRef } from 'react'; -import { isAtBottom } from '../../../../../app/ui/client/views/app/lib/scrolling'; +import { isAtBottom as isAtBottomLib } from '../../../../../app/ui/client/views/app/lib/scrolling'; import { withThrottling } from '../../../../../lib/utils/highOrderFunctions'; export const useListIsAtBottom = () => { const atBottomRef = useRef(true); - const ref = useCallback((node: HTMLElement | null) => { - if (!node) { - return; + const innerBoxRef = useRef(null); + + const sendToBottom = useCallback(() => { + innerBoxRef.current?.scrollTo({ left: 30, top: innerBoxRef.current?.scrollHeight }); + }, []); + + const sendToBottomIfNecessary = useCallback(() => { + if (atBottomRef.current === true) { + sendToBottom(); } + }, [atBottomRef, sendToBottom]); - node.addEventListener( - 'scroll', - withThrottling({ wait: 100 })(() => { - atBottomRef.current = isAtBottom(node, 100); - }), - { - passive: true, - }, - ); + const isAtBottom = useCallback((threshold = 0) => { + if (!innerBoxRef.current) { + return true; + } + return isAtBottomLib(innerBoxRef.current, threshold); }, []); + const ref = useCallback( + (node: HTMLElement | null) => { + if (!node) { + return; + } + + const messageList = node.querySelector('ul'); + + if (!messageList) { + return; + } + + const observer = new ResizeObserver(() => { + if (atBottomRef.current === true) { + node.scrollTo({ left: 30, top: node.scrollHeight }); + } + }); + + observer.observe(messageList); + + node.addEventListener( + 'scroll', + withThrottling({ wait: 100 })(() => { + atBottomRef.current = isAtBottom(100); + }), + { + passive: true, + }, + ); + }, + [isAtBottom], + ); + return { atBottomRef, - ref, + innerRef: useMergedRefs(ref, innerBoxRef) as unknown as React.MutableRefObject, + sendToBottom, + sendToBottomIfNecessary, + isAtBottom, }; }; diff --git a/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts b/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts index e9811bc5b98b..231fb81ab852 100644 --- a/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts +++ b/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts @@ -48,5 +48,7 @@ export function useRestoreScrollPosition(roomId: IRoom['_id']) { [roomId], ); - return useMergedRefs(refCallback, ref) as unknown as RefObject; + return { + innerRef: useMergedRefs(refCallback, ref) as unknown as RefObject, + }; } diff --git a/apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts b/apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts index 6ecf5a02d2b9..2a47f18a16bd 100644 --- a/apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts +++ b/apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts @@ -37,8 +37,8 @@ export const useHandleUnread = ( room: IRoom, subscription?: ISubscription, ): { - ref: (wrapper: HTMLDivElement | null) => void; - messagesBoxRef: React.MutableRefObject; + wrapperRef: (wrapper: HTMLDivElement | null) => void; + innerRef: React.MutableRefObject; handleUnreadBarJumpToButtonClick: () => void; handleMarkAsReadButtonClick: () => void; counter: readonly [number, Date | undefined]; @@ -171,8 +171,8 @@ export const useHandleUnread = ( ); return { - ref, - messagesBoxRef, + wrapperRef: ref, + innerRef: messagesBoxRef, handleUnreadBarJumpToButtonClick, handleMarkAsReadButtonClick, counter: [unread?.count ?? 0, unread?.since] as const, diff --git a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx index c515f88aaae6..1419f8ba939f 100644 --- a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx +++ b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx @@ -50,7 +50,7 @@ type ThreadMessageListProps = { const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElement => { const formatDate = useFormatDate(); - const { callbackRef, listStyle, bubbleDate, showBubble, style: bubbleDateStyle } = useDateScroll(); + const { innerRef, listStyle, bubbleDate, showBubble, style: bubbleDateStyle } = useDateScroll(); const { messages, loading } = useLegacyThreadMessages(mainMessage._id); const { @@ -68,7 +68,7 @@ const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElemen const { messageListRef, messageListProps } = useMessageListNavigation(); const listRef = useMergedRefs(listScrollRef, listJumpRef, messageListRef); - const scrollRef = useMergedRefs(callbackRef, listWrapperScrollRef); + const scrollRef = useMergedRefs(innerRef, listWrapperScrollRef); return (
                diff --git a/apps/meteor/client/views/room/hooks/useDateScroll.ts b/apps/meteor/client/views/room/hooks/useDateScroll.ts index c10aa3bf9640..252de3f92ef9 100644 --- a/apps/meteor/client/views/room/hooks/useDateScroll.ts +++ b/apps/meteor/client/views/room/hooks/useDateScroll.ts @@ -7,7 +7,7 @@ import { useDateListController } from '../providers/DateListProvider'; type useDateScrollReturn = { bubbleDate: string | undefined; - callbackRef: (node: HTMLElement | null) => void; + innerRef: (node: HTMLElement | null) => void; style?: ReturnType; showBubble: boolean; listStyle?: ReturnType; @@ -116,5 +116,5 @@ export const useDateScroll = (margin = 8): useDateScrollReturn => { ` : undefined; - return { callbackRef, listStyle, bubbleDate: bubbleDate.date, style: bubbleDate.style, showBubble: Boolean(bubbleDate.show) }; + return { innerRef: callbackRef, listStyle, bubbleDate: bubbleDate.date, style: bubbleDate.style, showBubble: Boolean(bubbleDate.show) }; }; From a6802cacafa37a9e40e22ca7af1bf898a46239dc Mon Sep 17 00:00:00 2001 From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Thu, 7 Mar 2024 00:50:54 -0300 Subject: [PATCH 105/207] chore: Remove references to EE code from the app events (#31775) Co-authored-by: Guilherme Gazzo --- .../authentication/server/startup/index.js | 8 +-- .../app/file-upload/server/lib/FileUpload.ts | 4 +- .../app/lib/server/functions/addUserToRoom.ts | 6 +- .../lib/server/functions/createDirectRoom.ts | 11 ++-- .../app/lib/server/functions/createRoom.ts | 11 ++-- .../app/lib/server/functions/deleteMessage.ts | 10 ++-- .../server/functions/removeUserFromRoom.ts | 6 +- .../app/lib/server/functions/saveUser.js | 4 +- .../app/lib/server/functions/sendMessage.ts | 2 +- .../app/lib/server/functions/updateMessage.ts | 35 +++++------ .../server/methods/deleteUserOwnAccount.ts | 4 +- apps/meteor/app/livechat/server/lib/Helper.ts | 10 ++-- .../app/livechat/server/lib/LivechatTyped.ts | 10 ++-- .../app/livechat/server/lib/RoutingManager.ts | 4 +- apps/meteor/app/mailer/server/api.ts | 4 +- .../app/message-pin/server/pinMessage.ts | 6 +- .../app/message-star/server/starMessage.ts | 4 +- .../app/reactions/server/setReaction.ts | 4 +- .../server/lib/getAppsStatistics.js | 15 ++--- .../threads/server/methods/followMessage.ts | 4 +- .../threads/server/methods/unfollowMessage.ts | 4 +- apps/meteor/ee/server/apps/index.ts | 2 +- apps/meteor/ee/server/apps/orchestrator.js | 4 +- .../server/lib/moderation/reportMessage.ts | 4 +- apps/meteor/server/methods/deleteUser.ts | 4 +- apps/meteor/server/methods/eraseRoom.ts | 6 +- apps/meteor/server/methods/logoutCleanUp.ts | 4 +- apps/meteor/server/methods/reportMessage.ts | 4 +- apps/meteor/server/methods/saveUserProfile.ts | 4 +- .../server/services/apps-engine/service.ts | 58 ++++++++++--------- .../services/video-conference/service.ts | 4 +- apps/meteor/server/startup/migrations/v291.ts | 11 ++-- apps/meteor/server/startup/migrations/v292.ts | 6 +- apps/meteor/server/startup/migrations/v294.ts | 6 +- packages/apps/src/AppsEngine.ts | 3 + packages/apps/src/IAppServerOrchestrator.ts | 8 +++ packages/apps/src/bridges/IListenerBridge.ts | 48 +++++++++++++++ packages/apps/src/index.ts | 3 + packages/apps/src/orchestrator.ts | 7 +++ yarn.lock | 4 +- 40 files changed, 223 insertions(+), 133 deletions(-) create mode 100644 packages/apps/src/bridges/IListenerBridge.ts create mode 100644 packages/apps/src/orchestrator.ts diff --git a/apps/meteor/app/authentication/server/startup/index.js b/apps/meteor/app/authentication/server/startup/index.js index e3b97c1aae88..ab622be95d53 100644 --- a/apps/meteor/app/authentication/server/startup/index.js +++ b/apps/meteor/app/authentication/server/startup/index.js @@ -1,3 +1,4 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import { Roles, Settings, Users } from '@rocket.chat/models'; import { escapeRegExp, escapeHTML } from '@rocket.chat/string-helpers'; import { Accounts } from 'meteor/accounts-base'; @@ -5,7 +6,6 @@ import { Match } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; -import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; import { callbacks } from '../../../../lib/callbacks'; import { beforeCreateUserCallback } from '../../../../lib/callbacks/beforeCreateUserCallback'; import { parseCSV } from '../../../../lib/utils/parseCSV'; @@ -350,8 +350,8 @@ const insertUserDocAsync = async function (options, user) { if (!options.skipAppsEngineEvent) { // `post` triggered events don't need to wait for the promise to resolve - Apps.triggerEvent(AppEvents.IPostUserCreated, { user, performedBy: await safeGetMeteorUser() }).catch((e) => { - Apps.getRocketChatLogger().error('Error while executing post user created event:', e); + Apps?.triggerEvent(AppEvents.IPostUserCreated, { user, performedBy: await safeGetMeteorUser() }).catch((e) => { + Apps?.getRocketChatLogger().error('Error while executing post user created event:', e); }); } @@ -424,7 +424,7 @@ const validateLoginAttemptAsync = async function (login) { */ if (login.type !== 'resume') { // App IPostUserLoggedIn event hook - await Apps.triggerEvent(AppEvents.IPostUserLoggedIn, login.user); + await Apps?.triggerEvent(AppEvents.IPostUserLoggedIn, login.user); } return true; diff --git a/apps/meteor/app/file-upload/server/lib/FileUpload.ts b/apps/meteor/app/file-upload/server/lib/FileUpload.ts index e512e5d09bfe..342d541f01bf 100644 --- a/apps/meteor/app/file-upload/server/lib/FileUpload.ts +++ b/apps/meteor/app/file-upload/server/lib/FileUpload.ts @@ -8,6 +8,7 @@ import stream from 'stream'; import URL from 'url'; import { hashLoginToken } from '@rocket.chat/account-utils'; +import { Apps, AppEvents } from '@rocket.chat/apps'; import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; import type { IUpload } from '@rocket.chat/core-typings'; import { Users, Avatars, UserDataFiles, Uploads, Settings, Subscriptions, Messages, Rooms } from '@rocket.chat/models'; @@ -21,7 +22,6 @@ import sharp from 'sharp'; import type { WritableStreamBuffer } from 'stream-buffers'; import streamBuffers from 'stream-buffers'; -import { AppEvents, Apps } from '../../../../ee/server/apps'; import { i18n } from '../../../../server/lib/i18n'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; @@ -177,7 +177,7 @@ export const FileUpload = { // App IPreFileUpload event hook try { - await Apps.triggerEvent(AppEvents.IPreFileUpload, { file, content: content || Buffer.from([]) }); + await Apps?.triggerEvent(AppEvents.IPreFileUpload, { file, content: content || Buffer.from([]) }); } catch (error: any) { if (error.name === AppsEngineException.name) { throw new Meteor.Error('error-app-prevented', error.message); diff --git a/apps/meteor/app/lib/server/functions/addUserToRoom.ts b/apps/meteor/app/lib/server/functions/addUserToRoom.ts index 4e29576cf3bb..4a70943d28e2 100644 --- a/apps/meteor/app/lib/server/functions/addUserToRoom.ts +++ b/apps/meteor/app/lib/server/functions/addUserToRoom.ts @@ -1,3 +1,4 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; import { Message, Team } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; @@ -5,7 +6,6 @@ import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; -import { AppEvents, Apps } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { getSubscriptionAutotranslateDefaultConfig } from '../../../../server/lib/getSubscriptionAutotranslateDefaultConfig'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; @@ -54,7 +54,7 @@ export const addUserToRoom = async function ( } try { - await Apps.triggerEvent(AppEvents.IPreRoomUserJoined, room, userToBeAdded, inviter); + await Apps?.triggerEvent(AppEvents.IPreRoomUserJoined, room, userToBeAdded, inviter); } catch (error: any) { if (error.name === AppsEngineException.name) { throw new Meteor.Error('error-app-prevented', error.message); @@ -118,7 +118,7 @@ export const addUserToRoom = async function ( // Keep the current event await callbacks.run('afterJoinRoom', userToBeAdded, room); - void Apps.triggerEvent(AppEvents.IPostRoomUserJoined, room, userToBeAdded, inviter); + void Apps?.triggerEvent(AppEvents.IPostRoomUserJoined, room, userToBeAdded, inviter); }); } diff --git a/apps/meteor/app/lib/server/functions/createDirectRoom.ts b/apps/meteor/app/lib/server/functions/createDirectRoom.ts index b8383875444f..cf9d2fdb7042 100644 --- a/apps/meteor/app/lib/server/functions/createDirectRoom.ts +++ b/apps/meteor/app/lib/server/functions/createDirectRoom.ts @@ -1,3 +1,4 @@ +import { AppEvents, Apps } from '@rocket.chat/apps'; import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; import type { ISubscriptionExtraData } from '@rocket.chat/core-services'; import type { ICreatedRoom, IRoom, ISubscription, IUser } from '@rocket.chat/core-typings'; @@ -6,7 +7,6 @@ import { Random } from '@rocket.chat/random'; import { Meteor } from 'meteor/meteor'; import type { MatchKeysAndValues } from 'mongodb'; -import { Apps } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { isTruthy } from '../../../../lib/isTruthy'; import { settings } from '../../../settings/server'; @@ -104,7 +104,7 @@ export async function createDirectRoom( _USERNAMES: usernames, }; - const prevent = await Apps.triggerEvent('IPreRoomCreatePrevent', tmpRoom).catch((error) => { + const prevent = await Apps?.triggerEvent(AppEvents.IPreRoomCreatePrevent, tmpRoom).catch((error) => { if (error.name === AppsEngineException.name) { throw new Meteor.Error('error-app-prevented', error.message); } @@ -116,7 +116,10 @@ export async function createDirectRoom( throw new Meteor.Error('error-app-prevented', 'A Rocket.Chat App prevented the room creation.'); } - const result = await Apps.triggerEvent('IPreRoomCreateModify', await Apps.triggerEvent('IPreRoomCreateExtend', tmpRoom)); + const result = await Apps?.triggerEvent( + AppEvents.IPreRoomCreateModify, + await Apps?.triggerEvent(AppEvents.IPreRoomCreateExtend, tmpRoom), + ); if (typeof result === 'object') { Object.assign(roomInfo, result); @@ -170,7 +173,7 @@ export async function createDirectRoom( await callbacks.run('afterCreateDirectRoom', insertedRoom, { members: roomMembers, creatorId: options?.creator }); - void Apps.triggerEvent('IPostRoomCreate', insertedRoom); + void Apps?.triggerEvent(AppEvents.IPostRoomCreate, insertedRoom); } return { diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index 3004dcd445ca..517d794e68d3 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -1,4 +1,5 @@ /* eslint-disable complexity */ +import { AppEvents, Apps } from '@rocket.chat/apps'; import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; import { Message, Team } from '@rocket.chat/core-services'; import type { ICreateRoomParams, ISubscriptionExtraData } from '@rocket.chat/core-services'; @@ -6,7 +7,6 @@ import type { ICreatedRoom, IUser, IRoom, RoomType } from '@rocket.chat/core-typ import { Rooms, Subscriptions, Users } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; -import { Apps } from '../../../../ee/server/apps/orchestrator'; import { callbacks } from '../../../../lib/callbacks'; import { beforeCreateRoomCallback } from '../../../../lib/callbacks/beforeCreateRoomCallback'; import { getSubscriptionAutotranslateDefaultConfig } from '../../../../server/lib/getSubscriptionAutotranslateDefaultConfig'; @@ -197,7 +197,7 @@ export const createRoom = async ( _USERNAMES: members, }; - const prevent = await Apps.triggerEvent('IPreRoomCreatePrevent', tmp).catch((error) => { + const prevent = await Apps?.triggerEvent(AppEvents.IPreRoomCreatePrevent, tmp).catch((error) => { if (error.name === AppsEngineException.name) { throw new Meteor.Error('error-app-prevented', error.message); } @@ -209,7 +209,10 @@ export const createRoom = async ( throw new Meteor.Error('error-app-prevented', 'A Rocket.Chat App prevented the room creation.'); } - const eventResult = await Apps.triggerEvent('IPreRoomCreateModify', await Apps.triggerEvent('IPreRoomCreateExtend', tmp)); + const eventResult = await Apps?.triggerEvent( + AppEvents.IPreRoomCreateModify, + await Apps.triggerEvent(AppEvents.IPreRoomCreateExtend, tmp), + ); if (eventResult && typeof eventResult === 'object' && delete eventResult._USERNAMES) { Object.assign(roomProps, eventResult); @@ -241,7 +244,7 @@ export const createRoom = async ( callbacks.runAsync('federation.afterCreateFederatedRoom', room, { owner, originalMemberList: members }); } - void Apps.triggerEvent('IPostRoomCreate', room); + void Apps?.triggerEvent(AppEvents.IPostRoomCreate, room); return { rid: room._id, // backwards compatible inserted: true, diff --git a/apps/meteor/app/lib/server/functions/deleteMessage.ts b/apps/meteor/app/lib/server/functions/deleteMessage.ts index cd4456b24514..26677bf37fff 100644 --- a/apps/meteor/app/lib/server/functions/deleteMessage.ts +++ b/apps/meteor/app/lib/server/functions/deleteMessage.ts @@ -1,9 +1,9 @@ +import { AppEvents, Apps } from '@rocket.chat/apps'; import { api } from '@rocket.chat/core-services'; import type { AtLeast, IMessage, IUser } from '@rocket.chat/core-typings'; import { Messages, Rooms, Uploads, Users, ReadReceipts } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; -import { Apps } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { broadcastMessageFromData } from '../../../../server/modules/watchers/lib/messages'; import { canDeleteMessageAsync } from '../../../authorization/server/functions/canDeleteMessage'; @@ -29,14 +29,14 @@ export const deleteMessageValidatingPermission = async (message: AtLeast { - const deletedMsg = await Messages.findOneById(message._id); + const deletedMsg: IMessage | null = await Messages.findOneById(message._id); const isThread = (deletedMsg?.tcount || 0) > 0; const keepHistory = settings.get('Message_KeepHistory') || isThread; const showDeletedStatus = settings.get('Message_ShowDeletedStatus') || isThread; const bridges = Apps?.isLoaded() && Apps.getBridges(); if (deletedMsg && bridges) { - const prevent = await bridges.getListenerBridge().messageEvent('IPreMessageDeletePrevent', deletedMsg); + const prevent = await bridges.getListenerBridge().messageEvent(AppEvents.IPreMessageDeletePrevent, deletedMsg); if (prevent) { throw new Meteor.Error('error-app-prevented-deleting', 'A Rocket.Chat App prevented the message deleting.'); } @@ -95,7 +95,7 @@ export async function deleteMessage(message: IMessage, user: IUser): Promise { const originalMessage = originalMsg || (await Messages.findOneById(message._id)); + if (!originalMessage) { + throw new Error('Invalid message ID.'); + } + + let messageData: IMessage = Object.assign({}, originalMessage, message); // For the Rocket.Chat Apps :) if (message && Apps && Apps.isLoaded()) { - const appMessage = Object.assign({}, originalMessage, message); - - const prevent = await Apps.getBridges()?.getListenerBridge().messageEvent('IPreMessageUpdatedPrevent', appMessage); + const prevent = await Apps.getBridges().getListenerBridge().messageEvent(AppEvents.IPreMessageUpdatedPrevent, messageData); if (prevent) { throw new Meteor.Error('error-app-prevented-updating', 'A Rocket.Chat App prevented the message updating.'); } - let result; - result = await Apps.getBridges()?.getListenerBridge().messageEvent('IPreMessageUpdatedExtend', appMessage); - result = await Apps.getBridges()?.getListenerBridge().messageEvent('IPreMessageUpdatedModify', result); + let result = await Apps.getBridges().getListenerBridge().messageEvent(AppEvents.IPreMessageUpdatedExtend, messageData); + result = await Apps.getBridges().getListenerBridge().messageEvent(AppEvents.IPreMessageUpdatedModify, result); if (typeof result === 'object') { - message = Object.assign(appMessage, result); + Object.assign(messageData, result); } } // If we keep history of edits, insert a new message to store history information if (settings.get('Message_KeepHistory')) { - await Messages.cloneAndSaveAsHistoryById(message._id, user as Required>); + await Messages.cloneAndSaveAsHistoryById(messageData._id, user as Required>); } - Object.assign, Omit>(message, { + Object.assign(messageData, { editedAt: new Date(), editedBy: { _id: user._id, @@ -48,17 +50,16 @@ export const updateMessage = async function ( }, }); - parseUrlsInMessage(message, previewUrls); + parseUrlsInMessage(messageData, previewUrls); - const room = await Rooms.findOneById(message.rid); + const room = await Rooms.findOneById(messageData.rid); if (!room) { return; } - // TODO remove type cast - message = await Message.beforeSave({ message: message as IMessage, room, user }); + messageData = await Message.beforeSave({ message: messageData, room, user }); - const { _id, ...editedMessage } = message; + const { _id, ...editedMessage } = messageData; if (!editedMessage.msg) { delete editedMessage.md; @@ -78,7 +79,7 @@ export const updateMessage = async function ( if (Apps?.isLoaded()) { // This returns a promise, but it won't mutate anything about the message // so, we don't really care if it is successful or fails - void Apps.getBridges()?.getListenerBridge().messageEvent('IPostMessageUpdated', message); + void Apps.getBridges()?.getListenerBridge().messageEvent(AppEvents.IPostMessageUpdated, messageData); } setImmediate(async () => { diff --git a/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts b/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts index ed9929622c6d..f30182def68e 100644 --- a/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts +++ b/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts @@ -1,3 +1,4 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import { Users } from '@rocket.chat/models'; import { SHA256 } from '@rocket.chat/sha256'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; @@ -5,7 +6,6 @@ import { Accounts } from 'meteor/accounts-base'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; -import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; import { trim } from '../../../../lib/utils/stringUtils'; import { settings } from '../../../settings/server'; import { deleteUser } from '../functions/deleteUser'; @@ -66,7 +66,7 @@ Meteor.methods({ await deleteUser(uid, confirmRelinquish); // App IPostUserDeleted event hook - await Apps.triggerEvent(AppEvents.IPostUserDeleted, { user }); + await Apps?.triggerEvent(AppEvents.IPostUserDeleted, { user }); return true; }, diff --git a/apps/meteor/app/livechat/server/lib/Helper.ts b/apps/meteor/app/livechat/server/lib/Helper.ts index 771f50724c38..24cb2dd320cc 100644 --- a/apps/meteor/app/livechat/server/lib/Helper.ts +++ b/apps/meteor/app/livechat/server/lib/Helper.ts @@ -1,3 +1,4 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import { LivechatTransferEventType } from '@rocket.chat/apps-engine/definition/livechat'; import { api, Message, Omnichannel } from '@rocket.chat/core-services'; import type { @@ -30,7 +31,6 @@ import { import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; -import { Apps, AppEvents } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { validateEmail as validatorFunc } from '../../../../lib/emailValidator'; import { i18n } from '../../../../server/lib/i18n'; @@ -118,7 +118,7 @@ export const createLivechatRoom = async ( const roomId = (await Rooms.insertOne(room)).insertedId; - void Apps.triggerEvent(AppEvents.IPostLivechatRoomStarted, room); + void Apps?.triggerEvent(AppEvents.IPostLivechatRoomStarted, room); await callbacks.run('livechat.newRoom', room); await sendMessage(guest, { t: 'livechat-started', msg: '', groupable: false }, room); @@ -274,7 +274,7 @@ export const removeAgentFromSubscription = async (rid: string, { _id, username } await Message.saveSystemMessage('ul', rid, username || '', { _id: user._id, username: user.username, name: user.name }); setImmediate(() => { - void Apps.triggerEvent(AppEvents.IPostLivechatAgentUnassigned, { room, user }); + void Apps?.triggerEvent(AppEvents.IPostLivechatAgentUnassigned, { room, user }); }); }; @@ -453,7 +453,7 @@ export const forwardRoomToAgent = async (room: IOmnichannelRoom, transferData: T } setImmediate(() => { - void Apps.triggerEvent(AppEvents.IPostLivechatRoomTransferred, { + void Apps?.triggerEvent(AppEvents.IPostLivechatRoomTransferred, { type: LivechatTransferEventType.AGENT, room: rid, from: oldServedBy?._id, @@ -483,7 +483,7 @@ export const updateChatDepartment = async ({ ]); setImmediate(() => { - void Apps.triggerEvent(AppEvents.IPostLivechatRoomTransferred, { + void Apps?.triggerEvent(AppEvents.IPostLivechatRoomTransferred, { type: LivechatTransferEventType.DEPARTMENT, room: rid, from: oldDepartmentId, diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index fb1098db4272..8442585d961f 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -1,6 +1,7 @@ import dns from 'dns'; import * as util from 'util'; +import { Apps, AppEvents } from '@rocket.chat/apps'; import { Message, VideoConf, api, Omnichannel } from '@rocket.chat/core-services'; import type { IOmnichannelRoom, @@ -43,7 +44,6 @@ import moment from 'moment-timezone'; import type { Filter, FindCursor, UpdateFilter } from 'mongodb'; import UAParser from 'ua-parser-js'; -import { Apps, AppEvents } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { trim } from '../../../../lib/utils/stringUtils'; import { i18n } from '../../../../server/lib/i18n'; @@ -330,8 +330,8 @@ class LivechatClass { * @deprecated the `AppEvents.ILivechatRoomClosedHandler` event will be removed * in the next major version of the Apps-Engine */ - void Apps.getBridges()?.getListenerBridge().livechatEvent(AppEvents.ILivechatRoomClosedHandler, newRoom); - void Apps.getBridges()?.getListenerBridge().livechatEvent(AppEvents.IPostLivechatRoomClosed, newRoom); + void Apps?.getBridges()?.getListenerBridge().livechatEvent(AppEvents.ILivechatRoomClosedHandler, newRoom); + void Apps?.getBridges()?.getListenerBridge().livechatEvent(AppEvents.IPostLivechatRoomClosed, newRoom); }); if (process.env.TEST_MODE) { await callbacks.run('livechat.closeRoom', { @@ -1420,7 +1420,7 @@ class LivechatClass { const ret = await LivechatVisitors.saveGuestById(_id, updateData); setImmediate(() => { - void Apps.triggerEvent(AppEvents.IPostLivechatGuestSaved, _id); + void Apps?.triggerEvent(AppEvents.IPostLivechatGuestSaved, _id); }); return ret; @@ -1786,7 +1786,7 @@ class LivechatClass { await LivechatRooms.saveRoomById(roomData); setImmediate(() => { - void Apps.triggerEvent(AppEvents.IPostLivechatRoomSaved, roomData._id); + void Apps?.triggerEvent(AppEvents.IPostLivechatRoomSaved, roomData._id); }); if (guestData?.name?.trim().length) { diff --git a/apps/meteor/app/livechat/server/lib/RoutingManager.ts b/apps/meteor/app/livechat/server/lib/RoutingManager.ts index 7b85c31f26ac..96621bece8b0 100644 --- a/apps/meteor/app/livechat/server/lib/RoutingManager.ts +++ b/apps/meteor/app/livechat/server/lib/RoutingManager.ts @@ -1,3 +1,4 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import { Message, Omnichannel } from '@rocket.chat/core-services'; import type { ILivechatInquiryRecord, @@ -16,7 +17,6 @@ import { LivechatInquiry, LivechatRooms, Subscriptions, Rooms, Users } from '@ro import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; -import { Apps, AppEvents } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { createLivechatSubscription, @@ -172,7 +172,7 @@ export const RoutingManager: Routing = { await dispatchAgentDelegated(rid, agent.agentId); logger.debug(`Agent ${agent.agentId} assigned to inquriy ${inquiry._id}. Instances notified`); - void Apps.getBridges()?.getListenerBridge().livechatEvent(AppEvents.IPostLivechatAgentAssigned, { room, user }); + void Apps?.getBridges()?.getListenerBridge().livechatEvent(AppEvents.IPostLivechatAgentAssigned, { room, user }); return inquiry; }, diff --git a/apps/meteor/app/mailer/server/api.ts b/apps/meteor/app/mailer/server/api.ts index b50fdfd26a2a..cc2caae74ba6 100644 --- a/apps/meteor/app/mailer/server/api.ts +++ b/apps/meteor/app/mailer/server/api.ts @@ -1,3 +1,4 @@ +import { AppEvents, Apps } from '@rocket.chat/apps'; import type { ISetting } from '@rocket.chat/core-typings'; import { Settings } from '@rocket.chat/models'; import { escapeHTML } from '@rocket.chat/string-helpers'; @@ -7,7 +8,6 @@ import { Meteor } from 'meteor/meteor'; import stripHtml from 'string-strip-html'; import _ from 'underscore'; -import { Apps } from '../../../ee/server/apps'; import { validateEmail } from '../../../lib/emailValidator'; import { strLeft, strRightBack } from '../../../lib/utils/stringUtils'; import { i18n } from '../../../server/lib/i18n'; @@ -170,7 +170,7 @@ export const sendNoWrap = async ({ const email = { to, from, replyTo, subject, html, text, headers }; - const eventResult = await Apps.triggerEvent('IPreEmailSent', { email }); + const eventResult = await Apps?.triggerEvent(AppEvents.IPreEmailSent, { email }); setImmediate(() => Email.sendAsync(eventResult || email).catch((e) => console.error(e))); }; diff --git a/apps/meteor/app/message-pin/server/pinMessage.ts b/apps/meteor/app/message-pin/server/pinMessage.ts index 1ed0a172028b..4887e3603122 100644 --- a/apps/meteor/app/message-pin/server/pinMessage.ts +++ b/apps/meteor/app/message-pin/server/pinMessage.ts @@ -1,3 +1,4 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import { Message } from '@rocket.chat/core-services'; import { isQuoteAttachment, isRegisterUser } from '@rocket.chat/core-typings'; import type { IMessage, MessageAttachment, MessageQuoteAttachment } from '@rocket.chat/core-typings'; @@ -6,7 +7,6 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; -import { Apps, AppEvents } from '../../../ee/server/apps/orchestrator'; import { isTruthy } from '../../../lib/isTruthy'; import { broadcastMessageFromData } from '../../../server/modules/watchers/lib/messages'; import { canAccessRoomAsync, roomAccessAttributes } from '../../authorization/server'; @@ -129,7 +129,7 @@ Meteor.methods({ } // App IPostMessagePinned event hook - await Apps.triggerEvent(AppEvents.IPostMessagePinned, originalMessage, await Meteor.userAsync(), originalMessage.pinned); + await Apps?.triggerEvent(AppEvents.IPostMessagePinned, originalMessage, await Meteor.userAsync(), originalMessage.pinned); const msgId = await Message.saveSystemMessage('message_pinned', originalMessage.rid, '', me, { attachments: [ @@ -216,7 +216,7 @@ Meteor.methods({ } // App IPostMessagePinned event hook - await Apps.triggerEvent(AppEvents.IPostMessagePinned, originalMessage, await Meteor.userAsync(), originalMessage.pinned); + await Apps?.triggerEvent(AppEvents.IPostMessagePinned, originalMessage, await Meteor.userAsync(), originalMessage.pinned); await Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned); if (settings.get('Message_Read_Receipt_Store_Users')) { diff --git a/apps/meteor/app/message-star/server/starMessage.ts b/apps/meteor/app/message-star/server/starMessage.ts index 8f025d920057..9f8ba75c4536 100644 --- a/apps/meteor/app/message-star/server/starMessage.ts +++ b/apps/meteor/app/message-star/server/starMessage.ts @@ -1,9 +1,9 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IMessage } from '@rocket.chat/core-typings'; import { Messages, Subscriptions, Rooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { Apps, AppEvents } from '../../../ee/server/apps/orchestrator'; import { broadcastMessageFromData } from '../../../server/modules/watchers/lib/messages'; import { canAccessRoomAsync, roomAccessAttributes } from '../../authorization/server'; import { isTheLastMessage } from '../../lib/server/functions/isTheLastMessage'; @@ -57,7 +57,7 @@ Meteor.methods({ await Rooms.updateLastMessageStar(room._id, uid, message.starred); } - await Apps.triggerEvent(AppEvents.IPostMessageStarred, message, await Meteor.userAsync(), message.starred); + await Apps?.triggerEvent(AppEvents.IPostMessageStarred, message, await Meteor.userAsync(), message.starred); await Messages.updateUserStarById(message._id, uid, message.starred); diff --git a/apps/meteor/app/reactions/server/setReaction.ts b/apps/meteor/app/reactions/server/setReaction.ts index 27fe4d36a053..ed2271a5d4d0 100644 --- a/apps/meteor/app/reactions/server/setReaction.ts +++ b/apps/meteor/app/reactions/server/setReaction.ts @@ -1,3 +1,4 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import { api } from '@rocket.chat/core-services'; import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; import { Messages, EmojiCustom, Rooms, Users } from '@rocket.chat/models'; @@ -5,7 +6,6 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; -import { AppEvents, Apps } from '../../../ee/server/apps/orchestrator'; import { callbacks } from '../../../lib/callbacks'; import { i18n } from '../../../server/lib/i18n'; import { broadcastMessageFromData } from '../../../server/modules/watchers/lib/messages'; @@ -106,7 +106,7 @@ async function setReaction(room: IRoom, user: IUser, message: IMessage, reaction isReacted = true; } - await Apps.triggerEvent(AppEvents.IPostMessageReacted, message, user, reaction, isReacted); + await Apps?.triggerEvent(AppEvents.IPostMessageReacted, message, user, reaction, isReacted); void broadcastMessageFromData({ id: message._id, diff --git a/apps/meteor/app/statistics/server/lib/getAppsStatistics.js b/apps/meteor/app/statistics/server/lib/getAppsStatistics.js index 6337b287506a..652686e6715c 100644 --- a/apps/meteor/app/statistics/server/lib/getAppsStatistics.js +++ b/apps/meteor/app/statistics/server/lib/getAppsStatistics.js @@ -1,17 +1,18 @@ +import { Apps } from '@rocket.chat/apps'; import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; -import { Apps } from '../../../../ee/server/apps'; import { Info } from '../../../utils/rocketchat.info'; export function getAppsStatistics() { return { engineVersion: Info.marketplaceApiVersion, - totalInstalled: Apps.isInitialized() && Apps.getManager().get().length, - totalActive: Apps.isInitialized() && Apps.getManager().get({ enabled: true }).length, + totalInstalled: (Apps?.isInitialized() && Apps.getManager().get().length) ?? 0, + totalActive: (Apps?.isInitialized() && Apps.getManager().get({ enabled: true }).length) ?? 0, totalFailed: - Apps.isInitialized() && - Apps.getManager() - .get({ disabled: true }) - .filter(({ app: { status } }) => status !== AppStatus.MANUALLY_DISABLED).length, + (Apps?.isInitialized() && + Apps.getManager() + .get({ disabled: true }) + .filter(({ app: { status } }) => status !== AppStatus.MANUALLY_DISABLED).length) ?? + 0, }; } diff --git a/apps/meteor/app/threads/server/methods/followMessage.ts b/apps/meteor/app/threads/server/methods/followMessage.ts index cede3dda33a7..f6bae69b1aaa 100644 --- a/apps/meteor/app/threads/server/methods/followMessage.ts +++ b/apps/meteor/app/threads/server/methods/followMessage.ts @@ -1,10 +1,10 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IMessage } from '@rocket.chat/core-typings'; import { Messages } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; -import { Apps, AppEvents } from '../../../../ee/server/apps/orchestrator'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; import { RateLimiter } from '../../../lib/server'; import { settings } from '../../../settings/server'; @@ -44,7 +44,7 @@ Meteor.methods({ const followResult = await follow({ tmid: message.tmid || message._id, uid }); const isFollowed = true; - await Apps.triggerEvent(AppEvents.IPostMessageFollowed, message, await Meteor.userAsync(), isFollowed); + await Apps?.triggerEvent(AppEvents.IPostMessageFollowed, message, await Meteor.userAsync(), isFollowed); return followResult; }, diff --git a/apps/meteor/app/threads/server/methods/unfollowMessage.ts b/apps/meteor/app/threads/server/methods/unfollowMessage.ts index c5dad1233173..b50c26508ebc 100644 --- a/apps/meteor/app/threads/server/methods/unfollowMessage.ts +++ b/apps/meteor/app/threads/server/methods/unfollowMessage.ts @@ -1,10 +1,10 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IMessage } from '@rocket.chat/core-typings'; import { Messages } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; -import { Apps, AppEvents } from '../../../../ee/server/apps/orchestrator'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; import { RateLimiter } from '../../../lib/server'; import { settings } from '../../../settings/server'; @@ -44,7 +44,7 @@ Meteor.methods({ const unfollowResult = await unfollow({ rid: message.rid, tmid: message.tmid || message._id, uid }); const isFollowed = false; - await Apps.triggerEvent(AppEvents.IPostMessageFollowed, message, await Meteor.userAsync(), isFollowed); + await Apps?.triggerEvent(AppEvents.IPostMessageFollowed, message, await Meteor.userAsync(), isFollowed); return unfollowResult; }, diff --git a/apps/meteor/ee/server/apps/index.ts b/apps/meteor/ee/server/apps/index.ts index 35f7c2cc041f..0306575e00f1 100644 --- a/apps/meteor/ee/server/apps/index.ts +++ b/apps/meteor/ee/server/apps/index.ts @@ -1,4 +1,4 @@ import './cron'; import './appRequestsCron'; -export { Apps, AppEvents } from './orchestrator'; +export { Apps } from './orchestrator'; diff --git a/apps/meteor/ee/server/apps/orchestrator.js b/apps/meteor/ee/server/apps/orchestrator.js index 84f9cb1372f3..37c31e890e89 100644 --- a/apps/meteor/ee/server/apps/orchestrator.js +++ b/apps/meteor/ee/server/apps/orchestrator.js @@ -1,5 +1,5 @@ +import { registerOrchestrator } from '@rocket.chat/apps'; import { EssentialAppDisabledException } from '@rocket.chat/apps-engine/definition/exceptions'; -import { AppInterface } from '@rocket.chat/apps-engine/definition/metadata'; import { AppManager } from '@rocket.chat/apps-engine/server/AppManager'; import { Logger } from '@rocket.chat/logger'; import { AppLogs, Apps as AppsModel, AppsPersistence } from '@rocket.chat/models'; @@ -249,8 +249,8 @@ export class AppServerOrchestrator { } } -export const AppEvents = AppInterface; export const Apps = new AppServerOrchestrator(); +registerOrchestrator(Apps); settings.watch('Apps_Framework_Source_Package_Storage_Type', (value) => { if (!Apps.isInitialized()) { diff --git a/apps/meteor/server/lib/moderation/reportMessage.ts b/apps/meteor/server/lib/moderation/reportMessage.ts index be8b917fd6f5..710ea6e1b685 100644 --- a/apps/meteor/server/lib/moderation/reportMessage.ts +++ b/apps/meteor/server/lib/moderation/reportMessage.ts @@ -1,8 +1,8 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IMessage, IUser } from '@rocket.chat/core-typings'; import { Messages, ModerationReports, Rooms, Users } from '@rocket.chat/models'; import { canAccessRoomAsync } from '../../../app/authorization/server/functions/canAccessRoom'; -import { AppEvents, Apps } from '../../../ee/server/apps'; export const reportMessage = async (messageId: IMessage['_id'], description: string, uid: IUser['_id']) => { if (!uid) { @@ -49,7 +49,7 @@ export const reportMessage = async (messageId: IMessage['_id'], description: str await ModerationReports.createWithMessageDescriptionAndUserId(message, description, roomInfo, reportedBy); - await Apps.triggerEvent(AppEvents.IPostMessageReported, message, user, description); + await Apps?.triggerEvent(AppEvents.IPostMessageReported, message, user, description); return true; }; diff --git a/apps/meteor/server/methods/deleteUser.ts b/apps/meteor/server/methods/deleteUser.ts index 4dafad7a3a0c..8762cfab2437 100644 --- a/apps/meteor/server/methods/deleteUser.ts +++ b/apps/meteor/server/methods/deleteUser.ts @@ -1,3 +1,4 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; @@ -6,7 +7,6 @@ import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../app/authorization/server/functions/hasPermission'; import { deleteUser } from '../../app/lib/server/functions/deleteUser'; -import { AppEvents, Apps } from '../../ee/server/apps/orchestrator'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -52,7 +52,7 @@ Meteor.methods({ await deleteUser(userId, confirmRelinquish, uid); // App IPostUserDeleted event hook - await Apps.triggerEvent(AppEvents.IPostUserDeleted, { user, performedBy: await Meteor.userAsync() }); + await Apps?.triggerEvent(AppEvents.IPostUserDeleted, { user, performedBy: await Meteor.userAsync() }); return true; }, diff --git a/apps/meteor/server/methods/eraseRoom.ts b/apps/meteor/server/methods/eraseRoom.ts index 177b3c23bb9c..687b9ad66992 100644 --- a/apps/meteor/server/methods/eraseRoom.ts +++ b/apps/meteor/server/methods/eraseRoom.ts @@ -1,3 +1,4 @@ +import { AppEvents, Apps } from '@rocket.chat/apps'; import { Message, Team } from '@rocket.chat/core-services'; import { Rooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; @@ -7,7 +8,6 @@ import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../app/authorization/server/functions/hasPermission'; import { deleteRoom } from '../../app/lib/server/functions/deleteRoom'; import { methodDeprecationLogger } from '../../app/lib/server/lib/deprecationWarningLogger'; -import { Apps } from '../../ee/server/apps'; import { roomCoordinator } from '../lib/rooms/roomCoordinator'; export async function eraseRoom(rid: string, uid: string): Promise { @@ -36,7 +36,7 @@ export async function eraseRoom(rid: string, uid: string): Promise { } if (Apps?.isLoaded()) { - const prevent = await Apps.getBridges()?.getListenerBridge().roomEvent('IPreRoomDeletePrevent', room); + const prevent = await Apps.getBridges()?.getListenerBridge().roomEvent(AppEvents.IPreRoomDeletePrevent, room); if (prevent) { throw new Meteor.Error('error-app-prevented-deleting', 'A Rocket.Chat App prevented the room erasing.'); } @@ -54,7 +54,7 @@ export async function eraseRoom(rid: string, uid: string): Promise { } if (Apps?.isLoaded()) { - void Apps.getBridges()?.getListenerBridge().roomEvent('IPostRoomDeleted', room); + void Apps.getBridges()?.getListenerBridge().roomEvent(AppEvents.IPostRoomDeleted, room); } } diff --git a/apps/meteor/server/methods/logoutCleanUp.ts b/apps/meteor/server/methods/logoutCleanUp.ts index 9b9af5356af5..502cad3c5fbf 100644 --- a/apps/meteor/server/methods/logoutCleanUp.ts +++ b/apps/meteor/server/methods/logoutCleanUp.ts @@ -1,9 +1,9 @@ +import { AppEvents, Apps } from '@rocket.chat/apps'; import type { IUser } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; -import { AppEvents, Apps } from '../../ee/server/apps/orchestrator'; import { afterLogoutCleanUpCallback } from '../../lib/callbacks/afterLogoutCleanUpCallback'; declare module '@rocket.chat/ui-contexts' { @@ -22,6 +22,6 @@ Meteor.methods({ }); // App IPostUserLogout event hook - await Apps.triggerEvent(AppEvents.IPostUserLoggedOut, user); + await Apps?.triggerEvent(AppEvents.IPostUserLoggedOut, user); }, }); diff --git a/apps/meteor/server/methods/reportMessage.ts b/apps/meteor/server/methods/reportMessage.ts index 94d6fc1fd315..44087dad0424 100644 --- a/apps/meteor/server/methods/reportMessage.ts +++ b/apps/meteor/server/methods/reportMessage.ts @@ -1,3 +1,4 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IMessage } from '@rocket.chat/core-typings'; import { ModerationReports, Rooms, Users, Messages } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; @@ -6,7 +7,6 @@ import { Meteor } from 'meteor/meteor'; import { canAccessRoomAsync } from '../../app/authorization/server/functions/canAccessRoom'; import { methodDeprecationLogger } from '../../app/lib/server/lib/deprecationWarningLogger'; -import { AppEvents, Apps } from '../../ee/server/apps'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -77,7 +77,7 @@ Meteor.methods({ await ModerationReports.createWithMessageDescriptionAndUserId(message, description, roomInfo, reportedBy); - await Apps.triggerEvent(AppEvents.IPostMessageReported, message, await Meteor.userAsync(), description); + await Apps?.triggerEvent(AppEvents.IPostMessageReported, message, await Meteor.userAsync(), description); return true; }, diff --git a/apps/meteor/server/methods/saveUserProfile.ts b/apps/meteor/server/methods/saveUserProfile.ts index 5bfbba5b1b3f..695742977ad3 100644 --- a/apps/meteor/server/methods/saveUserProfile.ts +++ b/apps/meteor/server/methods/saveUserProfile.ts @@ -1,3 +1,4 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import type { UserStatus } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; @@ -12,7 +13,6 @@ import { saveUserIdentity } from '../../app/lib/server/functions/saveUserIdentit import { passwordPolicy } from '../../app/lib/server/lib/passwordPolicy'; import { settings as rcSettings } from '../../app/settings/server'; import { setUserStatusMethod } from '../../app/user-status/server/methods/setUserStatus'; -import { AppEvents, Apps } from '../../ee/server/apps/orchestrator'; import { compareUserPassword } from '../lib/compareUserPassword'; import { compareUserPasswordHistory } from '../lib/compareUserPasswordHistory'; @@ -156,7 +156,7 @@ async function saveUserProfile( // App IPostUserUpdated event hook const updatedUser = await Users.findOneById(this.userId); - await Apps.triggerEvent(AppEvents.IPostUserUpdated, { user: updatedUser, previousUser: user }); + await Apps?.triggerEvent(AppEvents.IPostUserUpdated, { user: updatedUser, previousUser: user }); return true; } diff --git a/apps/meteor/server/services/apps-engine/service.ts b/apps/meteor/server/services/apps-engine/service.ts index e72ce3cbce0a..7e36a937e6a6 100644 --- a/apps/meteor/server/services/apps-engine/service.ts +++ b/apps/meteor/server/services/apps-engine/service.ts @@ -1,3 +1,4 @@ +import { Apps, AppEvents } from '@rocket.chat/apps'; import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; import { AppStatusUtils } from '@rocket.chat/apps-engine/definition/AppStatus'; import type { IAppInfo } from '@rocket.chat/apps-engine/definition/metadata'; @@ -6,7 +7,6 @@ import type { IAppStorageItem } from '@rocket.chat/apps-engine/server/storage'; import type { IAppsEngineService } from '@rocket.chat/core-services'; import { ServiceClassInternal } from '@rocket.chat/core-services'; -import { Apps, AppEvents } from '../../../ee/server/apps/orchestrator'; import { SystemLogger } from '../../lib/logger/system'; export class AppsEngineService extends ServiceClassInternal implements IAppsEngineService { @@ -16,7 +16,7 @@ export class AppsEngineService extends ServiceClassInternal implements IAppsEngi super(); this.onEvent('presence.status', async ({ user, previousStatus }): Promise => { - await Apps.triggerEvent(AppEvents.IPostUserStatusChanged, { + await Apps?.triggerEvent(AppEvents.IPostUserStatusChanged, { user, currentStatus: user.status, previousStatus, @@ -24,68 +24,70 @@ export class AppsEngineService extends ServiceClassInternal implements IAppsEngi }); this.onEvent('apps.added', async (appId: string): Promise => { - Apps.getRocketChatLogger().debug(`"apps.added" event received for app "${appId}"`); + Apps?.getRocketChatLogger().debug(`"apps.added" event received for app "${appId}"`); // if the app already exists in this instance, don't load it again - const app = Apps.getManager()?.getOneById(appId); + const app = Apps?.getManager()?.getOneById(appId); if (app) { - Apps.getRocketChatLogger().info(`"apps.added" event received for app "${appId}", but it already exists in this instance`); + Apps?.getRocketChatLogger().info(`"apps.added" event received for app "${appId}", but it already exists in this instance`); return; } - await Apps.getManager()?.addLocal(appId); + await Apps?.getManager()?.addLocal(appId); }); this.onEvent('apps.removed', async (appId: string): Promise => { - Apps.getRocketChatLogger().debug(`"apps.removed" event received for app "${appId}"`); - const app = Apps.getManager()?.getOneById(appId); + Apps?.getRocketChatLogger().debug(`"apps.removed" event received for app "${appId}"`); + const app = Apps?.getManager()?.getOneById(appId); if (!app) { - Apps.getRocketChatLogger().info(`"apps.removed" event received for app "${appId}", but it couldn't be found in this instance`); + Apps?.getRocketChatLogger().info(`"apps.removed" event received for app "${appId}", but it couldn't be found in this instance`); return; } - await Apps.getManager()?.removeLocal(appId); + await Apps?.getManager()?.removeLocal(appId); }); this.onEvent('apps.updated', async (appId: string): Promise => { - Apps.getRocketChatLogger().debug(`"apps.updated" event received for app "${appId}"`); - const storageItem = await Apps.getStorage()?.retrieveOne(appId); + Apps?.getRocketChatLogger().debug(`"apps.updated" event received for app "${appId}"`); + const storageItem = await Apps?.getStorage()?.retrieveOne(appId); if (!storageItem) { - Apps.getRocketChatLogger().info(`"apps.updated" event received for app "${appId}", but it couldn't be found in the storage`); + Apps?.getRocketChatLogger().info(`"apps.updated" event received for app "${appId}", but it couldn't be found in the storage`); return; } - const appPackage = await Apps.getAppSourceStorage()?.fetch(storageItem); + const appPackage = await Apps?.getAppSourceStorage()?.fetch(storageItem); if (!appPackage) { return; } - await Apps.getManager()?.updateLocal(storageItem, appPackage); + await Apps?.getManager()?.updateLocal(storageItem, appPackage); }); this.onEvent('apps.statusUpdate', async (appId: string, status: AppStatus): Promise => { - Apps.getRocketChatLogger().debug(`"apps.statusUpdate" event received for app "${appId}" with status "${status}"`); - const app = Apps.getManager()?.getOneById(appId); + Apps?.getRocketChatLogger().debug(`"apps.statusUpdate" event received for app "${appId}" with status "${status}"`); + const app = Apps?.getManager()?.getOneById(appId); if (!app) { - Apps.getRocketChatLogger().info(`"apps.statusUpdate" event received for app "${appId}", but it couldn't be found in this instance`); + Apps?.getRocketChatLogger().info( + `"apps.statusUpdate" event received for app "${appId}", but it couldn't be found in this instance`, + ); return; } if (app.getStatus() === status) { - Apps.getRocketChatLogger().info(`"apps.statusUpdate" event received for app "${appId}", but the status is the same`); + Apps?.getRocketChatLogger().info(`"apps.statusUpdate" event received for app "${appId}", but the status is the same`); return; } if (AppStatusUtils.isEnabled(status)) { - await Apps.getManager()?.enable(appId).catch(SystemLogger.error); + await Apps?.getManager()?.enable(appId).catch(SystemLogger.error); } else if (AppStatusUtils.isDisabled(status)) { - await Apps.getManager()?.disable(appId, status, true).catch(SystemLogger.error); + await Apps?.getManager()?.disable(appId, status, true).catch(SystemLogger.error); } }); this.onEvent('apps.settingUpdated', async (appId: string, setting): Promise => { - Apps.getRocketChatLogger().debug(`"apps.settingUpdated" event received for app "${appId}"`, { setting }); - const app = Apps.getManager()?.getOneById(appId); + Apps?.getRocketChatLogger().debug(`"apps.settingUpdated" event received for app "${appId}"`, { setting }); + const app = Apps?.getManager()?.getOneById(appId); const oldSetting = app?.getStorageItem().settings[setting.id].value; // avoid updating the setting if the value is the same, @@ -94,30 +96,30 @@ export class AppsEngineService extends ServiceClassInternal implements IAppsEngi // so we need to convert it to JSON stringified to compare it if (JSON.stringify(oldSetting) === JSON.stringify(setting.value)) { - Apps.getRocketChatLogger().info( + Apps?.getRocketChatLogger().info( `"apps.settingUpdated" event received for setting ${setting.id} of app "${appId}", but the setting value is the same`, ); return; } - await Apps.getManager() + await Apps?.getManager() ?.getSettingsManager() .updateAppSetting(appId, setting as any); }); } isInitialized(): boolean { - return Apps.isInitialized(); + return Boolean(Apps?.isInitialized()); } async getApps(query: IGetAppsFilter): Promise { - return Apps.getManager() + return Apps?.getManager() ?.get(query) .map((app) => app.getApp().getInfo()); } async getAppStorageItemById(appId: string): Promise { - const app = Apps.getManager()?.getOneById(appId); + const app = Apps?.getManager()?.getOneById(appId); if (!app) { return; diff --git a/apps/meteor/server/services/video-conference/service.ts b/apps/meteor/server/services/video-conference/service.ts index 90a7a3302427..87fe279d0d94 100644 --- a/apps/meteor/server/services/video-conference/service.ts +++ b/apps/meteor/server/services/video-conference/service.ts @@ -1,3 +1,4 @@ +import { Apps } from '@rocket.chat/apps'; import type { AppVideoConfProviderManager } from '@rocket.chat/apps-engine/server/managers'; import type { IVideoConfService, VideoConferenceJoinOptions } from '@rocket.chat/core-services'; import { api, ServiceClassInternal } from '@rocket.chat/core-services'; @@ -41,7 +42,6 @@ import { settings } from '../../../app/settings/server'; import { updateCounter } from '../../../app/statistics/server/functions/updateStatsCounter'; import { getUserAvatarURL } from '../../../app/utils/server/getUserAvatarURL'; import { getUserPreference } from '../../../app/utils/server/lib/getUserPreference'; -import { Apps } from '../../../ee/server/apps'; import { callbacks } from '../../../lib/callbacks'; import { availabilityErrors } from '../../../lib/videoConference/constants'; import { readSecondaryPreferred } from '../../database/readSecondaryPreferred'; @@ -832,7 +832,7 @@ export class VideoConfService extends ServiceClassInternal implements IVideoConf throw new Error('apps-engine-not-loaded'); } - const manager = Apps.getManager()?.getVideoConfProviderManager(); + const manager = Apps?.getManager()?.getVideoConfProviderManager(); if (!manager) { throw new Error(availabilityErrors.NO_APP); } diff --git a/apps/meteor/server/startup/migrations/v291.ts b/apps/meteor/server/startup/migrations/v291.ts index 8923f3b282c3..f4fdbb743447 100644 --- a/apps/meteor/server/startup/migrations/v291.ts +++ b/apps/meteor/server/startup/migrations/v291.ts @@ -1,8 +1,7 @@ +import { Apps, type AppMetadataStorage } from '@rocket.chat/apps'; import type { IAppStorageItem } from '@rocket.chat/apps-engine/server/storage'; import { Settings } from '@rocket.chat/models'; -import { Apps } from '../../../ee/server/apps'; -import type { AppRealStorage } from '../../../ee/server/apps/storage'; import { addMigration } from '../../lib/migrations'; addMigration({ @@ -13,13 +12,17 @@ addMigration({ await Settings.removeById('Apps_Framework_Development_Mode'); await Settings.removeById('Apps_Framework_enabled'); + if (!Apps) { + throw new Error('Apps Orchestrator not registered.'); + } + Apps.initialize(); - const appsStorage = Apps.getStorage() as AppRealStorage; + const appsStorage = Apps.getStorage(); const apps = await appsStorage.retrieveAll(); - const promises: Array> = []; + const promises: Array> = []; apps.forEach((app) => promises.push( diff --git a/apps/meteor/server/startup/migrations/v292.ts b/apps/meteor/server/startup/migrations/v292.ts index 7f590f4038e2..beec6967a904 100644 --- a/apps/meteor/server/startup/migrations/v292.ts +++ b/apps/meteor/server/startup/migrations/v292.ts @@ -1,7 +1,7 @@ +import { Apps } from '@rocket.chat/apps'; import type { AppSignatureManager } from '@rocket.chat/apps-engine/server/managers/AppSignatureManager'; import type { IAppStorageItem } from '@rocket.chat/apps-engine/server/storage'; -import { Apps } from '../../../ee/server/apps'; import type { AppRealStorage } from '../../../ee/server/apps/storage'; import { addMigration } from '../../lib/migrations'; @@ -9,6 +9,10 @@ addMigration({ version: 292, name: 'Add checksum signature to existing apps', async up() { + if (!Apps) { + throw new Error('Apps Orchestrator not registered.'); + } + Apps.initialize(); const sigMan = Apps.getManager()?.getSignatureManager() as AppSignatureManager; diff --git a/apps/meteor/server/startup/migrations/v294.ts b/apps/meteor/server/startup/migrations/v294.ts index abcb20d079ec..832043740f89 100644 --- a/apps/meteor/server/startup/migrations/v294.ts +++ b/apps/meteor/server/startup/migrations/v294.ts @@ -1,13 +1,17 @@ +import { Apps } from '@rocket.chat/apps'; import type { AppSignatureManager } from '@rocket.chat/apps-engine/server/managers/AppSignatureManager'; import type { IAppStorageItem } from '@rocket.chat/apps-engine/server/storage'; -import { Apps } from '../../../ee/server/apps'; import type { AppRealStorage } from '../../../ee/server/apps/storage'; import { addMigration } from '../../lib/migrations'; addMigration({ version: 294, async up() { + if (!Apps) { + throw new Error('Apps Orchestrator not registered.'); + } + Apps.initialize(); const sigMan = Apps.getManager()?.getSignatureManager() as AppSignatureManager; diff --git a/packages/apps/src/AppsEngine.ts b/packages/apps/src/AppsEngine.ts index 117e93c0ec2f..856bc1253790 100644 --- a/packages/apps/src/AppsEngine.ts +++ b/packages/apps/src/AppsEngine.ts @@ -8,6 +8,7 @@ export type { IVisitorPhone as IAppsVisitorPhone, } from '@rocket.chat/apps-engine/definition/livechat'; export type { IMessage as IAppsMessage } from '@rocket.chat/apps-engine/definition/messages'; +export { AppInterface as AppEvents } from '@rocket.chat/apps-engine/definition/metadata'; export type { IUser as IAppsUser } from '@rocket.chat/apps-engine/definition/users'; export type { IRole as IAppsRole } from '@rocket.chat/apps-engine/definition/roles'; export type { IRoom as IAppsRoom } from '@rocket.chat/apps-engine/definition/rooms'; @@ -18,3 +19,5 @@ export type { VideoConference as AppsVideoConference, } from '@rocket.chat/apps-engine/definition/videoConferences'; export { AppManager } from '@rocket.chat/apps-engine/server/AppManager'; +export { AppBridges } from '@rocket.chat/apps-engine/server/bridges'; +export { AppMetadataStorage } from '@rocket.chat/apps-engine/server/storage'; diff --git a/packages/apps/src/IAppServerOrchestrator.ts b/packages/apps/src/IAppServerOrchestrator.ts index dbfc5aee7a20..2f1f7db5d4b5 100644 --- a/packages/apps/src/IAppServerOrchestrator.ts +++ b/packages/apps/src/IAppServerOrchestrator.ts @@ -1,12 +1,16 @@ import type { AppManager } from '@rocket.chat/apps-engine/server/AppManager'; +import type { AppSourceStorage } from '@rocket.chat/apps-engine/server/storage'; import type { Logger } from '@rocket.chat/logger'; import type { IAppsPersistenceModel } from '@rocket.chat/model-typings'; +import type { AppBridges, AppEvents, AppMetadataStorage } from './AppsEngine'; import type { IAppServerNotifier } from './IAppServerNotifier'; import type { IAppConvertersMap } from './converters'; export interface IAppServerOrchestrator { initialize(): void; + isInitialized(): boolean; + isLoaded(): boolean; getNotifier(): IAppServerNotifier; isDebugging(): boolean; debugLog(...args: any[]): void; @@ -14,4 +18,8 @@ export interface IAppServerOrchestrator { getConverters(): IAppConvertersMap; getPersistenceModel(): IAppsPersistenceModel; getRocketChatLogger(): Logger; + triggerEvent(event: AppEvents, ...payload: any[]): Promise; + getBridges(): AppBridges; + getStorage(): AppMetadataStorage; + getAppSourceStorage(): AppSourceStorage; } diff --git a/packages/apps/src/bridges/IListenerBridge.ts b/packages/apps/src/bridges/IListenerBridge.ts new file mode 100644 index 000000000000..faf34118cd30 --- /dev/null +++ b/packages/apps/src/bridges/IListenerBridge.ts @@ -0,0 +1,48 @@ +import type { IMessage, IRoom, IUser, ILivechatDepartment, ILivechatVisitor, IOmnichannelRoom } from '@rocket.chat/core-typings'; + +import type { AppEvents } from '../AppsEngine'; + +declare module '@rocket.chat/apps-engine/server/bridges' { + interface IListenerBridge { + messageEvent(int: 'IPostMessageDeleted', message: IMessage, userDeleted: IUser): Promise; + messageEvent(int: 'IPostMessageReacted', message: IMessage, userReacted: IUser, reaction: string, isReacted: boolean): Promise; + messageEvent(int: 'IPostMessageFollowed', message: IMessage, userFollowed: IUser, isFollowed: boolean): Promise; + messageEvent(int: 'IPostMessagePinned', message: IMessage, userPinned: IUser, isPinned: boolean): Promise; + messageEvent(int: 'IPostMessageStarred', message: IMessage, userStarred: IUser, isStarred: boolean): Promise; + messageEvent(int: 'IPostMessageReported', message: IMessage, userReported: IUser, reason: boolean): Promise; + + messageEvent( + int: 'IPreMessageSentPrevent' | 'IPreMessageDeletePrevent' | 'IPreMessageUpdatedPrevent', + message: IMessage, + ): Promise; + messageEvent( + int: 'IPreMessageSentExtend' | 'IPreMessageSentModify' | 'IPreMessageUpdatedExtend' | 'IPreMessageUpdatedModify', + message: IMessage, + ): Promise; + messageEvent(int: 'IPostMessageSent' | 'IPostMessageUpdated', message: IMessage): Promise; + + roomEvent(int: 'IPreRoomUserJoined' | 'IPostRoomUserJoined', room: IRoom, joiningUser: IUser, invitingUser?: IUser): Promise; + roomEvent(int: 'IPreRoomUserLeave' | 'IPostRoomUserLeave', room: IRoom, leavingUser: IUser): Promise; + + roomEvent(int: 'IPreRoomCreatePrevent' | 'IPreRoomDeletePrevent', room: IRoom): Promise; + roomEvent(int: 'IPreRoomCreateExtend' | 'IPreRoomCreateModify', room: IRoom): Promise; + roomEvent(int: 'IPostRoomCreate' | 'IPostRoomDeleted', room: IRoom): Promise; + + livechatEvent( + int: 'IPostLivechatAgentAssigned' | 'IPostLivechatAgentUnassigned', + data: { user: IUser; room: IOmnichannelRoom }, + ): Promise; + livechatEvent( + int: 'IPostLivechatRoomTransferred', + data: { type: 'agent'; room: IRoom['_id']; from: IUser['_id']; to: IUser['_id'] }, + ): Promise; + livechatEvent( + int: 'IPostLivechatRoomTransferred', + data: { type: 'department'; room: IRoom['_id']; from: ILivechatDepartment['_id']; to: ILivechatDepartment['_id'] }, + ): Promise; + livechatEvent(int: 'IPostLivechatGuestSaved', data: ILivechatVisitor['_id']): Promise; + livechatEvent(int: 'IPostLivechatRoomSaved', data: IRoom['_id']): Promise; + livechatEvent(int: 'ILivechatRoomClosedHandler' | 'IPostLivechatRoomStarted' | 'IPostLivechatRoomClosed', data: IRoom): Promise; + livechatEvent(int: AppEvents | AppEvents[keyof AppEvents], data: any): Promise; + } +} diff --git a/packages/apps/src/index.ts b/packages/apps/src/index.ts index e137fa3cf007..837749af62c0 100644 --- a/packages/apps/src/index.ts +++ b/packages/apps/src/index.ts @@ -1,4 +1,7 @@ +import './bridges/IListenerBridge'; + export * from './converters'; export * from './AppsEngine'; export * from './IAppServerNotifier'; export * from './IAppServerOrchestrator'; +export * from './orchestrator'; diff --git a/packages/apps/src/orchestrator.ts b/packages/apps/src/orchestrator.ts new file mode 100644 index 000000000000..4e3a53d9d5f0 --- /dev/null +++ b/packages/apps/src/orchestrator.ts @@ -0,0 +1,7 @@ +import type { IAppServerOrchestrator } from './IAppServerOrchestrator'; + +export let Apps: IAppServerOrchestrator | undefined; + +export function registerOrchestrator(orch: IAppServerOrchestrator): void { + Apps = orch; +} diff --git a/yarn.lock b/yarn.lock index 72c94c3be6d4..41869c1761c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39995,7 +39995,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:~5.3.3": +"typescript@npm:~5.3.2, typescript@npm:~5.3.3": version: 5.3.3 resolution: "typescript@npm:5.3.3" bin: @@ -40005,7 +40005,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@~5.3.3#~builtin": +"typescript@patch:typescript@~5.3.2#~builtin, typescript@patch:typescript@~5.3.3#~builtin": version: 5.3.3 resolution: "typescript@patch:typescript@npm%3A5.3.3#~builtin::version=5.3.3&hash=85af82" bin: From 753c7b4f8a3540fa351249c79b6384796e041ed0 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Thu, 7 Mar 2024 01:27:55 -0300 Subject: [PATCH 106/207] Revert "chore: Remove references to EE code from the app events" (#31922) --- .../authentication/server/startup/index.js | 8 +-- .../app/file-upload/server/lib/FileUpload.ts | 4 +- .../app/lib/server/functions/addUserToRoom.ts | 6 +- .../lib/server/functions/createDirectRoom.ts | 11 ++-- .../app/lib/server/functions/createRoom.ts | 11 ++-- .../app/lib/server/functions/deleteMessage.ts | 10 ++-- .../server/functions/removeUserFromRoom.ts | 6 +- .../app/lib/server/functions/saveUser.js | 4 +- .../app/lib/server/functions/sendMessage.ts | 2 +- .../app/lib/server/functions/updateMessage.ts | 35 ++++++----- .../server/methods/deleteUserOwnAccount.ts | 4 +- apps/meteor/app/livechat/server/lib/Helper.ts | 10 ++-- .../app/livechat/server/lib/LivechatTyped.ts | 10 ++-- .../app/livechat/server/lib/RoutingManager.ts | 4 +- apps/meteor/app/mailer/server/api.ts | 4 +- .../app/message-pin/server/pinMessage.ts | 6 +- .../app/message-star/server/starMessage.ts | 4 +- .../app/reactions/server/setReaction.ts | 4 +- .../server/lib/getAppsStatistics.js | 15 +++-- .../threads/server/methods/followMessage.ts | 4 +- .../threads/server/methods/unfollowMessage.ts | 4 +- apps/meteor/ee/server/apps/index.ts | 2 +- apps/meteor/ee/server/apps/orchestrator.js | 4 +- .../server/lib/moderation/reportMessage.ts | 4 +- apps/meteor/server/methods/deleteUser.ts | 4 +- apps/meteor/server/methods/eraseRoom.ts | 6 +- apps/meteor/server/methods/logoutCleanUp.ts | 4 +- apps/meteor/server/methods/reportMessage.ts | 4 +- apps/meteor/server/methods/saveUserProfile.ts | 4 +- .../server/services/apps-engine/service.ts | 58 +++++++++---------- .../services/video-conference/service.ts | 4 +- apps/meteor/server/startup/migrations/v291.ts | 11 ++-- apps/meteor/server/startup/migrations/v292.ts | 6 +- apps/meteor/server/startup/migrations/v294.ts | 6 +- packages/apps/src/AppsEngine.ts | 3 - packages/apps/src/IAppServerOrchestrator.ts | 8 --- packages/apps/src/bridges/IListenerBridge.ts | 48 --------------- packages/apps/src/index.ts | 3 - packages/apps/src/orchestrator.ts | 7 --- yarn.lock | 4 +- 40 files changed, 133 insertions(+), 223 deletions(-) delete mode 100644 packages/apps/src/bridges/IListenerBridge.ts delete mode 100644 packages/apps/src/orchestrator.ts diff --git a/apps/meteor/app/authentication/server/startup/index.js b/apps/meteor/app/authentication/server/startup/index.js index ab622be95d53..e3b97c1aae88 100644 --- a/apps/meteor/app/authentication/server/startup/index.js +++ b/apps/meteor/app/authentication/server/startup/index.js @@ -1,4 +1,3 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import { Roles, Settings, Users } from '@rocket.chat/models'; import { escapeRegExp, escapeHTML } from '@rocket.chat/string-helpers'; import { Accounts } from 'meteor/accounts-base'; @@ -6,6 +5,7 @@ import { Match } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; +import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; import { callbacks } from '../../../../lib/callbacks'; import { beforeCreateUserCallback } from '../../../../lib/callbacks/beforeCreateUserCallback'; import { parseCSV } from '../../../../lib/utils/parseCSV'; @@ -350,8 +350,8 @@ const insertUserDocAsync = async function (options, user) { if (!options.skipAppsEngineEvent) { // `post` triggered events don't need to wait for the promise to resolve - Apps?.triggerEvent(AppEvents.IPostUserCreated, { user, performedBy: await safeGetMeteorUser() }).catch((e) => { - Apps?.getRocketChatLogger().error('Error while executing post user created event:', e); + Apps.triggerEvent(AppEvents.IPostUserCreated, { user, performedBy: await safeGetMeteorUser() }).catch((e) => { + Apps.getRocketChatLogger().error('Error while executing post user created event:', e); }); } @@ -424,7 +424,7 @@ const validateLoginAttemptAsync = async function (login) { */ if (login.type !== 'resume') { // App IPostUserLoggedIn event hook - await Apps?.triggerEvent(AppEvents.IPostUserLoggedIn, login.user); + await Apps.triggerEvent(AppEvents.IPostUserLoggedIn, login.user); } return true; diff --git a/apps/meteor/app/file-upload/server/lib/FileUpload.ts b/apps/meteor/app/file-upload/server/lib/FileUpload.ts index 342d541f01bf..e512e5d09bfe 100644 --- a/apps/meteor/app/file-upload/server/lib/FileUpload.ts +++ b/apps/meteor/app/file-upload/server/lib/FileUpload.ts @@ -8,7 +8,6 @@ import stream from 'stream'; import URL from 'url'; import { hashLoginToken } from '@rocket.chat/account-utils'; -import { Apps, AppEvents } from '@rocket.chat/apps'; import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; import type { IUpload } from '@rocket.chat/core-typings'; import { Users, Avatars, UserDataFiles, Uploads, Settings, Subscriptions, Messages, Rooms } from '@rocket.chat/models'; @@ -22,6 +21,7 @@ import sharp from 'sharp'; import type { WritableStreamBuffer } from 'stream-buffers'; import streamBuffers from 'stream-buffers'; +import { AppEvents, Apps } from '../../../../ee/server/apps'; import { i18n } from '../../../../server/lib/i18n'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; @@ -177,7 +177,7 @@ export const FileUpload = { // App IPreFileUpload event hook try { - await Apps?.triggerEvent(AppEvents.IPreFileUpload, { file, content: content || Buffer.from([]) }); + await Apps.triggerEvent(AppEvents.IPreFileUpload, { file, content: content || Buffer.from([]) }); } catch (error: any) { if (error.name === AppsEngineException.name) { throw new Meteor.Error('error-app-prevented', error.message); diff --git a/apps/meteor/app/lib/server/functions/addUserToRoom.ts b/apps/meteor/app/lib/server/functions/addUserToRoom.ts index 4a70943d28e2..4e29576cf3bb 100644 --- a/apps/meteor/app/lib/server/functions/addUserToRoom.ts +++ b/apps/meteor/app/lib/server/functions/addUserToRoom.ts @@ -1,4 +1,3 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; import { Message, Team } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; @@ -6,6 +5,7 @@ import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; +import { AppEvents, Apps } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { getSubscriptionAutotranslateDefaultConfig } from '../../../../server/lib/getSubscriptionAutotranslateDefaultConfig'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; @@ -54,7 +54,7 @@ export const addUserToRoom = async function ( } try { - await Apps?.triggerEvent(AppEvents.IPreRoomUserJoined, room, userToBeAdded, inviter); + await Apps.triggerEvent(AppEvents.IPreRoomUserJoined, room, userToBeAdded, inviter); } catch (error: any) { if (error.name === AppsEngineException.name) { throw new Meteor.Error('error-app-prevented', error.message); @@ -118,7 +118,7 @@ export const addUserToRoom = async function ( // Keep the current event await callbacks.run('afterJoinRoom', userToBeAdded, room); - void Apps?.triggerEvent(AppEvents.IPostRoomUserJoined, room, userToBeAdded, inviter); + void Apps.triggerEvent(AppEvents.IPostRoomUserJoined, room, userToBeAdded, inviter); }); } diff --git a/apps/meteor/app/lib/server/functions/createDirectRoom.ts b/apps/meteor/app/lib/server/functions/createDirectRoom.ts index cf9d2fdb7042..b8383875444f 100644 --- a/apps/meteor/app/lib/server/functions/createDirectRoom.ts +++ b/apps/meteor/app/lib/server/functions/createDirectRoom.ts @@ -1,4 +1,3 @@ -import { AppEvents, Apps } from '@rocket.chat/apps'; import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; import type { ISubscriptionExtraData } from '@rocket.chat/core-services'; import type { ICreatedRoom, IRoom, ISubscription, IUser } from '@rocket.chat/core-typings'; @@ -7,6 +6,7 @@ import { Random } from '@rocket.chat/random'; import { Meteor } from 'meteor/meteor'; import type { MatchKeysAndValues } from 'mongodb'; +import { Apps } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { isTruthy } from '../../../../lib/isTruthy'; import { settings } from '../../../settings/server'; @@ -104,7 +104,7 @@ export async function createDirectRoom( _USERNAMES: usernames, }; - const prevent = await Apps?.triggerEvent(AppEvents.IPreRoomCreatePrevent, tmpRoom).catch((error) => { + const prevent = await Apps.triggerEvent('IPreRoomCreatePrevent', tmpRoom).catch((error) => { if (error.name === AppsEngineException.name) { throw new Meteor.Error('error-app-prevented', error.message); } @@ -116,10 +116,7 @@ export async function createDirectRoom( throw new Meteor.Error('error-app-prevented', 'A Rocket.Chat App prevented the room creation.'); } - const result = await Apps?.triggerEvent( - AppEvents.IPreRoomCreateModify, - await Apps?.triggerEvent(AppEvents.IPreRoomCreateExtend, tmpRoom), - ); + const result = await Apps.triggerEvent('IPreRoomCreateModify', await Apps.triggerEvent('IPreRoomCreateExtend', tmpRoom)); if (typeof result === 'object') { Object.assign(roomInfo, result); @@ -173,7 +170,7 @@ export async function createDirectRoom( await callbacks.run('afterCreateDirectRoom', insertedRoom, { members: roomMembers, creatorId: options?.creator }); - void Apps?.triggerEvent(AppEvents.IPostRoomCreate, insertedRoom); + void Apps.triggerEvent('IPostRoomCreate', insertedRoom); } return { diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index 517d794e68d3..3004dcd445ca 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -1,5 +1,4 @@ /* eslint-disable complexity */ -import { AppEvents, Apps } from '@rocket.chat/apps'; import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; import { Message, Team } from '@rocket.chat/core-services'; import type { ICreateRoomParams, ISubscriptionExtraData } from '@rocket.chat/core-services'; @@ -7,6 +6,7 @@ import type { ICreatedRoom, IUser, IRoom, RoomType } from '@rocket.chat/core-typ import { Rooms, Subscriptions, Users } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; +import { Apps } from '../../../../ee/server/apps/orchestrator'; import { callbacks } from '../../../../lib/callbacks'; import { beforeCreateRoomCallback } from '../../../../lib/callbacks/beforeCreateRoomCallback'; import { getSubscriptionAutotranslateDefaultConfig } from '../../../../server/lib/getSubscriptionAutotranslateDefaultConfig'; @@ -197,7 +197,7 @@ export const createRoom = async ( _USERNAMES: members, }; - const prevent = await Apps?.triggerEvent(AppEvents.IPreRoomCreatePrevent, tmp).catch((error) => { + const prevent = await Apps.triggerEvent('IPreRoomCreatePrevent', tmp).catch((error) => { if (error.name === AppsEngineException.name) { throw new Meteor.Error('error-app-prevented', error.message); } @@ -209,10 +209,7 @@ export const createRoom = async ( throw new Meteor.Error('error-app-prevented', 'A Rocket.Chat App prevented the room creation.'); } - const eventResult = await Apps?.triggerEvent( - AppEvents.IPreRoomCreateModify, - await Apps.triggerEvent(AppEvents.IPreRoomCreateExtend, tmp), - ); + const eventResult = await Apps.triggerEvent('IPreRoomCreateModify', await Apps.triggerEvent('IPreRoomCreateExtend', tmp)); if (eventResult && typeof eventResult === 'object' && delete eventResult._USERNAMES) { Object.assign(roomProps, eventResult); @@ -244,7 +241,7 @@ export const createRoom = async ( callbacks.runAsync('federation.afterCreateFederatedRoom', room, { owner, originalMemberList: members }); } - void Apps?.triggerEvent(AppEvents.IPostRoomCreate, room); + void Apps.triggerEvent('IPostRoomCreate', room); return { rid: room._id, // backwards compatible inserted: true, diff --git a/apps/meteor/app/lib/server/functions/deleteMessage.ts b/apps/meteor/app/lib/server/functions/deleteMessage.ts index 26677bf37fff..cd4456b24514 100644 --- a/apps/meteor/app/lib/server/functions/deleteMessage.ts +++ b/apps/meteor/app/lib/server/functions/deleteMessage.ts @@ -1,9 +1,9 @@ -import { AppEvents, Apps } from '@rocket.chat/apps'; import { api } from '@rocket.chat/core-services'; import type { AtLeast, IMessage, IUser } from '@rocket.chat/core-typings'; import { Messages, Rooms, Uploads, Users, ReadReceipts } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; +import { Apps } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { broadcastMessageFromData } from '../../../../server/modules/watchers/lib/messages'; import { canDeleteMessageAsync } from '../../../authorization/server/functions/canDeleteMessage'; @@ -29,14 +29,14 @@ export const deleteMessageValidatingPermission = async (message: AtLeast { - const deletedMsg: IMessage | null = await Messages.findOneById(message._id); + const deletedMsg = await Messages.findOneById(message._id); const isThread = (deletedMsg?.tcount || 0) > 0; const keepHistory = settings.get('Message_KeepHistory') || isThread; const showDeletedStatus = settings.get('Message_ShowDeletedStatus') || isThread; const bridges = Apps?.isLoaded() && Apps.getBridges(); if (deletedMsg && bridges) { - const prevent = await bridges.getListenerBridge().messageEvent(AppEvents.IPreMessageDeletePrevent, deletedMsg); + const prevent = await bridges.getListenerBridge().messageEvent('IPreMessageDeletePrevent', deletedMsg); if (prevent) { throw new Meteor.Error('error-app-prevented-deleting', 'A Rocket.Chat App prevented the message deleting.'); } @@ -95,7 +95,7 @@ export async function deleteMessage(message: IMessage, user: IUser): Promise { const originalMessage = originalMsg || (await Messages.findOneById(message._id)); - if (!originalMessage) { - throw new Error('Invalid message ID.'); - } - - let messageData: IMessage = Object.assign({}, originalMessage, message); // For the Rocket.Chat Apps :) if (message && Apps && Apps.isLoaded()) { - const prevent = await Apps.getBridges().getListenerBridge().messageEvent(AppEvents.IPreMessageUpdatedPrevent, messageData); + const appMessage = Object.assign({}, originalMessage, message); + + const prevent = await Apps.getBridges()?.getListenerBridge().messageEvent('IPreMessageUpdatedPrevent', appMessage); if (prevent) { throw new Meteor.Error('error-app-prevented-updating', 'A Rocket.Chat App prevented the message updating.'); } - let result = await Apps.getBridges().getListenerBridge().messageEvent(AppEvents.IPreMessageUpdatedExtend, messageData); - result = await Apps.getBridges().getListenerBridge().messageEvent(AppEvents.IPreMessageUpdatedModify, result); + let result; + result = await Apps.getBridges()?.getListenerBridge().messageEvent('IPreMessageUpdatedExtend', appMessage); + result = await Apps.getBridges()?.getListenerBridge().messageEvent('IPreMessageUpdatedModify', result); if (typeof result === 'object') { - Object.assign(messageData, result); + message = Object.assign(appMessage, result); } } // If we keep history of edits, insert a new message to store history information if (settings.get('Message_KeepHistory')) { - await Messages.cloneAndSaveAsHistoryById(messageData._id, user as Required>); + await Messages.cloneAndSaveAsHistoryById(message._id, user as Required>); } - Object.assign(messageData, { + Object.assign, Omit>(message, { editedAt: new Date(), editedBy: { _id: user._id, @@ -50,16 +48,17 @@ export const updateMessage = async function ( }, }); - parseUrlsInMessage(messageData, previewUrls); + parseUrlsInMessage(message, previewUrls); - const room = await Rooms.findOneById(messageData.rid); + const room = await Rooms.findOneById(message.rid); if (!room) { return; } - messageData = await Message.beforeSave({ message: messageData, room, user }); + // TODO remove type cast + message = await Message.beforeSave({ message: message as IMessage, room, user }); - const { _id, ...editedMessage } = messageData; + const { _id, ...editedMessage } = message; if (!editedMessage.msg) { delete editedMessage.md; @@ -79,7 +78,7 @@ export const updateMessage = async function ( if (Apps?.isLoaded()) { // This returns a promise, but it won't mutate anything about the message // so, we don't really care if it is successful or fails - void Apps.getBridges()?.getListenerBridge().messageEvent(AppEvents.IPostMessageUpdated, messageData); + void Apps.getBridges()?.getListenerBridge().messageEvent('IPostMessageUpdated', message); } setImmediate(async () => { diff --git a/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts b/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts index f30182def68e..ed9929622c6d 100644 --- a/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts +++ b/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts @@ -1,4 +1,3 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import { Users } from '@rocket.chat/models'; import { SHA256 } from '@rocket.chat/sha256'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; @@ -6,6 +5,7 @@ import { Accounts } from 'meteor/accounts-base'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; +import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; import { trim } from '../../../../lib/utils/stringUtils'; import { settings } from '../../../settings/server'; import { deleteUser } from '../functions/deleteUser'; @@ -66,7 +66,7 @@ Meteor.methods({ await deleteUser(uid, confirmRelinquish); // App IPostUserDeleted event hook - await Apps?.triggerEvent(AppEvents.IPostUserDeleted, { user }); + await Apps.triggerEvent(AppEvents.IPostUserDeleted, { user }); return true; }, diff --git a/apps/meteor/app/livechat/server/lib/Helper.ts b/apps/meteor/app/livechat/server/lib/Helper.ts index 24cb2dd320cc..771f50724c38 100644 --- a/apps/meteor/app/livechat/server/lib/Helper.ts +++ b/apps/meteor/app/livechat/server/lib/Helper.ts @@ -1,4 +1,3 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import { LivechatTransferEventType } from '@rocket.chat/apps-engine/definition/livechat'; import { api, Message, Omnichannel } from '@rocket.chat/core-services'; import type { @@ -31,6 +30,7 @@ import { import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; +import { Apps, AppEvents } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { validateEmail as validatorFunc } from '../../../../lib/emailValidator'; import { i18n } from '../../../../server/lib/i18n'; @@ -118,7 +118,7 @@ export const createLivechatRoom = async ( const roomId = (await Rooms.insertOne(room)).insertedId; - void Apps?.triggerEvent(AppEvents.IPostLivechatRoomStarted, room); + void Apps.triggerEvent(AppEvents.IPostLivechatRoomStarted, room); await callbacks.run('livechat.newRoom', room); await sendMessage(guest, { t: 'livechat-started', msg: '', groupable: false }, room); @@ -274,7 +274,7 @@ export const removeAgentFromSubscription = async (rid: string, { _id, username } await Message.saveSystemMessage('ul', rid, username || '', { _id: user._id, username: user.username, name: user.name }); setImmediate(() => { - void Apps?.triggerEvent(AppEvents.IPostLivechatAgentUnassigned, { room, user }); + void Apps.triggerEvent(AppEvents.IPostLivechatAgentUnassigned, { room, user }); }); }; @@ -453,7 +453,7 @@ export const forwardRoomToAgent = async (room: IOmnichannelRoom, transferData: T } setImmediate(() => { - void Apps?.triggerEvent(AppEvents.IPostLivechatRoomTransferred, { + void Apps.triggerEvent(AppEvents.IPostLivechatRoomTransferred, { type: LivechatTransferEventType.AGENT, room: rid, from: oldServedBy?._id, @@ -483,7 +483,7 @@ export const updateChatDepartment = async ({ ]); setImmediate(() => { - void Apps?.triggerEvent(AppEvents.IPostLivechatRoomTransferred, { + void Apps.triggerEvent(AppEvents.IPostLivechatRoomTransferred, { type: LivechatTransferEventType.DEPARTMENT, room: rid, from: oldDepartmentId, diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 8442585d961f..fb1098db4272 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -1,7 +1,6 @@ import dns from 'dns'; import * as util from 'util'; -import { Apps, AppEvents } from '@rocket.chat/apps'; import { Message, VideoConf, api, Omnichannel } from '@rocket.chat/core-services'; import type { IOmnichannelRoom, @@ -44,6 +43,7 @@ import moment from 'moment-timezone'; import type { Filter, FindCursor, UpdateFilter } from 'mongodb'; import UAParser from 'ua-parser-js'; +import { Apps, AppEvents } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { trim } from '../../../../lib/utils/stringUtils'; import { i18n } from '../../../../server/lib/i18n'; @@ -330,8 +330,8 @@ class LivechatClass { * @deprecated the `AppEvents.ILivechatRoomClosedHandler` event will be removed * in the next major version of the Apps-Engine */ - void Apps?.getBridges()?.getListenerBridge().livechatEvent(AppEvents.ILivechatRoomClosedHandler, newRoom); - void Apps?.getBridges()?.getListenerBridge().livechatEvent(AppEvents.IPostLivechatRoomClosed, newRoom); + void Apps.getBridges()?.getListenerBridge().livechatEvent(AppEvents.ILivechatRoomClosedHandler, newRoom); + void Apps.getBridges()?.getListenerBridge().livechatEvent(AppEvents.IPostLivechatRoomClosed, newRoom); }); if (process.env.TEST_MODE) { await callbacks.run('livechat.closeRoom', { @@ -1420,7 +1420,7 @@ class LivechatClass { const ret = await LivechatVisitors.saveGuestById(_id, updateData); setImmediate(() => { - void Apps?.triggerEvent(AppEvents.IPostLivechatGuestSaved, _id); + void Apps.triggerEvent(AppEvents.IPostLivechatGuestSaved, _id); }); return ret; @@ -1786,7 +1786,7 @@ class LivechatClass { await LivechatRooms.saveRoomById(roomData); setImmediate(() => { - void Apps?.triggerEvent(AppEvents.IPostLivechatRoomSaved, roomData._id); + void Apps.triggerEvent(AppEvents.IPostLivechatRoomSaved, roomData._id); }); if (guestData?.name?.trim().length) { diff --git a/apps/meteor/app/livechat/server/lib/RoutingManager.ts b/apps/meteor/app/livechat/server/lib/RoutingManager.ts index 96621bece8b0..7b85c31f26ac 100644 --- a/apps/meteor/app/livechat/server/lib/RoutingManager.ts +++ b/apps/meteor/app/livechat/server/lib/RoutingManager.ts @@ -1,4 +1,3 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import { Message, Omnichannel } from '@rocket.chat/core-services'; import type { ILivechatInquiryRecord, @@ -17,6 +16,7 @@ import { LivechatInquiry, LivechatRooms, Subscriptions, Rooms, Users } from '@ro import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; +import { Apps, AppEvents } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { createLivechatSubscription, @@ -172,7 +172,7 @@ export const RoutingManager: Routing = { await dispatchAgentDelegated(rid, agent.agentId); logger.debug(`Agent ${agent.agentId} assigned to inquriy ${inquiry._id}. Instances notified`); - void Apps?.getBridges()?.getListenerBridge().livechatEvent(AppEvents.IPostLivechatAgentAssigned, { room, user }); + void Apps.getBridges()?.getListenerBridge().livechatEvent(AppEvents.IPostLivechatAgentAssigned, { room, user }); return inquiry; }, diff --git a/apps/meteor/app/mailer/server/api.ts b/apps/meteor/app/mailer/server/api.ts index cc2caae74ba6..b50fdfd26a2a 100644 --- a/apps/meteor/app/mailer/server/api.ts +++ b/apps/meteor/app/mailer/server/api.ts @@ -1,4 +1,3 @@ -import { AppEvents, Apps } from '@rocket.chat/apps'; import type { ISetting } from '@rocket.chat/core-typings'; import { Settings } from '@rocket.chat/models'; import { escapeHTML } from '@rocket.chat/string-helpers'; @@ -8,6 +7,7 @@ import { Meteor } from 'meteor/meteor'; import stripHtml from 'string-strip-html'; import _ from 'underscore'; +import { Apps } from '../../../ee/server/apps'; import { validateEmail } from '../../../lib/emailValidator'; import { strLeft, strRightBack } from '../../../lib/utils/stringUtils'; import { i18n } from '../../../server/lib/i18n'; @@ -170,7 +170,7 @@ export const sendNoWrap = async ({ const email = { to, from, replyTo, subject, html, text, headers }; - const eventResult = await Apps?.triggerEvent(AppEvents.IPreEmailSent, { email }); + const eventResult = await Apps.triggerEvent('IPreEmailSent', { email }); setImmediate(() => Email.sendAsync(eventResult || email).catch((e) => console.error(e))); }; diff --git a/apps/meteor/app/message-pin/server/pinMessage.ts b/apps/meteor/app/message-pin/server/pinMessage.ts index 4887e3603122..1ed0a172028b 100644 --- a/apps/meteor/app/message-pin/server/pinMessage.ts +++ b/apps/meteor/app/message-pin/server/pinMessage.ts @@ -1,4 +1,3 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import { Message } from '@rocket.chat/core-services'; import { isQuoteAttachment, isRegisterUser } from '@rocket.chat/core-typings'; import type { IMessage, MessageAttachment, MessageQuoteAttachment } from '@rocket.chat/core-typings'; @@ -7,6 +6,7 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; +import { Apps, AppEvents } from '../../../ee/server/apps/orchestrator'; import { isTruthy } from '../../../lib/isTruthy'; import { broadcastMessageFromData } from '../../../server/modules/watchers/lib/messages'; import { canAccessRoomAsync, roomAccessAttributes } from '../../authorization/server'; @@ -129,7 +129,7 @@ Meteor.methods({ } // App IPostMessagePinned event hook - await Apps?.triggerEvent(AppEvents.IPostMessagePinned, originalMessage, await Meteor.userAsync(), originalMessage.pinned); + await Apps.triggerEvent(AppEvents.IPostMessagePinned, originalMessage, await Meteor.userAsync(), originalMessage.pinned); const msgId = await Message.saveSystemMessage('message_pinned', originalMessage.rid, '', me, { attachments: [ @@ -216,7 +216,7 @@ Meteor.methods({ } // App IPostMessagePinned event hook - await Apps?.triggerEvent(AppEvents.IPostMessagePinned, originalMessage, await Meteor.userAsync(), originalMessage.pinned); + await Apps.triggerEvent(AppEvents.IPostMessagePinned, originalMessage, await Meteor.userAsync(), originalMessage.pinned); await Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned); if (settings.get('Message_Read_Receipt_Store_Users')) { diff --git a/apps/meteor/app/message-star/server/starMessage.ts b/apps/meteor/app/message-star/server/starMessage.ts index 9f8ba75c4536..8f025d920057 100644 --- a/apps/meteor/app/message-star/server/starMessage.ts +++ b/apps/meteor/app/message-star/server/starMessage.ts @@ -1,9 +1,9 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IMessage } from '@rocket.chat/core-typings'; import { Messages, Subscriptions, Rooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; +import { Apps, AppEvents } from '../../../ee/server/apps/orchestrator'; import { broadcastMessageFromData } from '../../../server/modules/watchers/lib/messages'; import { canAccessRoomAsync, roomAccessAttributes } from '../../authorization/server'; import { isTheLastMessage } from '../../lib/server/functions/isTheLastMessage'; @@ -57,7 +57,7 @@ Meteor.methods({ await Rooms.updateLastMessageStar(room._id, uid, message.starred); } - await Apps?.triggerEvent(AppEvents.IPostMessageStarred, message, await Meteor.userAsync(), message.starred); + await Apps.triggerEvent(AppEvents.IPostMessageStarred, message, await Meteor.userAsync(), message.starred); await Messages.updateUserStarById(message._id, uid, message.starred); diff --git a/apps/meteor/app/reactions/server/setReaction.ts b/apps/meteor/app/reactions/server/setReaction.ts index ed2271a5d4d0..27fe4d36a053 100644 --- a/apps/meteor/app/reactions/server/setReaction.ts +++ b/apps/meteor/app/reactions/server/setReaction.ts @@ -1,4 +1,3 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import { api } from '@rocket.chat/core-services'; import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; import { Messages, EmojiCustom, Rooms, Users } from '@rocket.chat/models'; @@ -6,6 +5,7 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; +import { AppEvents, Apps } from '../../../ee/server/apps/orchestrator'; import { callbacks } from '../../../lib/callbacks'; import { i18n } from '../../../server/lib/i18n'; import { broadcastMessageFromData } from '../../../server/modules/watchers/lib/messages'; @@ -106,7 +106,7 @@ async function setReaction(room: IRoom, user: IUser, message: IMessage, reaction isReacted = true; } - await Apps?.triggerEvent(AppEvents.IPostMessageReacted, message, user, reaction, isReacted); + await Apps.triggerEvent(AppEvents.IPostMessageReacted, message, user, reaction, isReacted); void broadcastMessageFromData({ id: message._id, diff --git a/apps/meteor/app/statistics/server/lib/getAppsStatistics.js b/apps/meteor/app/statistics/server/lib/getAppsStatistics.js index 652686e6715c..6337b287506a 100644 --- a/apps/meteor/app/statistics/server/lib/getAppsStatistics.js +++ b/apps/meteor/app/statistics/server/lib/getAppsStatistics.js @@ -1,18 +1,17 @@ -import { Apps } from '@rocket.chat/apps'; import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; +import { Apps } from '../../../../ee/server/apps'; import { Info } from '../../../utils/rocketchat.info'; export function getAppsStatistics() { return { engineVersion: Info.marketplaceApiVersion, - totalInstalled: (Apps?.isInitialized() && Apps.getManager().get().length) ?? 0, - totalActive: (Apps?.isInitialized() && Apps.getManager().get({ enabled: true }).length) ?? 0, + totalInstalled: Apps.isInitialized() && Apps.getManager().get().length, + totalActive: Apps.isInitialized() && Apps.getManager().get({ enabled: true }).length, totalFailed: - (Apps?.isInitialized() && - Apps.getManager() - .get({ disabled: true }) - .filter(({ app: { status } }) => status !== AppStatus.MANUALLY_DISABLED).length) ?? - 0, + Apps.isInitialized() && + Apps.getManager() + .get({ disabled: true }) + .filter(({ app: { status } }) => status !== AppStatus.MANUALLY_DISABLED).length, }; } diff --git a/apps/meteor/app/threads/server/methods/followMessage.ts b/apps/meteor/app/threads/server/methods/followMessage.ts index f6bae69b1aaa..cede3dda33a7 100644 --- a/apps/meteor/app/threads/server/methods/followMessage.ts +++ b/apps/meteor/app/threads/server/methods/followMessage.ts @@ -1,10 +1,10 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IMessage } from '@rocket.chat/core-typings'; import { Messages } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; +import { Apps, AppEvents } from '../../../../ee/server/apps/orchestrator'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; import { RateLimiter } from '../../../lib/server'; import { settings } from '../../../settings/server'; @@ -44,7 +44,7 @@ Meteor.methods({ const followResult = await follow({ tmid: message.tmid || message._id, uid }); const isFollowed = true; - await Apps?.triggerEvent(AppEvents.IPostMessageFollowed, message, await Meteor.userAsync(), isFollowed); + await Apps.triggerEvent(AppEvents.IPostMessageFollowed, message, await Meteor.userAsync(), isFollowed); return followResult; }, diff --git a/apps/meteor/app/threads/server/methods/unfollowMessage.ts b/apps/meteor/app/threads/server/methods/unfollowMessage.ts index b50c26508ebc..c5dad1233173 100644 --- a/apps/meteor/app/threads/server/methods/unfollowMessage.ts +++ b/apps/meteor/app/threads/server/methods/unfollowMessage.ts @@ -1,10 +1,10 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IMessage } from '@rocket.chat/core-typings'; import { Messages } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; +import { Apps, AppEvents } from '../../../../ee/server/apps/orchestrator'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; import { RateLimiter } from '../../../lib/server'; import { settings } from '../../../settings/server'; @@ -44,7 +44,7 @@ Meteor.methods({ const unfollowResult = await unfollow({ rid: message.rid, tmid: message.tmid || message._id, uid }); const isFollowed = false; - await Apps?.triggerEvent(AppEvents.IPostMessageFollowed, message, await Meteor.userAsync(), isFollowed); + await Apps.triggerEvent(AppEvents.IPostMessageFollowed, message, await Meteor.userAsync(), isFollowed); return unfollowResult; }, diff --git a/apps/meteor/ee/server/apps/index.ts b/apps/meteor/ee/server/apps/index.ts index 0306575e00f1..35f7c2cc041f 100644 --- a/apps/meteor/ee/server/apps/index.ts +++ b/apps/meteor/ee/server/apps/index.ts @@ -1,4 +1,4 @@ import './cron'; import './appRequestsCron'; -export { Apps } from './orchestrator'; +export { Apps, AppEvents } from './orchestrator'; diff --git a/apps/meteor/ee/server/apps/orchestrator.js b/apps/meteor/ee/server/apps/orchestrator.js index 37c31e890e89..84f9cb1372f3 100644 --- a/apps/meteor/ee/server/apps/orchestrator.js +++ b/apps/meteor/ee/server/apps/orchestrator.js @@ -1,5 +1,5 @@ -import { registerOrchestrator } from '@rocket.chat/apps'; import { EssentialAppDisabledException } from '@rocket.chat/apps-engine/definition/exceptions'; +import { AppInterface } from '@rocket.chat/apps-engine/definition/metadata'; import { AppManager } from '@rocket.chat/apps-engine/server/AppManager'; import { Logger } from '@rocket.chat/logger'; import { AppLogs, Apps as AppsModel, AppsPersistence } from '@rocket.chat/models'; @@ -249,8 +249,8 @@ export class AppServerOrchestrator { } } +export const AppEvents = AppInterface; export const Apps = new AppServerOrchestrator(); -registerOrchestrator(Apps); settings.watch('Apps_Framework_Source_Package_Storage_Type', (value) => { if (!Apps.isInitialized()) { diff --git a/apps/meteor/server/lib/moderation/reportMessage.ts b/apps/meteor/server/lib/moderation/reportMessage.ts index 710ea6e1b685..be8b917fd6f5 100644 --- a/apps/meteor/server/lib/moderation/reportMessage.ts +++ b/apps/meteor/server/lib/moderation/reportMessage.ts @@ -1,8 +1,8 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IMessage, IUser } from '@rocket.chat/core-typings'; import { Messages, ModerationReports, Rooms, Users } from '@rocket.chat/models'; import { canAccessRoomAsync } from '../../../app/authorization/server/functions/canAccessRoom'; +import { AppEvents, Apps } from '../../../ee/server/apps'; export const reportMessage = async (messageId: IMessage['_id'], description: string, uid: IUser['_id']) => { if (!uid) { @@ -49,7 +49,7 @@ export const reportMessage = async (messageId: IMessage['_id'], description: str await ModerationReports.createWithMessageDescriptionAndUserId(message, description, roomInfo, reportedBy); - await Apps?.triggerEvent(AppEvents.IPostMessageReported, message, user, description); + await Apps.triggerEvent(AppEvents.IPostMessageReported, message, user, description); return true; }; diff --git a/apps/meteor/server/methods/deleteUser.ts b/apps/meteor/server/methods/deleteUser.ts index 8762cfab2437..4dafad7a3a0c 100644 --- a/apps/meteor/server/methods/deleteUser.ts +++ b/apps/meteor/server/methods/deleteUser.ts @@ -1,4 +1,3 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; @@ -7,6 +6,7 @@ import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../app/authorization/server/functions/hasPermission'; import { deleteUser } from '../../app/lib/server/functions/deleteUser'; +import { AppEvents, Apps } from '../../ee/server/apps/orchestrator'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -52,7 +52,7 @@ Meteor.methods({ await deleteUser(userId, confirmRelinquish, uid); // App IPostUserDeleted event hook - await Apps?.triggerEvent(AppEvents.IPostUserDeleted, { user, performedBy: await Meteor.userAsync() }); + await Apps.triggerEvent(AppEvents.IPostUserDeleted, { user, performedBy: await Meteor.userAsync() }); return true; }, diff --git a/apps/meteor/server/methods/eraseRoom.ts b/apps/meteor/server/methods/eraseRoom.ts index 687b9ad66992..177b3c23bb9c 100644 --- a/apps/meteor/server/methods/eraseRoom.ts +++ b/apps/meteor/server/methods/eraseRoom.ts @@ -1,4 +1,3 @@ -import { AppEvents, Apps } from '@rocket.chat/apps'; import { Message, Team } from '@rocket.chat/core-services'; import { Rooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; @@ -8,6 +7,7 @@ import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../app/authorization/server/functions/hasPermission'; import { deleteRoom } from '../../app/lib/server/functions/deleteRoom'; import { methodDeprecationLogger } from '../../app/lib/server/lib/deprecationWarningLogger'; +import { Apps } from '../../ee/server/apps'; import { roomCoordinator } from '../lib/rooms/roomCoordinator'; export async function eraseRoom(rid: string, uid: string): Promise { @@ -36,7 +36,7 @@ export async function eraseRoom(rid: string, uid: string): Promise { } if (Apps?.isLoaded()) { - const prevent = await Apps.getBridges()?.getListenerBridge().roomEvent(AppEvents.IPreRoomDeletePrevent, room); + const prevent = await Apps.getBridges()?.getListenerBridge().roomEvent('IPreRoomDeletePrevent', room); if (prevent) { throw new Meteor.Error('error-app-prevented-deleting', 'A Rocket.Chat App prevented the room erasing.'); } @@ -54,7 +54,7 @@ export async function eraseRoom(rid: string, uid: string): Promise { } if (Apps?.isLoaded()) { - void Apps.getBridges()?.getListenerBridge().roomEvent(AppEvents.IPostRoomDeleted, room); + void Apps.getBridges()?.getListenerBridge().roomEvent('IPostRoomDeleted', room); } } diff --git a/apps/meteor/server/methods/logoutCleanUp.ts b/apps/meteor/server/methods/logoutCleanUp.ts index 502cad3c5fbf..9b9af5356af5 100644 --- a/apps/meteor/server/methods/logoutCleanUp.ts +++ b/apps/meteor/server/methods/logoutCleanUp.ts @@ -1,9 +1,9 @@ -import { AppEvents, Apps } from '@rocket.chat/apps'; import type { IUser } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; +import { AppEvents, Apps } from '../../ee/server/apps/orchestrator'; import { afterLogoutCleanUpCallback } from '../../lib/callbacks/afterLogoutCleanUpCallback'; declare module '@rocket.chat/ui-contexts' { @@ -22,6 +22,6 @@ Meteor.methods({ }); // App IPostUserLogout event hook - await Apps?.triggerEvent(AppEvents.IPostUserLoggedOut, user); + await Apps.triggerEvent(AppEvents.IPostUserLoggedOut, user); }, }); diff --git a/apps/meteor/server/methods/reportMessage.ts b/apps/meteor/server/methods/reportMessage.ts index 44087dad0424..94d6fc1fd315 100644 --- a/apps/meteor/server/methods/reportMessage.ts +++ b/apps/meteor/server/methods/reportMessage.ts @@ -1,4 +1,3 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IMessage } from '@rocket.chat/core-typings'; import { ModerationReports, Rooms, Users, Messages } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; @@ -7,6 +6,7 @@ import { Meteor } from 'meteor/meteor'; import { canAccessRoomAsync } from '../../app/authorization/server/functions/canAccessRoom'; import { methodDeprecationLogger } from '../../app/lib/server/lib/deprecationWarningLogger'; +import { AppEvents, Apps } from '../../ee/server/apps'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -77,7 +77,7 @@ Meteor.methods({ await ModerationReports.createWithMessageDescriptionAndUserId(message, description, roomInfo, reportedBy); - await Apps?.triggerEvent(AppEvents.IPostMessageReported, message, await Meteor.userAsync(), description); + await Apps.triggerEvent(AppEvents.IPostMessageReported, message, await Meteor.userAsync(), description); return true; }, diff --git a/apps/meteor/server/methods/saveUserProfile.ts b/apps/meteor/server/methods/saveUserProfile.ts index 695742977ad3..5bfbba5b1b3f 100644 --- a/apps/meteor/server/methods/saveUserProfile.ts +++ b/apps/meteor/server/methods/saveUserProfile.ts @@ -1,4 +1,3 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import type { UserStatus } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; @@ -13,6 +12,7 @@ import { saveUserIdentity } from '../../app/lib/server/functions/saveUserIdentit import { passwordPolicy } from '../../app/lib/server/lib/passwordPolicy'; import { settings as rcSettings } from '../../app/settings/server'; import { setUserStatusMethod } from '../../app/user-status/server/methods/setUserStatus'; +import { AppEvents, Apps } from '../../ee/server/apps/orchestrator'; import { compareUserPassword } from '../lib/compareUserPassword'; import { compareUserPasswordHistory } from '../lib/compareUserPasswordHistory'; @@ -156,7 +156,7 @@ async function saveUserProfile( // App IPostUserUpdated event hook const updatedUser = await Users.findOneById(this.userId); - await Apps?.triggerEvent(AppEvents.IPostUserUpdated, { user: updatedUser, previousUser: user }); + await Apps.triggerEvent(AppEvents.IPostUserUpdated, { user: updatedUser, previousUser: user }); return true; } diff --git a/apps/meteor/server/services/apps-engine/service.ts b/apps/meteor/server/services/apps-engine/service.ts index 7e36a937e6a6..e72ce3cbce0a 100644 --- a/apps/meteor/server/services/apps-engine/service.ts +++ b/apps/meteor/server/services/apps-engine/service.ts @@ -1,4 +1,3 @@ -import { Apps, AppEvents } from '@rocket.chat/apps'; import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; import { AppStatusUtils } from '@rocket.chat/apps-engine/definition/AppStatus'; import type { IAppInfo } from '@rocket.chat/apps-engine/definition/metadata'; @@ -7,6 +6,7 @@ import type { IAppStorageItem } from '@rocket.chat/apps-engine/server/storage'; import type { IAppsEngineService } from '@rocket.chat/core-services'; import { ServiceClassInternal } from '@rocket.chat/core-services'; +import { Apps, AppEvents } from '../../../ee/server/apps/orchestrator'; import { SystemLogger } from '../../lib/logger/system'; export class AppsEngineService extends ServiceClassInternal implements IAppsEngineService { @@ -16,7 +16,7 @@ export class AppsEngineService extends ServiceClassInternal implements IAppsEngi super(); this.onEvent('presence.status', async ({ user, previousStatus }): Promise => { - await Apps?.triggerEvent(AppEvents.IPostUserStatusChanged, { + await Apps.triggerEvent(AppEvents.IPostUserStatusChanged, { user, currentStatus: user.status, previousStatus, @@ -24,70 +24,68 @@ export class AppsEngineService extends ServiceClassInternal implements IAppsEngi }); this.onEvent('apps.added', async (appId: string): Promise => { - Apps?.getRocketChatLogger().debug(`"apps.added" event received for app "${appId}"`); + Apps.getRocketChatLogger().debug(`"apps.added" event received for app "${appId}"`); // if the app already exists in this instance, don't load it again - const app = Apps?.getManager()?.getOneById(appId); + const app = Apps.getManager()?.getOneById(appId); if (app) { - Apps?.getRocketChatLogger().info(`"apps.added" event received for app "${appId}", but it already exists in this instance`); + Apps.getRocketChatLogger().info(`"apps.added" event received for app "${appId}", but it already exists in this instance`); return; } - await Apps?.getManager()?.addLocal(appId); + await Apps.getManager()?.addLocal(appId); }); this.onEvent('apps.removed', async (appId: string): Promise => { - Apps?.getRocketChatLogger().debug(`"apps.removed" event received for app "${appId}"`); - const app = Apps?.getManager()?.getOneById(appId); + Apps.getRocketChatLogger().debug(`"apps.removed" event received for app "${appId}"`); + const app = Apps.getManager()?.getOneById(appId); if (!app) { - Apps?.getRocketChatLogger().info(`"apps.removed" event received for app "${appId}", but it couldn't be found in this instance`); + Apps.getRocketChatLogger().info(`"apps.removed" event received for app "${appId}", but it couldn't be found in this instance`); return; } - await Apps?.getManager()?.removeLocal(appId); + await Apps.getManager()?.removeLocal(appId); }); this.onEvent('apps.updated', async (appId: string): Promise => { - Apps?.getRocketChatLogger().debug(`"apps.updated" event received for app "${appId}"`); - const storageItem = await Apps?.getStorage()?.retrieveOne(appId); + Apps.getRocketChatLogger().debug(`"apps.updated" event received for app "${appId}"`); + const storageItem = await Apps.getStorage()?.retrieveOne(appId); if (!storageItem) { - Apps?.getRocketChatLogger().info(`"apps.updated" event received for app "${appId}", but it couldn't be found in the storage`); + Apps.getRocketChatLogger().info(`"apps.updated" event received for app "${appId}", but it couldn't be found in the storage`); return; } - const appPackage = await Apps?.getAppSourceStorage()?.fetch(storageItem); + const appPackage = await Apps.getAppSourceStorage()?.fetch(storageItem); if (!appPackage) { return; } - await Apps?.getManager()?.updateLocal(storageItem, appPackage); + await Apps.getManager()?.updateLocal(storageItem, appPackage); }); this.onEvent('apps.statusUpdate', async (appId: string, status: AppStatus): Promise => { - Apps?.getRocketChatLogger().debug(`"apps.statusUpdate" event received for app "${appId}" with status "${status}"`); - const app = Apps?.getManager()?.getOneById(appId); + Apps.getRocketChatLogger().debug(`"apps.statusUpdate" event received for app "${appId}" with status "${status}"`); + const app = Apps.getManager()?.getOneById(appId); if (!app) { - Apps?.getRocketChatLogger().info( - `"apps.statusUpdate" event received for app "${appId}", but it couldn't be found in this instance`, - ); + Apps.getRocketChatLogger().info(`"apps.statusUpdate" event received for app "${appId}", but it couldn't be found in this instance`); return; } if (app.getStatus() === status) { - Apps?.getRocketChatLogger().info(`"apps.statusUpdate" event received for app "${appId}", but the status is the same`); + Apps.getRocketChatLogger().info(`"apps.statusUpdate" event received for app "${appId}", but the status is the same`); return; } if (AppStatusUtils.isEnabled(status)) { - await Apps?.getManager()?.enable(appId).catch(SystemLogger.error); + await Apps.getManager()?.enable(appId).catch(SystemLogger.error); } else if (AppStatusUtils.isDisabled(status)) { - await Apps?.getManager()?.disable(appId, status, true).catch(SystemLogger.error); + await Apps.getManager()?.disable(appId, status, true).catch(SystemLogger.error); } }); this.onEvent('apps.settingUpdated', async (appId: string, setting): Promise => { - Apps?.getRocketChatLogger().debug(`"apps.settingUpdated" event received for app "${appId}"`, { setting }); - const app = Apps?.getManager()?.getOneById(appId); + Apps.getRocketChatLogger().debug(`"apps.settingUpdated" event received for app "${appId}"`, { setting }); + const app = Apps.getManager()?.getOneById(appId); const oldSetting = app?.getStorageItem().settings[setting.id].value; // avoid updating the setting if the value is the same, @@ -96,30 +94,30 @@ export class AppsEngineService extends ServiceClassInternal implements IAppsEngi // so we need to convert it to JSON stringified to compare it if (JSON.stringify(oldSetting) === JSON.stringify(setting.value)) { - Apps?.getRocketChatLogger().info( + Apps.getRocketChatLogger().info( `"apps.settingUpdated" event received for setting ${setting.id} of app "${appId}", but the setting value is the same`, ); return; } - await Apps?.getManager() + await Apps.getManager() ?.getSettingsManager() .updateAppSetting(appId, setting as any); }); } isInitialized(): boolean { - return Boolean(Apps?.isInitialized()); + return Apps.isInitialized(); } async getApps(query: IGetAppsFilter): Promise { - return Apps?.getManager() + return Apps.getManager() ?.get(query) .map((app) => app.getApp().getInfo()); } async getAppStorageItemById(appId: string): Promise { - const app = Apps?.getManager()?.getOneById(appId); + const app = Apps.getManager()?.getOneById(appId); if (!app) { return; diff --git a/apps/meteor/server/services/video-conference/service.ts b/apps/meteor/server/services/video-conference/service.ts index 87fe279d0d94..90a7a3302427 100644 --- a/apps/meteor/server/services/video-conference/service.ts +++ b/apps/meteor/server/services/video-conference/service.ts @@ -1,4 +1,3 @@ -import { Apps } from '@rocket.chat/apps'; import type { AppVideoConfProviderManager } from '@rocket.chat/apps-engine/server/managers'; import type { IVideoConfService, VideoConferenceJoinOptions } from '@rocket.chat/core-services'; import { api, ServiceClassInternal } from '@rocket.chat/core-services'; @@ -42,6 +41,7 @@ import { settings } from '../../../app/settings/server'; import { updateCounter } from '../../../app/statistics/server/functions/updateStatsCounter'; import { getUserAvatarURL } from '../../../app/utils/server/getUserAvatarURL'; import { getUserPreference } from '../../../app/utils/server/lib/getUserPreference'; +import { Apps } from '../../../ee/server/apps'; import { callbacks } from '../../../lib/callbacks'; import { availabilityErrors } from '../../../lib/videoConference/constants'; import { readSecondaryPreferred } from '../../database/readSecondaryPreferred'; @@ -832,7 +832,7 @@ export class VideoConfService extends ServiceClassInternal implements IVideoConf throw new Error('apps-engine-not-loaded'); } - const manager = Apps?.getManager()?.getVideoConfProviderManager(); + const manager = Apps.getManager()?.getVideoConfProviderManager(); if (!manager) { throw new Error(availabilityErrors.NO_APP); } diff --git a/apps/meteor/server/startup/migrations/v291.ts b/apps/meteor/server/startup/migrations/v291.ts index f4fdbb743447..8923f3b282c3 100644 --- a/apps/meteor/server/startup/migrations/v291.ts +++ b/apps/meteor/server/startup/migrations/v291.ts @@ -1,7 +1,8 @@ -import { Apps, type AppMetadataStorage } from '@rocket.chat/apps'; import type { IAppStorageItem } from '@rocket.chat/apps-engine/server/storage'; import { Settings } from '@rocket.chat/models'; +import { Apps } from '../../../ee/server/apps'; +import type { AppRealStorage } from '../../../ee/server/apps/storage'; import { addMigration } from '../../lib/migrations'; addMigration({ @@ -12,17 +13,13 @@ addMigration({ await Settings.removeById('Apps_Framework_Development_Mode'); await Settings.removeById('Apps_Framework_enabled'); - if (!Apps) { - throw new Error('Apps Orchestrator not registered.'); - } - Apps.initialize(); - const appsStorage = Apps.getStorage(); + const appsStorage = Apps.getStorage() as AppRealStorage; const apps = await appsStorage.retrieveAll(); - const promises: Array> = []; + const promises: Array> = []; apps.forEach((app) => promises.push( diff --git a/apps/meteor/server/startup/migrations/v292.ts b/apps/meteor/server/startup/migrations/v292.ts index beec6967a904..7f590f4038e2 100644 --- a/apps/meteor/server/startup/migrations/v292.ts +++ b/apps/meteor/server/startup/migrations/v292.ts @@ -1,7 +1,7 @@ -import { Apps } from '@rocket.chat/apps'; import type { AppSignatureManager } from '@rocket.chat/apps-engine/server/managers/AppSignatureManager'; import type { IAppStorageItem } from '@rocket.chat/apps-engine/server/storage'; +import { Apps } from '../../../ee/server/apps'; import type { AppRealStorage } from '../../../ee/server/apps/storage'; import { addMigration } from '../../lib/migrations'; @@ -9,10 +9,6 @@ addMigration({ version: 292, name: 'Add checksum signature to existing apps', async up() { - if (!Apps) { - throw new Error('Apps Orchestrator not registered.'); - } - Apps.initialize(); const sigMan = Apps.getManager()?.getSignatureManager() as AppSignatureManager; diff --git a/apps/meteor/server/startup/migrations/v294.ts b/apps/meteor/server/startup/migrations/v294.ts index 832043740f89..abcb20d079ec 100644 --- a/apps/meteor/server/startup/migrations/v294.ts +++ b/apps/meteor/server/startup/migrations/v294.ts @@ -1,17 +1,13 @@ -import { Apps } from '@rocket.chat/apps'; import type { AppSignatureManager } from '@rocket.chat/apps-engine/server/managers/AppSignatureManager'; import type { IAppStorageItem } from '@rocket.chat/apps-engine/server/storage'; +import { Apps } from '../../../ee/server/apps'; import type { AppRealStorage } from '../../../ee/server/apps/storage'; import { addMigration } from '../../lib/migrations'; addMigration({ version: 294, async up() { - if (!Apps) { - throw new Error('Apps Orchestrator not registered.'); - } - Apps.initialize(); const sigMan = Apps.getManager()?.getSignatureManager() as AppSignatureManager; diff --git a/packages/apps/src/AppsEngine.ts b/packages/apps/src/AppsEngine.ts index 856bc1253790..117e93c0ec2f 100644 --- a/packages/apps/src/AppsEngine.ts +++ b/packages/apps/src/AppsEngine.ts @@ -8,7 +8,6 @@ export type { IVisitorPhone as IAppsVisitorPhone, } from '@rocket.chat/apps-engine/definition/livechat'; export type { IMessage as IAppsMessage } from '@rocket.chat/apps-engine/definition/messages'; -export { AppInterface as AppEvents } from '@rocket.chat/apps-engine/definition/metadata'; export type { IUser as IAppsUser } from '@rocket.chat/apps-engine/definition/users'; export type { IRole as IAppsRole } from '@rocket.chat/apps-engine/definition/roles'; export type { IRoom as IAppsRoom } from '@rocket.chat/apps-engine/definition/rooms'; @@ -19,5 +18,3 @@ export type { VideoConference as AppsVideoConference, } from '@rocket.chat/apps-engine/definition/videoConferences'; export { AppManager } from '@rocket.chat/apps-engine/server/AppManager'; -export { AppBridges } from '@rocket.chat/apps-engine/server/bridges'; -export { AppMetadataStorage } from '@rocket.chat/apps-engine/server/storage'; diff --git a/packages/apps/src/IAppServerOrchestrator.ts b/packages/apps/src/IAppServerOrchestrator.ts index 2f1f7db5d4b5..dbfc5aee7a20 100644 --- a/packages/apps/src/IAppServerOrchestrator.ts +++ b/packages/apps/src/IAppServerOrchestrator.ts @@ -1,16 +1,12 @@ import type { AppManager } from '@rocket.chat/apps-engine/server/AppManager'; -import type { AppSourceStorage } from '@rocket.chat/apps-engine/server/storage'; import type { Logger } from '@rocket.chat/logger'; import type { IAppsPersistenceModel } from '@rocket.chat/model-typings'; -import type { AppBridges, AppEvents, AppMetadataStorage } from './AppsEngine'; import type { IAppServerNotifier } from './IAppServerNotifier'; import type { IAppConvertersMap } from './converters'; export interface IAppServerOrchestrator { initialize(): void; - isInitialized(): boolean; - isLoaded(): boolean; getNotifier(): IAppServerNotifier; isDebugging(): boolean; debugLog(...args: any[]): void; @@ -18,8 +14,4 @@ export interface IAppServerOrchestrator { getConverters(): IAppConvertersMap; getPersistenceModel(): IAppsPersistenceModel; getRocketChatLogger(): Logger; - triggerEvent(event: AppEvents, ...payload: any[]): Promise; - getBridges(): AppBridges; - getStorage(): AppMetadataStorage; - getAppSourceStorage(): AppSourceStorage; } diff --git a/packages/apps/src/bridges/IListenerBridge.ts b/packages/apps/src/bridges/IListenerBridge.ts deleted file mode 100644 index faf34118cd30..000000000000 --- a/packages/apps/src/bridges/IListenerBridge.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { IMessage, IRoom, IUser, ILivechatDepartment, ILivechatVisitor, IOmnichannelRoom } from '@rocket.chat/core-typings'; - -import type { AppEvents } from '../AppsEngine'; - -declare module '@rocket.chat/apps-engine/server/bridges' { - interface IListenerBridge { - messageEvent(int: 'IPostMessageDeleted', message: IMessage, userDeleted: IUser): Promise; - messageEvent(int: 'IPostMessageReacted', message: IMessage, userReacted: IUser, reaction: string, isReacted: boolean): Promise; - messageEvent(int: 'IPostMessageFollowed', message: IMessage, userFollowed: IUser, isFollowed: boolean): Promise; - messageEvent(int: 'IPostMessagePinned', message: IMessage, userPinned: IUser, isPinned: boolean): Promise; - messageEvent(int: 'IPostMessageStarred', message: IMessage, userStarred: IUser, isStarred: boolean): Promise; - messageEvent(int: 'IPostMessageReported', message: IMessage, userReported: IUser, reason: boolean): Promise; - - messageEvent( - int: 'IPreMessageSentPrevent' | 'IPreMessageDeletePrevent' | 'IPreMessageUpdatedPrevent', - message: IMessage, - ): Promise; - messageEvent( - int: 'IPreMessageSentExtend' | 'IPreMessageSentModify' | 'IPreMessageUpdatedExtend' | 'IPreMessageUpdatedModify', - message: IMessage, - ): Promise; - messageEvent(int: 'IPostMessageSent' | 'IPostMessageUpdated', message: IMessage): Promise; - - roomEvent(int: 'IPreRoomUserJoined' | 'IPostRoomUserJoined', room: IRoom, joiningUser: IUser, invitingUser?: IUser): Promise; - roomEvent(int: 'IPreRoomUserLeave' | 'IPostRoomUserLeave', room: IRoom, leavingUser: IUser): Promise; - - roomEvent(int: 'IPreRoomCreatePrevent' | 'IPreRoomDeletePrevent', room: IRoom): Promise; - roomEvent(int: 'IPreRoomCreateExtend' | 'IPreRoomCreateModify', room: IRoom): Promise; - roomEvent(int: 'IPostRoomCreate' | 'IPostRoomDeleted', room: IRoom): Promise; - - livechatEvent( - int: 'IPostLivechatAgentAssigned' | 'IPostLivechatAgentUnassigned', - data: { user: IUser; room: IOmnichannelRoom }, - ): Promise; - livechatEvent( - int: 'IPostLivechatRoomTransferred', - data: { type: 'agent'; room: IRoom['_id']; from: IUser['_id']; to: IUser['_id'] }, - ): Promise; - livechatEvent( - int: 'IPostLivechatRoomTransferred', - data: { type: 'department'; room: IRoom['_id']; from: ILivechatDepartment['_id']; to: ILivechatDepartment['_id'] }, - ): Promise; - livechatEvent(int: 'IPostLivechatGuestSaved', data: ILivechatVisitor['_id']): Promise; - livechatEvent(int: 'IPostLivechatRoomSaved', data: IRoom['_id']): Promise; - livechatEvent(int: 'ILivechatRoomClosedHandler' | 'IPostLivechatRoomStarted' | 'IPostLivechatRoomClosed', data: IRoom): Promise; - livechatEvent(int: AppEvents | AppEvents[keyof AppEvents], data: any): Promise; - } -} diff --git a/packages/apps/src/index.ts b/packages/apps/src/index.ts index 837749af62c0..e137fa3cf007 100644 --- a/packages/apps/src/index.ts +++ b/packages/apps/src/index.ts @@ -1,7 +1,4 @@ -import './bridges/IListenerBridge'; - export * from './converters'; export * from './AppsEngine'; export * from './IAppServerNotifier'; export * from './IAppServerOrchestrator'; -export * from './orchestrator'; diff --git a/packages/apps/src/orchestrator.ts b/packages/apps/src/orchestrator.ts deleted file mode 100644 index 4e3a53d9d5f0..000000000000 --- a/packages/apps/src/orchestrator.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { IAppServerOrchestrator } from './IAppServerOrchestrator'; - -export let Apps: IAppServerOrchestrator | undefined; - -export function registerOrchestrator(orch: IAppServerOrchestrator): void { - Apps = orch; -} diff --git a/yarn.lock b/yarn.lock index 41869c1761c6..72c94c3be6d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39995,7 +39995,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:~5.3.2, typescript@npm:~5.3.3": +"typescript@npm:~5.3.3": version: 5.3.3 resolution: "typescript@npm:5.3.3" bin: @@ -40005,7 +40005,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@~5.3.2#~builtin, typescript@patch:typescript@~5.3.3#~builtin": +"typescript@patch:typescript@~5.3.3#~builtin": version: 5.3.3 resolution: "typescript@patch:typescript@npm%3A5.3.3#~builtin::version=5.3.3&hash=85af82" bin: From 70edbb1112fc738e224ac736883103b3c6e858f3 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Thu, 7 Mar 2024 01:47:01 -0300 Subject: [PATCH 107/207] regression: `MessageToolbarMenu` not rendering on demand (#31916) Co-authored-by: Guilherme Gazzo --- .../message/MessageToolbarHolder.tsx | 34 ++++++++++++------- .../composer/EmojiPicker/EmojiPicker.tsx | 10 +++--- .../client/views/room/hooks/useIsVisible.ts | 34 +++++++++---------- apps/meteor/package.json | 2 +- apps/meteor/tests/e2e/messaging.spec.ts | 1 + .../omnichannel-contact-center.spec.ts | 6 ++-- .../omnichannel-manage-contact.ts | 8 ----- ee/packages/ui-theming/package.json | 2 +- packages/fuselage-ui-kit/package.json | 2 +- packages/gazzodown/package.json | 2 +- packages/ui-avatar/package.json | 2 +- packages/ui-client/package.json | 2 +- packages/ui-composer/package.json | 2 +- packages/ui-video-conf/package.json | 2 +- packages/uikit-playground/package.json | 2 +- yarn.lock | 26 +++++++------- 16 files changed, 70 insertions(+), 67 deletions(-) diff --git a/apps/meteor/client/components/message/MessageToolbarHolder.tsx b/apps/meteor/client/components/message/MessageToolbarHolder.tsx index cd0b8d6574bb..c5314b198374 100644 --- a/apps/meteor/client/components/message/MessageToolbarHolder.tsx +++ b/apps/meteor/client/components/message/MessageToolbarHolder.tsx @@ -6,6 +6,7 @@ import React, { Suspense, lazy, memo, useState } from 'react'; import type { MessageActionContext } from '../../../app/ui-utils/client/lib/MessageAction'; import { useChat } from '../../views/room/contexts/ChatContext'; +import { useIsVisible } from '../../views/room/hooks/useIsVisible'; type MessageToolbarHolderProps = { message: IMessage; @@ -16,27 +17,36 @@ const MessageToolbar = lazy(() => import('./toolbar/MessageToolbar')); const MessageToolbarHolder = ({ message, context }: MessageToolbarHolderProps): ReactElement => { const chat = useChat(); - const [showToolbar, setShowToolbar] = useState(false); + const [ref, isVisible] = useIsVisible(); + const [isToolbarMenuOpen, setIsToolbarMenuOpen] = useState(false); - const depsQueryResult = useQuery(['toolbox', message._id, context], async () => { - const room = await chat?.data.findRoom(); - const subscription = await chat?.data.findSubscription(); - return { - room, - subscription, - }; - }); + const showToolbar = isVisible || isToolbarMenuOpen; + + const depsQueryResult = useQuery( + ['toolbox', message._id, context], + async () => { + const room = await chat?.data.findRoom(); + const subscription = await chat?.data.findSubscription(); + return { + room, + subscription, + }; + }, + { + enabled: showToolbar, + }, + ); return ( - - {depsQueryResult.isSuccess && depsQueryResult.data.room && ( + + {showToolbar && depsQueryResult.isSuccess && depsQueryResult.data.room && ( )} diff --git a/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx b/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx index 054ed258e11e..045952078304 100644 --- a/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx +++ b/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx @@ -1,5 +1,5 @@ import { TextInput, Icon, Button, Divider } from '@rocket.chat/fuselage'; -import { useMediaQuery, useOutsideClick } from '@rocket.chat/fuselage-hooks'; +import { useMediaQuery, useMergedRefs, useOutsideClick } from '@rocket.chat/fuselage-hooks'; import { EmojiPickerCategoryHeader, EmojiPickerContainer, @@ -36,11 +36,13 @@ const EmojiPicker = ({ reference, onClose, onPickEmoji }: EmojiPickerProps) => { const ref = useRef(reference); const categoriesPosition = useRef([]); - const textInputRef = useRef(null); const virtuosoRef = useRef(null); const emojiContainerRef = useRef(null); - const isInputVisible = useIsVisible(textInputRef); + const [isVisibleRef, isInputVisible] = useIsVisible(); + const textInputRef = useRef(); + + const mergedTextInputRef = useMergedRefs(isVisibleRef, textInputRef); const emojiCategories = getCategoriesList(); @@ -187,7 +189,7 @@ const EmojiPicker = ({ reference, onClose, onPickEmoji }: EmojiPickerProps) => { } diff --git a/apps/meteor/client/views/room/hooks/useIsVisible.ts b/apps/meteor/client/views/room/hooks/useIsVisible.ts index d57a0e8db47a..5fec2ce1b1e8 100644 --- a/apps/meteor/client/views/room/hooks/useIsVisible.ts +++ b/apps/meteor/client/views/room/hooks/useIsVisible.ts @@ -1,26 +1,24 @@ import { useDebouncedState, useSafely } from '@rocket.chat/fuselage-hooks'; -import type { RefObject } from 'react'; -import { useEffect } from 'react'; +import { useCallback } from 'react'; -export const useIsVisible = (ref: RefObject): [boolean] => { +export const useIsVisible = () => { const [menuVisibility, setMenuVisibility] = useSafely(useDebouncedState(!!window.DISABLE_ANIMATION, 100)); - useEffect(() => { - if (!ref.current) { - return; - } - const observer = new IntersectionObserver((entries) => { - entries.forEach((entry) => { - setMenuVisibility(entry.isIntersecting); + const callbackRef = useCallback( + (node: HTMLElement | null) => { + if (!node) { + return; + } + const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + setMenuVisibility(entry.isIntersecting); + }); }); - }); - observer.observe(ref.current); + observer.observe(node); + }, + [setMenuVisibility], + ); - return (): void => { - observer.disconnect(); - }; - }, [setMenuVisibility, ref]); - - return [menuVisibility]; + return [callbackRef, menuVisibility] as const; }; diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 68a608f7787f..d58a1cbfae67 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -242,7 +242,7 @@ "@rocket.chat/favicon": "workspace:^", "@rocket.chat/forked-matrix-appservice-bridge": "^4.0.2", "@rocket.chat/forked-matrix-bot-sdk": "^0.6.0-beta.3", - "@rocket.chat/fuselage": "^0.49.0", + "@rocket.chat/fuselage": "^0.50.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/fuselage-toastbar": "~0.31.25", diff --git a/apps/meteor/tests/e2e/messaging.spec.ts b/apps/meteor/tests/e2e/messaging.spec.ts index ab31dcba2729..02c5f01f495c 100644 --- a/apps/meteor/tests/e2e/messaging.spec.ts +++ b/apps/meteor/tests/e2e/messaging.spec.ts @@ -57,6 +57,7 @@ test.describe.serial('Messaging', () => { // move focus to the composer await page.keyboard.press('Tab'); + await page.locator('[data-qa-type="message"]:has-text("msg2")').locator('[role=toolbar][aria-label="Message actions"]').getByRole('button', { name: 'Add reaction' }).waitFor(); await page.keyboard.press('Tab'); await page.keyboard.press('Tab'); await expect(poHomeChannel.composer).toBeFocused(); 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 0f5f1e9605b2..b66ac538d5c2 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-contact-center.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-contact-center.spec.ts @@ -98,16 +98,16 @@ test.describe('Omnichannel Contact Center', () => { await test.step('cancel button', async () => { await poContacts.btnNewContact.click(); await page.waitForURL(URL.newContact); - await expect(poContacts.newContact.newContactTitle).toBeVisible(); + await expect(poContacts.newContact.inputName).toBeVisible(); await poContacts.newContact.btnCancel.click(); await page.waitForURL(URL.contactCenter); - await expect(poContacts.newContact.newContactTitle).not.toBeVisible(); + await expect(poContacts.newContact.inputName).not.toBeVisible(); }); await test.step('open contextual bar', async () => { await poContacts.btnNewContact.click(); await page.waitForURL(URL.newContact); - await expect(poContacts.newContact.newContactTitle).toBeVisible(); + await expect(poContacts.newContact.inputName).toBeVisible(); await expect(poContacts.newContact.btnSave).toBeDisabled(); }); diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-manage-contact.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-manage-contact.ts index 18bf71376626..8f41d535ad65 100644 --- a/apps/meteor/tests/e2e/page-objects/omnichannel-manage-contact.ts +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-manage-contact.ts @@ -7,14 +7,6 @@ export class OmnichannelManageContact { this.page = page; } - get newContactTitle(): Locator { - return this.page.locator('h3 >> text="New contact"'); - } - - get editContactTitle(): Locator { - return this.page.locator('h3 >> text="Edit Contact Profile"'); - } - get inputName(): Locator { return this.page.locator('input[name=name]'); } diff --git a/ee/packages/ui-theming/package.json b/ee/packages/ui-theming/package.json index 6065d22c8764..dda7e9b2d7c2 100644 --- a/ee/packages/ui-theming/package.json +++ b/ee/packages/ui-theming/package.json @@ -4,7 +4,7 @@ "private": true, "devDependencies": { "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.49.0", + "@rocket.chat/fuselage": "^0.50.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/ui-contexts": "workspace:~", diff --git a/packages/fuselage-ui-kit/package.json b/packages/fuselage-ui-kit/package.json index a9a9e7a7fd99..2c928b8383e0 100644 --- a/packages/fuselage-ui-kit/package.json +++ b/packages/fuselage-ui-kit/package.json @@ -63,7 +63,7 @@ "@babel/preset-typescript": "~7.22.15", "@rocket.chat/apps-engine": "1.41.0", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.49.0", + "@rocket.chat/fuselage": "^0.50.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/icons": "^0.33.0", diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index 78226d19012b..dfe25325e7e6 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.49.0", + "@rocket.chat/fuselage": "^0.50.0", "@rocket.chat/fuselage-tokens": "^0.33.0", "@rocket.chat/message-parser": "workspace:^", "@rocket.chat/styled": "~0.31.25", diff --git a/packages/ui-avatar/package.json b/packages/ui-avatar/package.json index 3d4468366c72..b9d3f2c313ee 100644 --- a/packages/ui-avatar/package.json +++ b/packages/ui-avatar/package.json @@ -4,7 +4,7 @@ "private": true, "devDependencies": { "@babel/core": "~7.22.20", - "@rocket.chat/fuselage": "^0.49.0", + "@rocket.chat/fuselage": "^0.50.0", "@rocket.chat/ui-contexts": "workspace:^", "@types/babel__core": "~7.20.3", "@types/react": "~17.0.69", diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index d50cb7738f40..95e5d419f21a 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.49.0", + "@rocket.chat/fuselage": "^0.50.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/mock-providers": "workspace:^", diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json index 65035ce9a77c..331758263835 100644 --- a/packages/ui-composer/package.json +++ b/packages/ui-composer/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.49.0", + "@rocket.chat/fuselage": "^0.50.0", "@rocket.chat/icons": "^0.33.0", "@storybook/addon-actions": "~6.5.16", "@storybook/addon-docs": "~6.5.16", diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index 2079018cb029..eeda6b3ac09c 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.20", "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.49.0", + "@rocket.chat/fuselage": "^0.50.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/icons": "^0.33.0", "@rocket.chat/styled": "~0.31.25", diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index 26b65d9bcbda..d1976dafae8e 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -15,7 +15,7 @@ "@codemirror/tooltip": "^0.19.16", "@lezer/highlight": "^1.1.6", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.49.0", + "@rocket.chat/fuselage": "^0.50.0", "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/fuselage-toastbar": "^0.31.25", diff --git a/yarn.lock b/yarn.lock index 72c94c3be6d4..41719f7f977b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8732,7 +8732,7 @@ __metadata: "@babel/preset-typescript": ~7.22.15 "@rocket.chat/apps-engine": 1.41.0 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.49.0 + "@rocket.chat/fuselage": ^0.50.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/gazzodown": "workspace:^" @@ -8787,9 +8787,9 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/fuselage@npm:^0.49.0": - version: 0.49.0 - resolution: "@rocket.chat/fuselage@npm:0.49.0" +"@rocket.chat/fuselage@npm:^0.50.0": + version: 0.50.0 + resolution: "@rocket.chat/fuselage@npm:0.50.0" dependencies: "@rocket.chat/css-in-js": ^0.31.25 "@rocket.chat/css-supports": ^0.31.25 @@ -8807,7 +8807,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 06adb80ca8399f5ca01364a433dcab9fbd3da819e109b6eff764f7c0ffa19fc533c6ff023fc23b6beba1a389f6b4d483450a5d747bc59987b1355508fa81d6c0 + checksum: 57f043ac92fbc71bfb875ae35073862545959d8daf3f6fb3e94840c5500d184c23b68631246c7b99f9d41341b5f606cf2f340cf487ae455d724a2d37f2388187 languageName: node linkType: hard @@ -8818,7 +8818,7 @@ __metadata: "@babel/core": ~7.22.20 "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.49.0 + "@rocket.chat/fuselage": ^0.50.0 "@rocket.chat/fuselage-tokens": ^0.33.0 "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/styled": ~0.31.25 @@ -9174,7 +9174,7 @@ __metadata: "@rocket.chat/favicon": "workspace:^" "@rocket.chat/forked-matrix-appservice-bridge": ^4.0.2 "@rocket.chat/forked-matrix-bot-sdk": ^0.6.0-beta.3 - "@rocket.chat/fuselage": ^0.49.0 + "@rocket.chat/fuselage": ^0.50.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/fuselage-toastbar": ~0.31.25 @@ -10054,7 +10054,7 @@ __metadata: resolution: "@rocket.chat/ui-avatar@workspace:packages/ui-avatar" dependencies: "@babel/core": ~7.22.20 - "@rocket.chat/fuselage": ^0.49.0 + "@rocket.chat/fuselage": ^0.50.0 "@rocket.chat/ui-contexts": "workspace:^" "@types/babel__core": ~7.20.3 "@types/react": ~17.0.69 @@ -10080,7 +10080,7 @@ __metadata: "@babel/core": ~7.22.20 "@react-aria/toolbar": ^3.0.0-beta.1 "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.49.0 + "@rocket.chat/fuselage": ^0.50.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/mock-providers": "workspace:^" @@ -10133,7 +10133,7 @@ __metadata: "@babel/core": ~7.22.20 "@react-aria/toolbar": ^3.0.0-beta.1 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.49.0 + "@rocket.chat/fuselage": ^0.50.0 "@rocket.chat/icons": ^0.33.0 "@storybook/addon-actions": ~6.5.16 "@storybook/addon-docs": ~6.5.16 @@ -10225,7 +10225,7 @@ __metadata: resolution: "@rocket.chat/ui-theming@workspace:ee/packages/ui-theming" dependencies: "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.49.0 + "@rocket.chat/fuselage": ^0.50.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/ui-contexts": "workspace:~" @@ -10268,7 +10268,7 @@ __metadata: "@rocket.chat/css-in-js": ~0.31.25 "@rocket.chat/emitter": ~0.31.25 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.49.0 + "@rocket.chat/fuselage": ^0.50.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/icons": ^0.33.0 "@rocket.chat/styled": ~0.31.25 @@ -10313,7 +10313,7 @@ __metadata: "@codemirror/tooltip": ^0.19.16 "@lezer/highlight": ^1.1.6 "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.49.0 + "@rocket.chat/fuselage": ^0.50.0 "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/fuselage-toastbar": ^0.31.25 From 7e94b0de75367bc244801474505e21e27d1f9ab3 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Thu, 7 Mar 2024 14:22:26 -0300 Subject: [PATCH 108/207] chore: remove forgotten console.log from federation (#31928) --- .../server/services/federation/infrastructure/matrix/Bridge.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/meteor/server/services/federation/infrastructure/matrix/Bridge.ts b/apps/meteor/server/services/federation/infrastructure/matrix/Bridge.ts index a1015696de98..393142e165d2 100644 --- a/apps/meteor/server/services/federation/infrastructure/matrix/Bridge.ts +++ b/apps/meteor/server/services/federation/infrastructure/matrix/Bridge.ts @@ -724,7 +724,6 @@ export class MatrixBridge implements IFederationBridge { controller: { onEvent: (request) => { const event = request.getData() as unknown as AbstractMatrixEvent; - console.log({ event }); this.eventHandler(event); }, onLog: (line, isError) => { From aa5b8629f0a8375a93f992b616310b2478f06ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Thu, 7 Mar 2024 14:35:29 -0300 Subject: [PATCH 109/207] chore: date scroll improvements (#31872) Co-authored-by: Guilherme Gazzo --- .../views/room/BubbleDate/BubbleDate.tsx | 24 +++++ .../client/views/room/BubbleDate/index.tsx | 1 + .../room/MessageList/MessageListItem.tsx | 10 +- .../client/views/room/body/RoomBody.tsx | 60 ++++++------ .../room/body/UnreadMessagesIndicator.tsx | 9 +- .../Threads/components/ThreadMessageItem.tsx | 10 +- .../Threads/components/ThreadMessageList.tsx | 15 +-- .../client/views/room/hooks/useDateScroll.ts | 94 +++++++++++++------ 8 files changed, 143 insertions(+), 80 deletions(-) create mode 100644 apps/meteor/client/views/room/BubbleDate/BubbleDate.tsx create mode 100644 apps/meteor/client/views/room/BubbleDate/index.tsx diff --git a/apps/meteor/client/views/room/BubbleDate/BubbleDate.tsx b/apps/meteor/client/views/room/BubbleDate/BubbleDate.tsx new file mode 100644 index 000000000000..a841fc3567e7 --- /dev/null +++ b/apps/meteor/client/views/room/BubbleDate/BubbleDate.tsx @@ -0,0 +1,24 @@ +import { Box, Bubble } from '@rocket.chat/fuselage'; +import type { Ref } from 'react'; +import React, { forwardRef } from 'react'; + +import { useFormatDate } from '../../../hooks/useFormatDate'; +import type { BubbleDateProps } from '../hooks/useDateScroll'; + +export const BubbleDate = forwardRef(function BubbleDate( + { bubbleDate, showBubble, bubbleDateStyle, bubbleDateClassName }: BubbleDateProps, + ref: Ref, +) { + const formatDate = useFormatDate(); + return ( + + + {bubbleDate && ( + + {formatDate(bubbleDate)} + + )} + + + ); +}); diff --git a/apps/meteor/client/views/room/BubbleDate/index.tsx b/apps/meteor/client/views/room/BubbleDate/index.tsx new file mode 100644 index 000000000000..7ab14682e444 --- /dev/null +++ b/apps/meteor/client/views/room/BubbleDate/index.tsx @@ -0,0 +1 @@ +export * from './BubbleDate'; diff --git a/apps/meteor/client/views/room/MessageList/MessageListItem.tsx b/apps/meteor/client/views/room/MessageList/MessageListItem.tsx index 538290c8a7ad..bb005020a294 100644 --- a/apps/meteor/client/views/room/MessageList/MessageListItem.tsx +++ b/apps/meteor/client/views/room/MessageList/MessageListItem.tsx @@ -50,10 +50,12 @@ export const MessageListItem = ({ {newDay && ( diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index 1918ef0c5275..14edba49e36e 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -1,5 +1,5 @@ import type { IUser } from '@rocket.chat/core-typings'; -import { Box, Bubble } from '@rocket.chat/fuselage'; +import { Box, Margins } from '@rocket.chat/fuselage'; import { useMergedRefs } from '@rocket.chat/fuselage-hooks'; import { usePermission, useRole, useSetting, useTranslation, useUser, useUserPreference } from '@rocket.chat/ui-contexts'; import type { MouseEventHandler, ReactElement, UIEvent } from 'react'; @@ -9,9 +9,9 @@ import { RoomRoles } from '../../../../app/models/client'; import { isTruthy } from '../../../../lib/isTruthy'; import { CustomScrollbars } from '../../../components/CustomScrollbars'; import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout'; -import { useFormatDate } from '../../../hooks/useFormatDate'; import { useReactiveQuery } from '../../../hooks/useReactiveQuery'; import Announcement from '../Announcement'; +import { BubbleDate } from '../BubbleDate'; import { MessageList } from '../MessageList'; import MessageListErrorBoundary from '../MessageList/MessageListErrorBoundary'; import ComposerContainer from '../composer/ComposerContainer'; @@ -48,7 +48,6 @@ const RoomBody = (): ReactElement => { throw new Error('No ChatContext provided'); } - const formatDate = useFormatDate(); const t = useTranslation(); const isLayoutEmbedded = useEmbeddedLayout(); const room = useRoom(); @@ -56,6 +55,7 @@ const RoomBody = (): ReactElement => { const toolbox = useRoomToolbox(); const admin = useRole('admin'); const subscription = useRoomSubscription(); + const retentionPolicy = useRetentionPolicy(room); const hideFlexTab = useUserPreference('hideFlexTab') || undefined; @@ -98,7 +98,7 @@ const RoomBody = (): ReactElement => { counter: [unread], } = useHandleUnread(room, subscription); - const { innerRef: dateScrollInnerRef, listStyle, bubbleDate, showBubble, style: bubbleDateStyle } = useDateScroll(); + const { innerRef: dateScrollInnerRef, bubbleRef, listStyle, ...bubbleDate } = useDateScroll(); const { innerRef: isAtBottomInnerRef, atBottomRef, sendToBottom, sendToBottomIfNecessary, isAtBottom } = useListIsAtBottom(); @@ -228,32 +228,32 @@ const RoomBody = (): ReactElement => {
                -
                - {uploads.map((upload) => ( - - ))} -
                - {bubbleDate && ( - - - {formatDate(bubbleDate)} - - - )} - {Boolean(unread) && ( - - )} + + +
                + {uploads.map((upload) => ( + + ))} +
                + {Boolean(unread) && ( + + )} + + +
                +
                +
                { diff --git a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageItem.tsx b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageItem.tsx index 1a7da3e0ea63..9165e267a740 100644 --- a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageItem.tsx +++ b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageItem.tsx @@ -41,10 +41,12 @@ export const ThreadMessageItem = ({ {newDay && ( diff --git a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx index 1419f8ba939f..71b71b74bde6 100644 --- a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx +++ b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadMessageList.tsx @@ -1,5 +1,5 @@ import type { IMessage, IThreadMainMessage } from '@rocket.chat/core-typings'; -import { Box, Bubble } from '@rocket.chat/fuselage'; +import { Box } from '@rocket.chat/fuselage'; import { useMergedRefs } from '@rocket.chat/fuselage-hooks'; import { useSetting, useUserPreference } from '@rocket.chat/ui-contexts'; import { differenceInSeconds } from 'date-fns'; @@ -9,7 +9,7 @@ import React, { Fragment } from 'react'; import { MessageTypes } from '../../../../../../app/ui-utils/client'; import { isTruthy } from '../../../../../../lib/isTruthy'; import { CustomScrollbars } from '../../../../../components/CustomScrollbars'; -import { useFormatDate } from '../../../../../hooks/useFormatDate'; +import { BubbleDate } from '../../../BubbleDate'; import { isMessageNewDay } from '../../../MessageList/lib/isMessageNewDay'; import MessageListProvider from '../../../MessageList/providers/MessageListProvider'; import LoadingMessagesIndicator from '../../../body/LoadingMessagesIndicator'; @@ -49,8 +49,7 @@ type ThreadMessageListProps = { }; const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElement => { - const formatDate = useFormatDate(); - const { innerRef, listStyle, bubbleDate, showBubble, style: bubbleDateStyle } = useDateScroll(); + const { innerRef, bubbleRef, listStyle, ...bubbleDate } = useDateScroll(); const { messages, loading } = useLegacyThreadMessages(mainMessage._id); const { @@ -72,13 +71,7 @@ const ThreadMessageList = ({ mainMessage }: ThreadMessageListProps): ReactElemen return (
                - {bubbleDate && ( - - - {formatDate(bubbleDate)} - - - )} + { diff --git a/apps/meteor/client/views/room/hooks/useDateScroll.ts b/apps/meteor/client/views/room/hooks/useDateScroll.ts index 252de3f92ef9..56c86a62839c 100644 --- a/apps/meteor/client/views/room/hooks/useDateScroll.ts +++ b/apps/meteor/client/views/room/hooks/useDateScroll.ts @@ -1,16 +1,22 @@ import { css } from '@rocket.chat/css-in-js'; import { useSafely } from '@rocket.chat/fuselage-hooks'; -import { useCallback, useState } from 'react'; +import type { MutableRefObject } from 'react'; +import { useCallback, useRef, useState } from 'react'; import { withThrottling } from '../../../../lib/utils/highOrderFunctions'; import { useDateListController } from '../providers/DateListProvider'; type useDateScrollReturn = { - bubbleDate: string | undefined; innerRef: (node: HTMLElement | null) => void; - style?: ReturnType; - showBubble: boolean; + bubbleRef: MutableRefObject; listStyle?: ReturnType; +} & BubbleDateProps; + +export type BubbleDateProps = { + bubbleDate: string | undefined; + bubbleDateClassName?: ReturnType; + showBubble: boolean; + bubbleDateStyle?: React.CSSProperties; }; export const useDateScroll = (margin = 8): useDateScrollReturn => { @@ -18,61 +24,85 @@ export const useDateScroll = (margin = 8): useDateScrollReturn => { useState<{ date: string; show: boolean; - style?: ReturnType; + style?: React.CSSProperties; + bubbleDateClassName?: ReturnType; + offset: number; }>({ date: '', show: false, style: undefined, + bubbleDateClassName: undefined, + offset: 0, }), ); const { list } = useDateListController(); + const bubbleRef = useRef(null); + const callbackRef = useCallback( (node: HTMLElement | null) => { if (!node) { return; } + const bubbleOffset = bubbleRef.current?.getBoundingClientRect().bottom || 0; + const onScroll = (() => { let timeout: ReturnType; - return (elements: Set, offset: number) => { + return (elements: Set) => { clearTimeout(timeout); - const bubbleOffset = offset; - // Gets the first non visible message date and sets the bubble date to it - const [date, message] = [...elements].reduce((ret, message) => { + const [date, message, style] = [...elements].reduce((ret, message) => { // Sanitize elements if (!message.dataset.id) { return ret; } - const { top } = message.getBoundingClientRect(); + const { top, height } = message.getBoundingClientRect(); const { id } = message.dataset; - if (top < bubbleOffset) { - // Remove T - . : from the date - return [new Date(id).toISOString(), message]; + // if the bubble if between the divider and the top, position it at the top of the divider + if (top > bubbleOffset && top < bubbleOffset + height) { + return [ + ret[0] || new Date(id).toISOString(), + ret[1] || message, + { + position: 'absolute', + top: `${top - height - bubbleOffset + margin}px`, + left: ' 50%', + translate: '-50%', + zIndex: 11, + }, + ]; + } + + if (top < bubbleOffset + height) { + return [ + new Date(id).toISOString(), + message, + { + position: 'absolute', + top: `${margin}px`, + left: ' 50%', + translate: '-50%', + zIndex: 11, + }, + ]; } return ret; - }, [] as [string, HTMLElement] | []); + }, [] as [string, HTMLElement, { [key: number]: string | number }?] | []); // We always keep the previous date if we don't have a new one, so when the bubble disappears it doesn't flicker - - setBubbleDate(() => ({ + setBubbleDate((current) => ({ + ...current, date: '', ...(date && { date }), show: Boolean(date), - style: css` - position: absolute; - top: ${margin}px; - left: 50%; - translate: -50%; - z-index: 1; - + style, + bubbleDateClassName: css` opacity: 0; transition: opacity 0.6s; - &.bubble-visible { opacity: 1; } @@ -81,7 +111,7 @@ export const useDateScroll = (margin = 8): useDateScrollReturn => { if (message) { const { top } = message.getBoundingClientRect(); - if (top < offset && top > 0) { + if (top < bubbleOffset && top > 0) { return; } } @@ -96,9 +126,9 @@ export const useDateScroll = (margin = 8): useDateScrollReturn => { ); }; })(); + const fn = withThrottling({ wait: 30 })(() => { - const offset = node.getBoundingClientRect().top; - onScroll(list, offset); + onScroll(list); }); node.addEventListener('scroll', fn, { passive: true }); @@ -116,5 +146,13 @@ export const useDateScroll = (margin = 8): useDateScrollReturn => { ` : undefined; - return { innerRef: callbackRef, listStyle, bubbleDate: bubbleDate.date, style: bubbleDate.style, showBubble: Boolean(bubbleDate.show) }; + return { + innerRef: callbackRef, + bubbleRef, + listStyle, + bubbleDate: bubbleDate.date, + bubbleDateStyle: bubbleDate.style, + showBubble: Boolean(bubbleDate.show), + bubbleDateClassName: bubbleDate.bubbleDateClassName, + }; }; From 274f19cc5b95b787fb3991373187e764ab235f60 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Thu, 7 Mar 2024 12:54:42 -0600 Subject: [PATCH 110/207] refactor: Use `settings` instead of model for fetching initial omnichannel config (#31915) --- .../app/livechat/server/lib/LivechatTyped.ts | 17 ++++++++-------- .../end-to-end/api/livechat/02-appearance.ts | 20 ++++++++++++++++++- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index fb1098db4272..d9e1fe84d854 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -33,7 +33,6 @@ import { LivechatDepartmentAgents, ReadReceipts, Rooms, - Settings, LivechatCustomField, } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; @@ -1096,9 +1095,7 @@ class LivechatClass { } async getInitSettings() { - const rcSettings: Record = {}; - - await Settings.findNotHiddenPublic([ + const validSettings = [ 'Livechat_title', 'Livechat_title_color', 'Livechat_enable_message_character_limit', @@ -1128,11 +1125,15 @@ class LivechatClass { 'Livechat_data_processing_consent_text', 'Livechat_show_agent_info', 'Livechat_clear_local_storage_when_chat_ended', - ]).forEach((setting) => { - rcSettings[setting._id] = setting.value; - }); + 'Livechat_history_monitor_type', + ] as const; + + type SettingTypes = (typeof validSettings)[number] | 'Livechat_Show_Connecting'; - rcSettings.Livechat_history_monitor_type = settings.get('Livechat_history_monitor_type'); + const rcSettings = validSettings.reduce>((acc, setting) => { + acc[setting] = settings.get(setting); + return acc; + }, {} as any); rcSettings.Livechat_Show_Connecting = this.showConnecting(); diff --git a/apps/meteor/tests/end-to-end/api/livechat/02-appearance.ts b/apps/meteor/tests/end-to-end/api/livechat/02-appearance.ts index 9d1d3dc7d525..281db92f7233 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/02-appearance.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/02-appearance.ts @@ -3,6 +3,7 @@ import { before, describe, it } from 'mocha'; import type { Response } from 'supertest'; import { getCredentials, api, request, credentials } from '../../../data/api-data'; +import { sleep } from '../../../data/livechat/utils'; import { removePermissionFromAllRoles, restorePermissionToRoles, updatePermission, updateSetting } from '../../../data/permissions.helper'; describe('LIVECHAT - appearance', function () { @@ -88,6 +89,9 @@ describe('LIVECHAT - appearance', function () { .send([{ _id: 'Livechat_registration_form', value: true }]) .expect(200); + // Just enough to get the stream to update cached settings + await sleep(500); + // Get data from livechat/config const { body } = await request.get(api('livechat/config')).set(credentials).expect(200); expect(body.config.settings.registrationForm).to.be.true; @@ -99,6 +103,8 @@ describe('LIVECHAT - appearance', function () { .send([{ _id: 'Livechat_registration_form', value: false }]) .expect(200); + // Just enough to get the stream to update cached settings + await sleep(500); // Get data from livechat/config const { body } = await request.get(api('livechat/config')).set(credentials).expect(200); expect(body.config.settings.registrationForm).to.be.false; @@ -111,6 +117,9 @@ describe('LIVECHAT - appearance', function () { .send([{ _id: 'Livechat_message_character_limit', value: 100 }]) .expect(200); + // Just enough to get the stream to update cached settings + await sleep(500); + // Get data from livechat/config const { body } = await request.get(api('livechat/config')).set(credentials).expect(200); expect(body.config.settings.limitTextLength).to.be.equal(100); @@ -123,7 +132,8 @@ describe('LIVECHAT - appearance', function () { .set(credentials) .send([{ _id: 'Livechat_message_character_limit', value: '100' }]) .expect(200); - + // Just enough to get the stream to update cached settings + await sleep(500); // Get data from livechat/config const { body } = await request.get(api('livechat/config')).set(credentials).expect(200); expect(body.config.settings.limitTextLength).to.be.equal(100); @@ -136,6 +146,8 @@ describe('LIVECHAT - appearance', function () { .send([{ _id: 'Livechat_registration_form', value: 'true' }]) .expect(200); + // Just enough to get the stream to update cached settings + await sleep(500); // Get data from livechat/config const { body } = await request.get(api('livechat/config')).set(credentials).expect(200); expect(body.config.settings.registrationForm).to.be.true; @@ -150,6 +162,8 @@ describe('LIVECHAT - appearance', function () { ]) .expect(200); + // Just enough to get the stream to update cached settings + await sleep(500); // Get data from livechat/config const { body } = await request.get(api('livechat/config')).set(credentials).expect(200); // When setting is 0, we default to Message_MaxAllowedSize value @@ -165,6 +179,8 @@ describe('LIVECHAT - appearance', function () { ]) .expect(200); + // Just enough to get the stream to update cached settings + await sleep(500); // Get data from livechat/config const { body } = await request.get(api('livechat/config')).set(credentials).expect(200); expect(body.config.settings.limitTextLength).to.be.equal(5000); @@ -176,6 +192,8 @@ describe('LIVECHAT - appearance', function () { .send([{ _id: 'Livechat_enable_message_character_limit', value: 'xxxx' }]) .expect(200); + // Just enough to get the stream to update cached settings + await sleep(500); // Get data from livechat/config const { body } = await request.get(api('livechat/config')).set(credentials).expect(200); expect(body.config.settings.limitTextLength).to.be.false; From efab62d4bac139b6d1cea802b63c66eb84b733f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:58:00 -0300 Subject: [PATCH 111/207] regression: `RoomBody` unread and new messages behavior (#31929) --- .../client/views/room/body/RoomBody.tsx | 19 +++++++---------- .../room/body/hooks/useHasNewMessages.ts | 21 +++++++++++++++++++ .../room/body/hooks/useUnreadMessages.ts | 8 +++---- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index 14edba49e36e..acf95eb5c119 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -117,11 +117,19 @@ const RoomBody = (): ReactElement => { const { messageListRef, messageListProps } = useMessageListNavigation(); + const { handleNewMessageButtonClick, handleJumpToRecentButtonClick, handleComposerResize, hasNewMessages, newMessagesScrollRef } = + useHasNewMessages(room._id, user?._id, atBottomRef, { + sendToBottom, + sendToBottomIfNecessary, + isAtBottom, + }); + const innerRef = useMergedRefs( dateScrollInnerRef, innerBoxRef, restoreScrollPositionInnerRef, isAtBottomInnerRef, + newMessagesScrollRef, leaderBannerInnerRef, unreadBarInnerRef, getMoreInnerRef, @@ -131,17 +139,6 @@ const RoomBody = (): ReactElement => { const wrapperBoxRefs = useMergedRefs(unreadBarWrapperRef, leaderBannerWrapperRef); - const { handleNewMessageButtonClick, handleJumpToRecentButtonClick, handleComposerResize, hasNewMessages } = useHasNewMessages( - room._id, - user?._id, - atBottomRef, - { - sendToBottom, - sendToBottomIfNecessary, - isAtBottom, - }, - ); - const handleNavigateToPreviousMessage = useCallback((): void => { chat.messageEditing.toPreviousMessage(); }, [chat.messageEditing]); diff --git a/apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts b/apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts index 653f7b212fa6..e8e68b8a9a6a 100644 --- a/apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts +++ b/apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts @@ -5,6 +5,7 @@ import { useCallback, useEffect, useState } from 'react'; import { RoomHistoryManager } from '../../../../../app/ui-utils/client'; import { callbacks } from '../../../../../lib/callbacks'; +import { withThrottling } from '../../../../../lib/utils/highOrderFunctions'; import { useChat } from '../../contexts/ChatContext'; export const useHasNewMessages = ( @@ -74,7 +75,27 @@ export const useHasNewMessages = ( }; }, [isAtBottom, rid, sendToBottom, uid]); + const ref = useCallback( + (node: HTMLElement | null) => { + if (!node) { + return; + } + + node.addEventListener( + 'scroll', + withThrottling({ wait: 100 })(() => { + atBottomRef.current && setHasNewMessages(false); + }), + { + passive: true, + }, + ); + }, + [atBottomRef], + ); + return { + newMessagesScrollRef: ref, handleNewMessageButtonClick, handleJumpToRecentButtonClick, handleComposerResize, diff --git a/apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts b/apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts index 2a47f18a16bd..5e74164b7882 100644 --- a/apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts +++ b/apps/meteor/client/views/room/body/hooks/useUnreadMessages.ts @@ -37,8 +37,8 @@ export const useHandleUnread = ( room: IRoom, subscription?: ISubscription, ): { - wrapperRef: (wrapper: HTMLDivElement | null) => void; - innerRef: React.MutableRefObject; + innerRef: (wrapper: HTMLDivElement | null) => void; + wrapperRef: React.MutableRefObject; handleUnreadBarJumpToButtonClick: () => void; handleMarkAsReadButtonClick: () => void; counter: readonly [number, Date | undefined]; @@ -171,8 +171,8 @@ export const useHandleUnread = ( ); return { - wrapperRef: ref, - innerRef: messagesBoxRef, + innerRef: ref, + wrapperRef: messagesBoxRef, handleUnreadBarJumpToButtonClick, handleMarkAsReadButtonClick, counter: [unread?.count ?? 0, unread?.since] as const, From 9d0a3f1baedd9bf37f668951d84428e0cb9ad9fc Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:37:28 -0300 Subject: [PATCH 112/207] fix: Mentions stop working when mentioned user changes username (#31860) --- .changeset/large-files-agree.md | 5 +++++ apps/meteor/server/models/raw/Messages.ts | 3 +++ 2 files changed, 8 insertions(+) create mode 100644 .changeset/large-files-agree.md diff --git a/.changeset/large-files-agree.md b/.changeset/large-files-agree.md new file mode 100644 index 000000000000..63d534012662 --- /dev/null +++ b/.changeset/large-files-agree.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed mentions not working when mentioned user changes username. diff --git a/apps/meteor/server/models/raw/Messages.ts b/apps/meteor/server/models/raw/Messages.ts index eb0ef0790ebb..f61f0dda99f1 100644 --- a/apps/meteor/server/models/raw/Messages.ts +++ b/apps/meteor/server/models/raw/Messages.ts @@ -1245,6 +1245,9 @@ export class MessagesRaw extends BaseRaw implements IMessagesModel { 'mentions.$.username': newUsername, 'msg': newMessage, }, + $unset: { + md: 1, + }, }; return this.updateOne(query, update); From 04cb5d533969dc500f99dbb9b7d2c3fc4a2597f5 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Thu, 7 Mar 2024 19:12:01 -0300 Subject: [PATCH 113/207] chore: Migrate notifyUsersOnMessage to TypeScript (#31877) Co-authored-by: Diego Sampaio <8591547+sampaiodiego@users.noreply.github.com> --- ...rsOnMessage.js => notifyUsersOnMessage.ts} | 99 +++++++++---------- .../server/getMentionedTeamMembers.ts | 28 ++---- .../threads/server/hooks/aftersavemessage.ts | 8 +- apps/meteor/lib/callbacks.ts | 3 +- apps/meteor/server/models/raw/Rooms.ts | 2 +- .../meteor/server/models/raw/Subscriptions.ts | 10 +- .../model-typings/src/models/IRoomsModel.ts | 2 +- .../src/models/ISubscriptionsModel.ts | 10 +- 8 files changed, 70 insertions(+), 92 deletions(-) rename apps/meteor/app/lib/server/lib/{notifyUsersOnMessage.js => notifyUsersOnMessage.ts} (63%) diff --git a/apps/meteor/app/lib/server/lib/notifyUsersOnMessage.js b/apps/meteor/app/lib/server/lib/notifyUsersOnMessage.ts similarity index 63% rename from apps/meteor/app/lib/server/lib/notifyUsersOnMessage.js rename to apps/meteor/app/lib/server/lib/notifyUsersOnMessage.ts index ccd06e886b9f..76a18eba3362 100644 --- a/apps/meteor/app/lib/server/lib/notifyUsersOnMessage.js +++ b/apps/meteor/app/lib/server/lib/notifyUsersOnMessage.ts @@ -1,3 +1,5 @@ +import type { IMessage, IRoom, IUser, RoomType } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; import { Subscriptions, Rooms } from '@rocket.chat/models'; import { escapeRegExp } from '@rocket.chat/string-helpers'; import moment from 'moment'; @@ -5,26 +7,16 @@ import moment from 'moment'; import { callbacks } from '../../../../lib/callbacks'; import { settings } from '../../../settings/server'; -/** - * Chechs if a messages contains a user highlight - * - * @param {string} message - * @param {array|undefined} highlights - * - * @returns {boolean} - */ -function messageContainsHighlight(message, highlights) { - if (!highlights || highlights.length === 0) { - return false; - } +function messageContainsHighlight(message: IMessage, highlights: string[]): boolean { + if (!highlights || highlights.length === 0) return false; - return highlights.some((highlight) => { + return highlights.some((highlight: string) => { const regexp = new RegExp(escapeRegExp(highlight), 'i'); return regexp.test(message.msg); }); } -export async function getMentions(message) { +export async function getMentions(message: IMessage): Promise<{ toAll: boolean; toHere: boolean; mentionIds: string[] }> { const { mentions, u: { _id: senderId }, @@ -41,16 +33,13 @@ export async function getMentions(message) { const toAll = mentions.some(({ _id }) => _id === 'all'); const toHere = mentions.some(({ _id }) => _id === 'here'); - const userMentions = mentions.filter((mention) => !mention.type || mention.type === 'user'); - const otherMentions = mentions.filter((mention) => mention?.type !== 'user'); - - const filteredMentions = userMentions.filter(({ _id }) => _id !== senderId && !['all', 'here'].includes(_id)).map(({ _id }) => _id); + const teamsMentions = mentions.filter((mention) => mention.type === 'team'); + const filteredMentions = mentions + .filter((mention) => !mention.type || mention.type === 'user') + .filter(({ _id }) => _id !== senderId && !['all', 'here'].includes(_id)) + .map(({ _id }) => _id); - const mentionIds = await callbacks.run('beforeGetMentions', filteredMentions, { - userMentions, - otherMentions, - message, - }); + const mentionIds = await callbacks.run('beforeGetMentions', filteredMentions, teamsMentions); return { toAll, @@ -59,21 +48,31 @@ export async function getMentions(message) { }; } -const incGroupMentions = async (rid, roomType, excludeUserId, unreadCount) => { +type UnreadCountType = 'all_messages' | 'user_mentions_only' | 'group_mentions_only' | 'user_and_group_mentions_only'; + +const incGroupMentions = async ( + rid: IRoom['_id'], + roomType: RoomType, + excludeUserId: IUser['_id'], + unreadCount: Exclude, +): Promise => { const incUnreadByGroup = ['all_messages', 'group_mentions_only', 'user_and_group_mentions_only'].includes(unreadCount); const incUnread = roomType === 'd' || roomType === 'l' || incUnreadByGroup ? 1 : 0; - await Subscriptions.incGroupMentionsAndUnreadForRoomIdExcludingUserId(rid, excludeUserId, 1, incUnread); }; -const incUserMentions = async (rid, roomType, uids, unreadCount) => { - const incUnreadByUser = ['all_messages', 'user_mentions_only', 'user_and_group_mentions_only'].includes(unreadCount); +const incUserMentions = async ( + rid: IRoom['_id'], + roomType: RoomType, + uids: IUser['_id'][], + unreadCount: Exclude, +): Promise => { + const incUnreadByUser = new Set(['all_messages', 'user_mentions_only', 'user_and_group_mentions_only']).has(unreadCount); const incUnread = roomType === 'd' || roomType === 'l' || incUnreadByUser ? 1 : 0; - await Subscriptions.incUserMentionsAndUnreadForRoomIdAndUserIds(rid, uids, 1, incUnread); }; -export const getUserIdsFromHighlights = async (rid, message) => { +export const getUserIdsFromHighlights = async (rid: IRoom['_id'], message: IMessage): Promise => { const highlightOptions = { projection: { 'userHighlights': 1, 'u._id': 1 } }; const subs = await Subscriptions.findByRoomWithUserHighlights(rid, highlightOptions).toArray(); @@ -84,11 +83,7 @@ export const getUserIdsFromHighlights = async (rid, message) => { .map(({ u: { _id: uid } }) => uid); }; -/* - * {IRoom['t']} roomType - The type of the room - * @returns {string} - The setting value for unread count - */ -const getUnreadSettingCount = (roomType) => { +const getUnreadSettingCount = (roomType: RoomType): UnreadCountType => { let unreadSetting = 'Unread_Count'; switch (roomType) { case 'd': { @@ -104,7 +99,7 @@ const getUnreadSettingCount = (roomType) => { return settings.get(unreadSetting); }; -async function updateUsersSubscriptions(message, room) { +async function updateUsersSubscriptions(message: IMessage, room: IRoom): Promise { // Don't increase unread counter on thread messages if (room != null && !message.tmid) { const { toAll, toHere, mentionIds } = await getMentions(message); @@ -115,16 +110,16 @@ async function updateUsersSubscriptions(message, room) { (await getUserIdsFromHighlights(room._id, message)).forEach((uid) => userIds.add(uid)); - // give priority to user mentions over group mentions + // Give priority to user mentions over group mentions if (userIds.size > 0) { - await incUserMentions(room._id, room.t, [...userIds], unreadCount); + await incUserMentions(room._id, room.t, [...userIds], unreadCount as Exclude); } else if (toAll || toHere) { - await incGroupMentions(room._id, room.t, message.u._id, unreadCount); + await incGroupMentions(room._id, room.t, message.u._id, unreadCount as Exclude); } // this shouldn't run only if has group mentions because it will already exclude mentioned users from the query if (!toAll && !toHere && unreadCount === 'all_messages') { - await Subscriptions.incUnreadForRoomIdExcludingUserIds(room._id, [...userIds, message.u._id]); + await Subscriptions.incUnreadForRoomIdExcludingUserIds(room._id, [...userIds, message.u._id], 1); } } @@ -137,30 +132,25 @@ async function updateUsersSubscriptions(message, room) { ]); } -export async function updateThreadUsersSubscriptions(message, room, replies) { - // const unreadCount = settings.get('Unread_Count'); - - // incUserMentions(room._id, room.t, replies, unreadCount); +export async function updateThreadUsersSubscriptions(message: IMessage, replies: IUser['_id'][]): Promise { + // Don't increase unread counter on thread messages await Subscriptions.setAlertForRoomIdAndUserIds(message.rid, replies); - const repliesPlusSender = [...new Set([message.u._id, ...replies])]; - await Subscriptions.setOpenForRoomIdAndUserIds(message.rid, repliesPlusSender); - await Subscriptions.setLastReplyForRoomIdAndUserIds(message.rid, repliesPlusSender, new Date()); } -export async function notifyUsersOnMessage(message, room) { - // skips this callback if the message was edited and increments it if the edit was way in the past (aka imported) - if (message.editedAt) { - if (Math.abs(moment(message.editedAt).diff()) > 60000) { +export async function notifyUsersOnMessage(message: IMessage, room: IRoom): Promise { + // Skips this callback if the message was edited and increments it if the edit was way in the past (aka imported) + if (isEditedMessage(message)) { + if (Math.abs(moment(message.editedAt).diff(Date.now())) > 60000) { // TODO: Review as I am not sure how else to get around this as the incrementing of the msgs count shouldn't be in this callback await Rooms.incMsgCountById(message.rid, 1); return message; } - // only updates last message if it was edited (skip rest of callback) + // Only updates last message if it was edited (skip rest of callback) if ( settings.get('Store_Last_Message') && (!message.tmid || message.tshow) && @@ -172,20 +162,19 @@ export async function notifyUsersOnMessage(message, room) { return message; } - if (message.ts && Math.abs(moment(message.ts).diff()) > 60000) { + if (message.ts && Math.abs(moment(message.ts).diff(Date.now())) > 60000) { await Rooms.incMsgCountById(message.rid, 1); return message; } - // if message sent ONLY on a thread, skips the rest as it is done on a callback specific to threads + // If message sent ONLY on a thread, skips the rest as it is done on a callback specific to threads if (message.tmid && !message.tshow) { await Rooms.incMsgCountById(message.rid, 1); return message; } // Update all the room activity tracker fields - await Rooms.incMsgCountAndSetLastMessageById(message.rid, 1, message.ts, settings.get('Store_Last_Message') && message); - + await Rooms.incMsgCountAndSetLastMessageById(message.rid, 1, message.ts, settings.get('Store_Last_Message') ? message : undefined); await updateUsersSubscriptions(message, room); return message; diff --git a/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts b/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts index b1355c90cb93..8c836e918a8a 100644 --- a/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts +++ b/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts @@ -1,35 +1,23 @@ import { Team } from '@rocket.chat/core-services'; -import type { IMessage } from '@rocket.chat/core-typings'; +import type { MessageMention } from '@rocket.chat/core-typings'; import { callbacks } from '../../../lib/callbacks'; import { settings } from '../../settings/server'; -interface IExtraDataForNotification { - userMentions: any[]; - otherMentions: any[]; - message: IMessage; -} - -const beforeGetMentions = async (mentionIds: string[], extra?: IExtraDataForNotification) => { - const { otherMentions } = extra ?? {}; - - const teamIds = otherMentions?.filter(({ type }) => type === 'team').map(({ _id }) => _id); - - if (!teamIds?.length) { +const beforeGetMentions = async (mentionIds: string[], teamMentions: MessageMention[]): Promise => { + if (!teamMentions.length) { return mentionIds; } - const members = await Team.getMembersByTeamIds(teamIds, { projection: { userId: 1 } }); - mentionIds.push(...new Set(members.map(({ userId }) => userId).filter((userId) => !mentionIds.includes(userId)))); - - return mentionIds; + const teamsIds = teamMentions.map(({ _id }) => _id); + const members = await Team.getMembersByTeamIds(teamsIds, { projection: { userId: 1 } }); + return [...new Set([...mentionIds, ...members.map(({ userId }) => userId)])]; }; settings.watch('Troubleshoot_Disable_Teams_Mention', (value) => { if (value) { callbacks.remove('beforeGetMentions', 'before-get-mentions-get-teams'); - return; + } else { + callbacks.add('beforeGetMentions', beforeGetMentions, callbacks.priority.MEDIUM, 'before-get-mentions-get-teams'); } - - callbacks.add('beforeGetMentions', beforeGetMentions, callbacks.priority.MEDIUM, 'before-get-mentions-get-teams'); }); diff --git a/apps/meteor/app/threads/server/hooks/aftersavemessage.ts b/apps/meteor/app/threads/server/hooks/aftersavemessage.ts index 4b0f94aa8b52..ab1fa182599b 100644 --- a/apps/meteor/app/threads/server/hooks/aftersavemessage.ts +++ b/apps/meteor/app/threads/server/hooks/aftersavemessage.ts @@ -1,4 +1,4 @@ -import type { IMessage, IRoom } from '@rocket.chat/core-typings'; +import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; import { isEditedMessage } from '@rocket.chat/core-typings'; import { Messages } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; @@ -10,13 +10,13 @@ import { sendMessageNotifications } from '../../../lib/server/lib/sendNotificati import { settings } from '../../../settings/server'; import { reply } from '../functions'; -async function notifyUsersOnReply(message: IMessage, replies: string[], room: IRoom) { +async function notifyUsersOnReply(message: IMessage, replies: IUser['_id'][]) { // skips this callback if the message was edited if (isEditedMessage(message)) { return message; } - await updateThreadUsersSubscriptions(message, room, replies); + await updateThreadUsersSubscriptions(message, replies); return message; } @@ -59,7 +59,7 @@ export async function processThreads(message: IMessage, room: IRoom) { ]), ].filter((userId) => userId !== message.u._id); - await notifyUsersOnReply(message, replies, room); + await notifyUsersOnReply(message, replies); await metaData(message, parentMessage, replies); await notification(message, room, replies); void broadcastMessageFromData({ diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index 2619781800f5..6dfd53b12c65 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -20,6 +20,7 @@ import type { AtLeast, UserStatus, ILivechatDepartment, + MessageMention, } from '@rocket.chat/core-typings'; import type { FilterOperators } from 'mongodb'; @@ -206,6 +207,7 @@ type ChainedCallbackSignatures = { 'archiveRoom': (room: IRoom) => void; 'unarchiveRoom': (room: IRoom) => void; 'roomAvatarChanged': (room: IRoom) => void; + 'beforeGetMentions': (mentionIds: string[], teamMentions: MessageMention[]) => Promise; }; export type Hook = @@ -217,7 +219,6 @@ export type Hook = | 'afterSaveUser' | 'afterValidateNewOAuthUser' | 'beforeActivateUser' - | 'beforeGetMentions' | 'beforeReadMessages' | 'beforeRemoveFromRoom' | 'beforeValidateLogin' diff --git a/apps/meteor/server/models/raw/Rooms.ts b/apps/meteor/server/models/raw/Rooms.ts index 06b6d030424b..e0466b48c114 100644 --- a/apps/meteor/server/models/raw/Rooms.ts +++ b/apps/meteor/server/models/raw/Rooms.ts @@ -1516,7 +1516,7 @@ export class RoomsRaw extends BaseRaw implements IRoomsModel { _id: IRoom['_id'], inc = 1, lastMessageTimestamp: NonNullable, - lastMessage: IRoom['lastMessage'], + lastMessage?: IMessage, ): Promise { const query: Filter = { _id }; diff --git a/apps/meteor/server/models/raw/Subscriptions.ts b/apps/meteor/server/models/raw/Subscriptions.ts index 6b3c184a0e24..de7505619dc0 100644 --- a/apps/meteor/server/models/raw/Subscriptions.ts +++ b/apps/meteor/server/models/raw/Subscriptions.ts @@ -1287,8 +1287,8 @@ export class SubscriptionsRaw extends BaseRaw implements ISubscri } incGroupMentionsAndUnreadForRoomIdExcludingUserId( - roomId: string, - userId: string, + roomId: IRoom['_id'], + userId: IUser['_id'], incGroup = 1, incUnread = 1, ): Promise { @@ -1314,8 +1314,8 @@ export class SubscriptionsRaw extends BaseRaw implements ISubscri } incUserMentionsAndUnreadForRoomIdAndUserIds( - roomId: string, - userIds: string[], + roomId: IRoom['_id'], + userIds: IUser['_id'][], incUser = 1, incUnread = 1, ): Promise { @@ -1384,7 +1384,7 @@ export class SubscriptionsRaw extends BaseRaw implements ISubscri return this.updateMany(query, update); } - setLastReplyForRoomIdAndUserIds(roomId: string, uids: string, lr: Date): Promise { + setLastReplyForRoomIdAndUserIds(roomId: IRoom['_id'], uids: IUser['_id'][], lr: Date): Promise { const query = { 'rid': roomId, 'u._id': { $in: uids }, diff --git a/packages/model-typings/src/models/IRoomsModel.ts b/packages/model-typings/src/models/IRoomsModel.ts index a4743216f7d4..173a373c7df8 100644 --- a/packages/model-typings/src/models/IRoomsModel.ts +++ b/packages/model-typings/src/models/IRoomsModel.ts @@ -237,7 +237,7 @@ export interface IRoomsModel extends IBaseModel { archiveById(rid: string): Promise; unarchiveById(rid: string): Promise; setNameById(rid: string, name: string, fname: string): Promise; - incMsgCountAndSetLastMessageById(rid: string, inc: number, lastMessageTs: Date, lastMessage: IRoom['lastMessage']): Promise; + incMsgCountAndSetLastMessageById(rid: IRoom['_id'], inc: number, lastMessageTs: Date, lastMessage?: IMessage): Promise; incUsersCountById(rid: string, inc: number): Promise; incUsersCountNotDMsByIds(rids: string[], inc: number): Promise; setLastMessageById(rid: string, lastMessage: IRoom['lastMessage']): Promise; diff --git a/packages/model-typings/src/models/ISubscriptionsModel.ts b/packages/model-typings/src/models/ISubscriptionsModel.ts index bb7d4718b7ff..65a3c1106e3e 100644 --- a/packages/model-typings/src/models/ISubscriptionsModel.ts +++ b/packages/model-typings/src/models/ISubscriptionsModel.ts @@ -187,13 +187,13 @@ export interface ISubscriptionsModel extends IBaseModel { updateDirectNameAndFnameByName(name: string, newName?: string, newFname?: string): Promise; incGroupMentionsAndUnreadForRoomIdExcludingUserId( - roomId: string, - userId: string, + roomId: IRoom['_id'], + userId: IUser['_id'], incGroup?: number, incUnread?: number, ): Promise; unsetBlockedByRoomId(rid: string, blocked: string, blocker: string): Promise; - setLastReplyForRoomIdAndUserIds(roomId: string, uids: string, lr: Date): Promise; + setLastReplyForRoomIdAndUserIds(roomId: IRoom['_id'], uids: IUser['_id'][], lr: Date): Promise; updateCustomFieldsByRoomId(rid: string, cfields: Record): Promise; setOpenForRoomIdAndUserIds(roomId: string, uids: string[]): Promise; @@ -201,8 +201,8 @@ export interface ISubscriptionsModel extends IBaseModel { updateTypeByRoomId(roomId: string, type: ISubscription['t']): Promise; setBlockedByRoomId(rid: string, blocked: string, blocker: string): Promise; incUserMentionsAndUnreadForRoomIdAndUserIds( - roomId: string, - userIds: string[], + roomId: IRoom['_id'], + userIds: IUser['_id'][], incUser?: number, incUnread?: number, ): Promise; From ccd4e9b69a7b2ac5e2a22eab5e2faefbdf94c554 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Thu, 7 Mar 2024 19:12:39 -0300 Subject: [PATCH 114/207] regression: Favorite action announcement (#31934) --- .../client/views/room/Header/icons/Favorite.tsx | 16 +++++++++++++--- packages/i18n/src/locales/en.i18n.json | 2 ++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/apps/meteor/client/views/room/Header/icons/Favorite.tsx b/apps/meteor/client/views/room/Header/icons/Favorite.tsx index 4a99a7a0411e..1023a04947c3 100644 --- a/apps/meteor/client/views/room/Header/icons/Favorite.tsx +++ b/apps/meteor/client/views/room/Header/icons/Favorite.tsx @@ -1,7 +1,7 @@ import type { IRoom, ISubscription } from '@rocket.chat/core-typings'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { HeaderState } from '@rocket.chat/ui-client'; -import { useSetting, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; +import { useSetting, useMethod, useTranslation, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import React, { memo } from 'react'; import { useUserIsSubscribed } from '../../contexts/RoomContext'; @@ -9,6 +9,7 @@ import { useUserIsSubscribed } from '../../contexts/RoomContext'; const Favorite = ({ room: { _id, f: favorite = false, t: type, name } }: { room: IRoom & { f?: ISubscription['f'] } }) => { const t = useTranslation(); const subscribed = useUserIsSubscribed(); + const dispatchToastMessage = useToastMessageDispatch(); const isFavoritesEnabled = useSetting('Favorite_Rooms') && ['c', 'p', 'd', 't'].includes(type); const toggleFavorite = useMethod('toggleFavorite'); @@ -18,7 +19,17 @@ const Favorite = ({ room: { _id, f: favorite = false, t: type, name } }: { room: return; } - toggleFavorite(_id, !favorite); + try { + toggleFavorite(_id, !favorite); + dispatchToastMessage({ + type: 'success', + message: !favorite + ? t('__roomName__was_added_to_favorites', { roomName: name }) + : t('__roomName__was_removed_from_favorites', { roomName: name }), + }); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error }); + } }); const favoriteLabel = favorite ? `${t('Unfavorite')} ${name}` : `${t('Favorite')} ${name}`; @@ -30,7 +41,6 @@ const Favorite = ({ room: { _id, f: favorite = false, t: type, name } }: { room: return ( Date: Thu, 7 Mar 2024 19:13:40 -0300 Subject: [PATCH 115/207] chore: improve room `LeaderBar` behavior with date bubble (#31933) --- .../client/views/room/body/LeaderBar.tsx | 12 +--- .../client/views/room/body/RoomBody.tsx | 62 +++++++++---------- .../room/body/UnreadMessagesIndicator.tsx | 6 +- 3 files changed, 34 insertions(+), 46 deletions(-) diff --git a/apps/meteor/client/views/room/body/LeaderBar.tsx b/apps/meteor/client/views/room/body/LeaderBar.tsx index bb0ba305633a..2d5fdb8bbdb8 100644 --- a/apps/meteor/client/views/room/body/LeaderBar.tsx +++ b/apps/meteor/client/views/room/body/LeaderBar.tsx @@ -37,25 +37,19 @@ const LeaderBar = ({ _id, name, username, visible, onAvatarClick, triggerProps } } const roomLeaderStyle = css` - position: absolute; + position: relative; z-index: 9; right: 0; left: 0; - - visibility: visible; - - transition: transform 0.15s cubic-bezier(0.5, 0, 0.1, 1), visibility 0.15s cubic-bezier(0.5, 0, 0.1, 1); + display: flex; &.animated-hidden { - visibility: hidden; - - transform: translateY(-100%); + display: none !important; } `; return ( {
                - -
                - {uploads.map((upload) => ( - - ))} -
                - {Boolean(unread) && ( - + ) : null} +
                + {uploads.map((upload) => ( + - )} + ))} +
                + {Boolean(unread) && ( + + )} - -
                +
                @@ -263,16 +271,6 @@ const RoomBody = (): ReactElement => {
                {t('You_must_join_to_view_messages_in_this_channel')}
                ) : null} - {roomLeader ? ( - - ) : null}
                + Date: Thu, 7 Mar 2024 19:47:27 -0300 Subject: [PATCH 116/207] Revert "regression: Favorite action announcement" (#31935) --- .../client/views/room/Header/icons/Favorite.tsx | 16 +++------------- packages/i18n/src/locales/en.i18n.json | 2 -- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/apps/meteor/client/views/room/Header/icons/Favorite.tsx b/apps/meteor/client/views/room/Header/icons/Favorite.tsx index 1023a04947c3..4a99a7a0411e 100644 --- a/apps/meteor/client/views/room/Header/icons/Favorite.tsx +++ b/apps/meteor/client/views/room/Header/icons/Favorite.tsx @@ -1,7 +1,7 @@ import type { IRoom, ISubscription } from '@rocket.chat/core-typings'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { HeaderState } from '@rocket.chat/ui-client'; -import { useSetting, useMethod, useTranslation, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; +import { useSetting, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; import React, { memo } from 'react'; import { useUserIsSubscribed } from '../../contexts/RoomContext'; @@ -9,7 +9,6 @@ import { useUserIsSubscribed } from '../../contexts/RoomContext'; const Favorite = ({ room: { _id, f: favorite = false, t: type, name } }: { room: IRoom & { f?: ISubscription['f'] } }) => { const t = useTranslation(); const subscribed = useUserIsSubscribed(); - const dispatchToastMessage = useToastMessageDispatch(); const isFavoritesEnabled = useSetting('Favorite_Rooms') && ['c', 'p', 'd', 't'].includes(type); const toggleFavorite = useMethod('toggleFavorite'); @@ -19,17 +18,7 @@ const Favorite = ({ room: { _id, f: favorite = false, t: type, name } }: { room: return; } - try { - toggleFavorite(_id, !favorite); - dispatchToastMessage({ - type: 'success', - message: !favorite - ? t('__roomName__was_added_to_favorites', { roomName: name }) - : t('__roomName__was_removed_from_favorites', { roomName: name }), - }); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } + toggleFavorite(_id, !favorite); }); const favoriteLabel = favorite ? `${t('Unfavorite')} ${name}` : `${t('Favorite')} ${name}`; @@ -41,6 +30,7 @@ const Favorite = ({ room: { _id, f: favorite = false, t: type, name } }: { room: return ( Date: Fri, 8 Mar 2024 11:27:20 -0300 Subject: [PATCH 117/207] fix: Quote attachments are not deleted on message update (#31660) --- .changeset/eight-rice-tease.md | 5 + .../messages/hooks/BeforeSaveJumpToMessage.ts | 20 ++- apps/meteor/tests/end-to-end/api/05-chat.js | 88 +++++++++++ .../meteor/tests/end-to-end/api/24-methods.js | 141 ++++++++++++++++++ .../hooks/BeforeSaveJumpToMessage.tests.ts | 68 ++++++++- .../MessageQuoteAttachment.ts | 3 +- 6 files changed, 315 insertions(+), 10 deletions(-) create mode 100644 .changeset/eight-rice-tease.md diff --git a/.changeset/eight-rice-tease.md b/.changeset/eight-rice-tease.md new file mode 100644 index 000000000000..7b4ac3879d0f --- /dev/null +++ b/.changeset/eight-rice-tease.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed issue with quote attachments still being displayed within the message even after removing link diff --git a/apps/meteor/server/services/messages/hooks/BeforeSaveJumpToMessage.ts b/apps/meteor/server/services/messages/hooks/BeforeSaveJumpToMessage.ts index c88a27cb2b73..f38ff1ee08e3 100644 --- a/apps/meteor/server/services/messages/hooks/BeforeSaveJumpToMessage.ts +++ b/apps/meteor/server/services/messages/hooks/BeforeSaveJumpToMessage.ts @@ -32,6 +32,13 @@ const validateAttachmentDeepness = (message: IMessage, quoteChainLimit: number): return message; }; +const removeQuoteAttachments = (message: IMessage) => { + if (!message.attachments) { + return; + } + message.attachments = message.attachments.filter((attachment) => !isQuoteAttachment(attachment)); +}; + type JumpToMessageInit = { getMessages(messageIds: IMessage['_id'][]): Promise; getRooms(roomIds: IRoom['_id'][]): Promise; @@ -71,6 +78,9 @@ export class BeforeSaveJumpToMessage { useRealName: boolean; }; }): Promise { + // Quote attachments are always rebuilt. Do not keep old ones since they may not still be linked to the message + removeQuoteAttachments(message); + // if no message is present, or the message doesn't have any URL, skip if (!message?.urls?.length) { return message; @@ -143,18 +153,12 @@ export class BeforeSaveJumpToMessage { item.ignoreParse = true; - // Only QuoteAttachments have "message_link" property - const index = message.attachments?.findIndex((a) => isQuoteAttachment(a) && a.message_link === item.url); - if (index !== undefined && index > -1) { - message.attachments?.splice(index, 1); - } - quotes.push(createQuoteAttachment(messageFromUrl, item.url, useRealName, this.getUserAvatarURL(messageFromUrl.u.username))); } if (quotes.length > 0) { - message.attachments = message.attachments || []; - message.attachments.push(...quotes); + const currentAttachments = message.attachments || []; + message.attachments = [...currentAttachments, ...quotes]; } return message; diff --git a/apps/meteor/tests/end-to-end/api/05-chat.js b/apps/meteor/tests/end-to-end/api/05-chat.js index 0fa52cf3392d..aa0b364a33ee 100644 --- a/apps/meteor/tests/end-to-end/api/05-chat.js +++ b/apps/meteor/tests/end-to-end/api/05-chat.js @@ -1112,6 +1112,14 @@ describe('[Chat]', function () { }); describe('/chat.update', () => { + const siteUrl = process.env.SITE_URL || process.env.TEST_API_URL || 'http://localhost:3000'; + let simpleMessageId; + + before('should send simple message in room', async () => { + const res = await sendSimpleMessage({ roomId: 'GENERAL' }); + simpleMessageId = res.body.message._id; + }); + it('should update a message successfully', (done) => { request .post(api('chat.update')) @@ -1129,6 +1137,86 @@ describe('[Chat]', function () { }) .end(done); }); + + it('should add quote attachments to a message', async () => { + const quotedMsgLink = `${siteUrl}/channel/general?msg=${message._id}`; + request + .post(api('chat.update')) + .set(credentials) + .send({ + roomId: 'GENERAL', + msgId: message._id, + text: `Testing quotes ${quotedMsgLink}`, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('message.msg', `Testing quotes ${quotedMsgLink}`); + expect(res.body.message).to.have.property('attachments').that.is.an('array').that.has.lengthOf(1); + expect(res.body.message.attachments[0]).to.have.property('message_link', quotedMsgLink); + }); + }); + + it('should replace a quote attachment in a message', async () => { + const quotedMsgLink = `${siteUrl}/channel/general?msg=${simpleMessageId}`; + request + .post(api('chat.update')) + .set(credentials) + .send({ + roomId: 'GENERAL', + msgId: message._id, + text: `Testing quotes ${quotedMsgLink}`, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('message.msg', `Testing quotes ${quotedMsgLink}`); + expect(res.body.message).to.have.property('attachments').that.is.an('array').that.has.lengthOf(1); + expect(res.body.message.attachments[0]).to.have.property('message_link', quotedMsgLink); + }); + }); + + it('should add multiple quote attachments in a single message', async () => { + const quotedMsgLink = `${siteUrl}/channel/general?msg=${simpleMessageId}`; + const newQuotedMsgLink = `${siteUrl}/channel/general?msg=${message._id}`; + request + .post(api('chat.update')) + .set(credentials) + .send({ + roomId: 'GENERAL', + msgId: message._id, + text: `${newQuotedMsgLink} Testing quotes ${quotedMsgLink}`, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('message.msg', `Testing quotes ${quotedMsgLink}`); + expect(res.body.message).to.have.property('attachments').that.is.an('array').that.has.lengthOf(2); + expect(res.body.message.attachments[0]).to.have.property('message_link', newQuotedMsgLink); + expect(res.body.message.attachments[1]).to.have.property('message_link', quotedMsgLink); + }); + }); + + it('should erase old quote attachments when updating a message', async () => { + await request + .post(api('chat.update')) + .set(credentials) + .send({ + roomId: 'GENERAL', + msgId: message._id, + text: 'This message was edited via API', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('message.msg', 'This message was edited via API'); + expect(res.body.message).to.have.property('attachments').that.is.an('array').that.has.lengthOf(0); + }); + }); }); describe('[/chat.delete]', () => { diff --git a/apps/meteor/tests/end-to-end/api/24-methods.js b/apps/meteor/tests/end-to-end/api/24-methods.js index fc6c8bf4020c..b9705aa599e1 100644 --- a/apps/meteor/tests/end-to-end/api/24-methods.js +++ b/apps/meteor/tests/end-to-end/api/24-methods.js @@ -1982,9 +1982,12 @@ describe('Meteor.methods', function () { describe('[@updateMessage]', () => { let rid = false; + let roomName = false; let messageId; + let simpleMessageId; let messageWithMarkdownId; let channelName = false; + const siteUrl = process.env.SITE_URL || process.env.TEST_API_URL || 'http://localhost:3000'; before('create room', (done) => { channelName = `methods-test-channel-${Date.now()}`; @@ -2003,10 +2006,16 @@ describe('Meteor.methods', function () { expect(res.body).to.have.nested.property('group.t', 'p'); expect(res.body).to.have.nested.property('group.msgs', 0); rid = res.body.group._id; + roomName = res.body.group.name; }) .end(done); }); + before('send simple message', async () => { + const res = await sendSimpleMessage({ roomId: rid }); + simpleMessageId = res.body.message._id; + }); + before('send message with URL', (done) => { request .post(methodCall('sendMessage')) @@ -2102,6 +2111,138 @@ describe('Meteor.methods', function () { .end(done); }); + it('should add a quote attachment to a message', async () => { + const quotedMsgLink = `${siteUrl}/group/${roomName}?msg=${messageWithMarkdownId}`; + await request + .post(methodCall('updateMessage')) + .set(credentials) + .send({ + message: JSON.stringify({ + method: 'updateMessage', + params: [{ _id: messageId, rid, msg: `${quotedMsgLink} updated` }], + id: 'id', + msg: 'method', + }), + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.a.property('success', true); + expect(res.body).to.have.a.property('message').that.is.a('string'); + }); + + await request + .get(api(`chat.getMessage?msgId=${messageId}`)) + .set(credentials) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('message').that.is.an('object'); + expect(res.body.message).to.have.property('msg', `${quotedMsgLink} updated`); + expect(res.body.message).to.have.property('attachments').that.is.an('array').that.has.lengthOf(1); + expect(res.body.message.attachments[0]).to.have.property('message_link', quotedMsgLink); + }); + }); + + it('should replace a quote attachment in a message', async () => { + const quotedMsgLink = `${siteUrl}/group/${roomName}?msg=${simpleMessageId}`; + await request + .post(methodCall('updateMessage')) + .set(credentials) + .send({ + message: JSON.stringify({ + method: 'updateMessage', + params: [{ _id: messageId, rid, msg: `${quotedMsgLink} updated` }], + id: 'id', + msg: 'method', + }), + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.a.property('success', true); + expect(res.body).to.have.a.property('message').that.is.a('string'); + }); + + await request + .get(api(`chat.getMessage?msgId=${messageId}`)) + .set(credentials) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('message').that.is.an('object'); + expect(res.body.message).to.have.property('msg', `${quotedMsgLink} updated`); + expect(res.body.message).to.have.property('attachments').that.is.an('array').that.has.lengthOf(1); + expect(res.body.message.attachments[0]).to.have.property('message_link', quotedMsgLink); + }); + }); + + it('should add multiple quote attachments in a single message', async () => { + const quotedMsgLink = `${siteUrl}/group/${roomName}?msg=${simpleMessageId}`; + const newQuotedMsgLink = `${siteUrl}/group/${roomName}?msg=${messageWithMarkdownId}`; + await request + .post(methodCall('updateMessage')) + .set(credentials) + .send({ + message: JSON.stringify({ + method: 'updateMessage', + params: [{ _id: messageId, rid, msg: `${newQuotedMsgLink} ${quotedMsgLink} updated` }], + id: 'id', + msg: 'method', + }), + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.a.property('success', true); + expect(res.body).to.have.a.property('message').that.is.a('string'); + }); + + await request + .get(api(`chat.getMessage?msgId=${messageId}`)) + .set(credentials) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('message').that.is.an('object'); + expect(res.body.message).to.have.property('msg', `${newQuotedMsgLink} ${quotedMsgLink} updated`); + expect(res.body.message).to.have.property('attachments').that.is.an('array').that.has.lengthOf(2); + expect(res.body.message.attachments[0]).to.have.property('message_link', newQuotedMsgLink); + expect(res.body.message.attachments[1]).to.have.property('message_link', quotedMsgLink); + }); + }); + + it('should remove a quote attachment from a message', async () => { + await request + .post(methodCall('updateMessage')) + .set(credentials) + .send({ + message: JSON.stringify({ + method: 'updateMessage', + params: [{ _id: messageId, rid, msg: 'updated' }], + id: 'id', + msg: 'method', + }), + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.a.property('success', true); + expect(res.body).to.have.a.property('message').that.is.a('string'); + }); + + await request + .get(api(`chat.getMessage?msgId=${messageId}`)) + .set(credentials) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('message').that.is.an('object'); + expect(res.body.message).to.have.property('msg', 'updated'); + expect(res.body.message).to.have.property('attachments').that.is.an('array').that.has.lengthOf(0); + }); + }); + it('should update a message when bypass time limits permission is enabled', async () => { await Promise.all([ updatePermission('bypass-time-limit-edit-and-delete', ['admin']), diff --git a/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveJumpToMessage.tests.ts b/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveJumpToMessage.tests.ts index 5e5a26b6b268..89a48c231a41 100644 --- a/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveJumpToMessage.tests.ts +++ b/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveJumpToMessage.tests.ts @@ -202,7 +202,7 @@ describe('Create attachments for message URLs', () => { expect(message).to.not.have.property('attachments'); }); - it('should remove other attachments from the message if message_link is the same as the URL', async () => { + it('should not duplicate quote attachment from the message if message_link is the same as the URL', async () => { const jumpToMessage = new BeforeSaveJumpToMessage({ getMessages: async () => [createMessage('linked message', { _id: 'linked' })], getRooms: async () => [createRoom()], @@ -247,6 +247,72 @@ describe('Create attachments for message URLs', () => { expect(attachment).to.have.property('text', 'linked message'); }); + it('should remove existing quote attachments provided in the message if they are not in the urls field', async () => { + const jumpToMessage = new BeforeSaveJumpToMessage({ + getMessages: async () => [createMessage('linked message', { _id: 'linked' })], + getRooms: async () => [createRoom()], + canAccessRoom: async () => true, + getUserAvatarURL: () => 'url', + }); + + const message = await jumpToMessage.createAttachmentForMessageURLs({ + message: createMessage('hey', { + urls: [], + attachments: [ + { + text: 'old attachment', + author_name: 'username', + author_icon: 'url', + message_link: 'https://open.rocket.chat/linked?msg=linked', + ts: new Date(), + }, + ], + }), + user: createUser(), + config: { + chainLimit: 10, + siteUrl: 'https://open.rocket.chat', + useRealName: true, + }, + }); + + expect(message).to.have.property('attachments').and.to.have.lengthOf(0); + }); + + it('should not consider attachments with undefined message_link as quotes', async () => { + const jumpToMessage = new BeforeSaveJumpToMessage({ + getMessages: async () => [createMessage('linked message', { _id: 'linked' })], + getRooms: async () => [createRoom()], + canAccessRoom: async () => true, + getUserAvatarURL: () => 'url', + }); + + const message = await jumpToMessage.createAttachmentForMessageURLs({ + message: createMessage('hey', { + urls: [], + attachments: [ + { + text: 'old attachment', + author_name: 'username', + author_icon: 'url', + message_link: undefined, + ts: new Date(), + }, + ], + }), + user: createUser(), + config: { + chainLimit: 10, + siteUrl: 'https://open.rocket.chat', + useRealName: true, + }, + }); + + expect(message).to.have.property('attachments').and.to.have.lengthOf(1); + const [attachment] = message.attachments ?? []; + expect(attachment).to.have.property('text', 'old attachment'); + }); + it('should return an attachment with the message content if a message URL is provided', async () => { const jumpToMessage = new BeforeSaveJumpToMessage({ getMessages: async () => [createMessage('linked message', { _id: 'linked' })], diff --git a/packages/core-typings/src/IMessage/MessageAttachment/MessageQuoteAttachment.ts b/packages/core-typings/src/IMessage/MessageAttachment/MessageQuoteAttachment.ts index f0135a46438a..f0dad665d28b 100644 --- a/packages/core-typings/src/IMessage/MessageAttachment/MessageQuoteAttachment.ts +++ b/packages/core-typings/src/IMessage/MessageAttachment/MessageQuoteAttachment.ts @@ -13,4 +13,5 @@ export type MessageQuoteAttachment = { attachments?: Array; // TODO this is cauising issues to define a model, see @ts-expect-error at apps/meteor/app/api/server/v1/channels.ts:274 } & MessageAttachmentBase; -export const isQuoteAttachment = (attachment: MessageAttachment): attachment is MessageQuoteAttachment => 'message_link' in attachment; +export const isQuoteAttachment = (attachment: MessageAttachment): attachment is MessageQuoteAttachment => + 'message_link' in attachment && attachment.message_link !== undefined; From 1ca08cac9e36bc15ea5e2a7999e1773f7384e50b Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 8 Mar 2024 13:33:41 -0300 Subject: [PATCH 118/207] fix: livechat sdk reconnect not resubscribing (#31941) --- .changeset/empty-hounds-jog.md | 5 +++++ ee/packages/ddp-client/src/livechat/LivechatClientImpl.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/empty-hounds-jog.md diff --git a/.changeset/empty-hounds-jog.md b/.changeset/empty-hounds-jog.md new file mode 100644 index 000000000000..b6830e0522ef --- /dev/null +++ b/.changeset/empty-hounds-jog.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/ddp-client": patch +--- + +fix: livechat sdk reconnect not resubscribing diff --git a/ee/packages/ddp-client/src/livechat/LivechatClientImpl.ts b/ee/packages/ddp-client/src/livechat/LivechatClientImpl.ts index fa28d48ba5c8..973853678f6e 100644 --- a/ee/packages/ddp-client/src/livechat/LivechatClientImpl.ts +++ b/ee/packages/ddp-client/src/livechat/LivechatClientImpl.ts @@ -381,9 +381,9 @@ export class LivechatClientImpl extends DDPSDK implements LivechatStream, Livech const sdk = new LivechatClientImpl(connection, stream, account, timeoutControl, rest); connection.on('connected', () => { - Object.entries(stream.subscriptions).forEach(([, sub]) => { + for (const [, sub] of stream.subscriptions.entries()) { ddp.subscribeWithId(sub.id, sub.name, sub.params); - }); + } }); return sdk; From 4b58e2fb7b40f19fb7667a764a74ff2a4c1f2115 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 8 Mar 2024 12:21:13 -0600 Subject: [PATCH 119/207] fix: Validate rooms are not taken before processing by queue (#31896) Co-authored-by: Marcos Spessatto Defendi <15324204+MarcosSpessatto@users.noreply.github.com> --- .changeset/strange-jars-invent.md | 5 +++++ apps/meteor/server/services/omnichannel/queue.ts | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 .changeset/strange-jars-invent.md diff --git a/.changeset/strange-jars-invent.md b/.changeset/strange-jars-invent.md new file mode 100644 index 000000000000..b91aa564894f --- /dev/null +++ b/.changeset/strange-jars-invent.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: Validate rooms are not taken before processing by queue. This will prevent an issue that caused a room, that's on an invalid state, to be re-processed by the queue worker, assigning it again to another user despite being already assigned to one. This happens when a room's inquiry gets to an state where it desyncs from the room object. Room is taken & served while inquiry is still queued. This fix will also reconciliate both when something like this happens: whenever the queue picks a chat that's already taken, it will update it's inquiry object to reflect that and avoid processing again. diff --git a/apps/meteor/server/services/omnichannel/queue.ts b/apps/meteor/server/services/omnichannel/queue.ts index 603c5197ed30..218705aa306e 100644 --- a/apps/meteor/server/services/omnichannel/queue.ts +++ b/apps/meteor/server/services/omnichannel/queue.ts @@ -1,6 +1,6 @@ import type { InquiryWithAgentInfo, IOmnichannelQueue } from '@rocket.chat/core-typings'; import { License } from '@rocket.chat/license'; -import { LivechatInquiry } from '@rocket.chat/models'; +import { LivechatInquiry, LivechatRooms } from '@rocket.chat/models'; import { dispatchAgentDelegated } from '../../../app/livechat/server/lib/Helper'; import { RoutingManager } from '../../../app/livechat/server/lib/RoutingManager'; @@ -129,6 +129,15 @@ export class OmnichannelQueue implements IOmnichannelQueue { queueLogger.debug(`Processing inquiry ${inquiry._id} from queue ${queue}`); const { defaultAgent } = inquiry; + + const roomFromDb = await LivechatRooms.findOneById(inquiry.rid, { projection: { servedBy: 1 } }); + + // This is a precaution to avoid taking the same inquiry multiple times. It should not happen, but it's a safety net + if (roomFromDb?.servedBy) { + queueLogger.debug(`Inquiry ${inquiry._id} already taken by agent ${roomFromDb.servedBy._id}. Skipping`); + return true; + } + const room = await RoutingManager.delegateInquiry(inquiry, defaultAgent); const propagateAgentDelegated = async (rid: string, agentId: string) => { From 7396045d45e57861a076a3e25122cd0295d432b8 Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Fri, 8 Mar 2024 16:19:49 -0300 Subject: [PATCH 120/207] refactor: use uiContexts on livechat (#31919) Co-authored-by: Guilherme Gazzo --- packages/livechat/package.json | 3 + packages/livechat/src/api.ts | 2 - .../livechat/src/components/App/index.tsx | 65 ++++++--- .../src/components/Calls/CallIFrame.tsx | 5 +- .../src/components/Calls/JoinCallButton.tsx | 3 + .../hooks/livechatRoomSubscriptionHooks.ts | 49 +++++++ .../src/hooks/useDeleteMessageSubscription.ts | 24 ++++ .../src/hooks/useRoomMessagesSubscription.ts | 18 +++ .../src/hooks/useUserActivitySubscription.ts | 17 +++ packages/livechat/src/lib/connection.ts | 23 ++- packages/livechat/src/lib/room.js | 43 +++--- .../providers/ConnectionStatusProvider.tsx | 42 ++++++ .../livechat/src/providers/SDKProvider.tsx | 26 ++++ .../livechat/src/providers/ServerProvider.tsx | 134 ++++++++++++++++++ .../livechat/src/routes/Chat/container.js | 58 +++++--- packages/livechat/src/store/index.tsx | 9 +- yarn.lock | 3 + 17 files changed, 449 insertions(+), 75 deletions(-) create mode 100644 packages/livechat/src/hooks/livechatRoomSubscriptionHooks.ts create mode 100644 packages/livechat/src/hooks/useDeleteMessageSubscription.ts create mode 100644 packages/livechat/src/hooks/useRoomMessagesSubscription.ts create mode 100644 packages/livechat/src/hooks/useUserActivitySubscription.ts create mode 100644 packages/livechat/src/providers/ConnectionStatusProvider.tsx create mode 100644 packages/livechat/src/providers/SDKProvider.tsx create mode 100644 packages/livechat/src/providers/ServerProvider.tsx diff --git a/packages/livechat/package.json b/packages/livechat/package.json index 9ba1be9afb56..6d130abf7125 100644 --- a/packages/livechat/package.json +++ b/packages/livechat/package.json @@ -31,8 +31,10 @@ "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/ddp-client": "workspace:^", "@rocket.chat/eslint-config": "workspace:^", + "@rocket.chat/fuselage-hooks": "^0.33.0", "@rocket.chat/fuselage-tokens": "^0.33.0", "@rocket.chat/logo": "^0.31.29", + "@rocket.chat/ui-contexts": "workspace:^", "@storybook/addon-essentials": "~6.5.16", "@storybook/addon-postcss": "~2.0.0", "@storybook/preact": "~6.5.16", @@ -107,6 +109,7 @@ "markdown-it": "^11.0.1", "mem": "^6.1.1", "mitt": "^2.1.0", + "path-to-regexp": "^6.2.1", "preact": "10.15.1", "preact-router": "^3.2.1", "query-string": "^7.1.3", diff --git a/packages/livechat/src/api.ts b/packages/livechat/src/api.ts index 643647f4494f..bd2059649cb3 100644 --- a/packages/livechat/src/api.ts +++ b/packages/livechat/src/api.ts @@ -19,5 +19,3 @@ Livechat.rest.use(async function (request, next) { throw error; } }); - -Livechat.connection.connect(); diff --git a/packages/livechat/src/components/App/index.tsx b/packages/livechat/src/components/App/index.tsx index 7fd13a4943ea..7d93dc9ae0cb 100644 --- a/packages/livechat/src/components/App/index.tsx +++ b/packages/livechat/src/components/App/index.tsx @@ -1,27 +1,56 @@ +import { parse } from 'query-string'; + +import ConnectionStatusProvider from '../../providers/ConnectionStatusProvider'; +import SDKProvider from '../../providers/SDKProvider'; +import ServerProvider from '../../providers/ServerProvider'; import { Provider as StoreProvider, Consumer as StoreConsumer } from '../../store'; import App from './App'; +export const host = + window.SERVER_URL ?? parse(window.location.search).serverUrl ?? (process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : null); + +export const useSsl = Boolean((Array.isArray(host) ? host[0] : host)?.match(/^https:/)); + const AppConnector = () => (
                - - {({ config, user, triggered, gdpr, sound, undocked, minimized = true, expanded = false, alerts, modal, dispatch, iframe }) => ( - - )} - + + + + + {({ + config, + user, + triggered, + gdpr, + sound, + undocked, + minimized = true, + expanded = false, + alerts, + modal, + dispatch, + iframe, + }) => ( + + )} + + + +
                ); diff --git a/packages/livechat/src/components/Calls/CallIFrame.tsx b/packages/livechat/src/components/Calls/CallIFrame.tsx index e36f9505a118..8c06e5af31e5 100644 --- a/packages/livechat/src/components/Calls/CallIFrame.tsx +++ b/packages/livechat/src/components/Calls/CallIFrame.tsx @@ -8,12 +8,15 @@ import styles from './styles.scss'; export const CallIframe = () => { const { token, room, incomingCallAlert, ongoingCall } = store.state; - const url = `${getConnectionBaseUrl()}/meet/${room._id}?token=${token}&layout=embedded`; + const url = room && `${getConnectionBaseUrl()}/meet/${room._id}?token=${token}&layout=embedded`; useEffect(() => { window.handleIframeClose = () => store.setState({ incomingCallAlert: { ...incomingCallAlert, show: false } }); window.expandCall = () => { + if (!room) { + return; + } window.open(`${getConnectionBaseUrl()}/meet/${room._id}?token=${token}`, room._id); return store.setState({ incomingCallAlert: { ...incomingCallAlert, show: false }, diff --git a/packages/livechat/src/components/Calls/JoinCallButton.tsx b/packages/livechat/src/components/Calls/JoinCallButton.tsx index 9a276b0e9180..b055f4547cab 100644 --- a/packages/livechat/src/components/Calls/JoinCallButton.tsx +++ b/packages/livechat/src/components/Calls/JoinCallButton.tsx @@ -21,6 +21,9 @@ export const JoinCallButton = ({ t, ...props }: JoinCallButtonProps) => { const { token, room } = store.state; const clickJoinCall = () => { + if (!room) { + return; + } switch (props.callProvider) { case 'video-conference': { window.open(props.url, room._id); diff --git a/packages/livechat/src/hooks/livechatRoomSubscriptionHooks.ts b/packages/livechat/src/hooks/livechatRoomSubscriptionHooks.ts new file mode 100644 index 000000000000..f053c4ddcd3a --- /dev/null +++ b/packages/livechat/src/hooks/livechatRoomSubscriptionHooks.ts @@ -0,0 +1,49 @@ +import { useStream } from '@rocket.chat/ui-contexts'; +import { useEffect } from 'react'; + +import { onAgentChange, onAgentStatusChange, onQueuePositionChange } from '../lib/room'; + +export const useAgentChangeSubscription = (rid: string) => { + const stream = useStream('livechat-room'); + + useEffect(() => { + if (!rid) { + return; + } + return stream(`${rid}`, (data) => { + if (data.type === 'agentData') { + onAgentChange(data.data); + } + }); + }, [rid, stream]); +}; + +export const useAgentStatusChangeSubscription = (rid: string) => { + const stream = useStream('livechat-room'); + + useEffect(() => { + if (!rid) { + return; + } + return stream(`${rid}`, (data) => { + if (data.type === 'agentStatus') { + onAgentStatusChange(data.status); + } + }); + }, [rid, stream]); +}; + +export const useQueuePositionChangeSubscription = (rid: string) => { + const stream = useStream('livechat-room'); + + useEffect(() => { + if (!rid) { + return; + } + return stream(`${rid}`, (data) => { + if (data.type === 'queueData') { + onQueuePositionChange(data.data); + } + }); + }, [rid, stream]); +}; diff --git a/packages/livechat/src/hooks/useDeleteMessageSubscription.ts b/packages/livechat/src/hooks/useDeleteMessageSubscription.ts new file mode 100644 index 000000000000..e50781339be6 --- /dev/null +++ b/packages/livechat/src/hooks/useDeleteMessageSubscription.ts @@ -0,0 +1,24 @@ +import { useStream } from '@rocket.chat/ui-contexts'; +import { useEffect } from 'react'; + +import store from '../store'; + +// TODO: optimize this function +const deleteMessage = (messageId: string) => { + store.setState({ + messages: store.state.messages.filter((message) => message._id !== messageId), + }); +}; + +export const useDeleteMessageSubscription = (rid: string) => { + const stream = useStream('notify-room'); + + useEffect(() => { + if (!rid) { + return; + } + return stream(`${rid}/deleteMessage`, async ({ _id }) => { + deleteMessage(_id); + }); + }, [rid, stream]); +}; diff --git a/packages/livechat/src/hooks/useRoomMessagesSubscription.ts b/packages/livechat/src/hooks/useRoomMessagesSubscription.ts new file mode 100644 index 000000000000..2bb4a2f5229a --- /dev/null +++ b/packages/livechat/src/hooks/useRoomMessagesSubscription.ts @@ -0,0 +1,18 @@ +import type { IMessage } from '@rocket.chat/core-typings'; +import { useStream } from '@rocket.chat/ui-contexts'; +import { useEffect } from 'react'; + +import { onMessage } from '../lib/room'; + +export const useRoomMessagesSubscription = (rid: string, token: string) => { + const stream = useStream('room-messages'); + + useEffect(() => { + if (!rid) { + return; + } + return stream(rid, (msg: IMessage) => { + onMessage(msg); + }); + }, [rid, stream, token]); +}; diff --git a/packages/livechat/src/hooks/useUserActivitySubscription.ts b/packages/livechat/src/hooks/useUserActivitySubscription.ts new file mode 100644 index 000000000000..88cb61066f7c --- /dev/null +++ b/packages/livechat/src/hooks/useUserActivitySubscription.ts @@ -0,0 +1,17 @@ +import { useStream } from '@rocket.chat/ui-contexts'; +import { useEffect } from 'react'; + +import { onUserActivity } from '../lib/room'; + +export const useUserActivitySubscription = (rid: string) => { + const stream = useStream('notify-room'); + + useEffect(() => { + if (!rid) { + return; + } + return stream(`${rid}/user-activity`, (username, activities) => { + onUserActivity(username, activities); + }); + }, [rid, stream]); +}; diff --git a/packages/livechat/src/lib/connection.ts b/packages/livechat/src/lib/connection.ts index 34bcf77541dd..3ea5e6b29087 100644 --- a/packages/livechat/src/lib/connection.ts +++ b/packages/livechat/src/lib/connection.ts @@ -6,11 +6,10 @@ import constants from './constants'; import { loadConfig } from './main'; import { loadMessages } from './room'; -let connectedListener: Promise<() => void> | false; -let disconnectedListener: Promise<() => void> | false; +let connectedListener: (() => void) | undefined; +let disconnectedListener: (() => void) | undefined; let initiated = false; const { livechatDisconnectedAlertId, livechatConnectedAlertId } = constants; -const removeListener = (l: any) => l.stop(); const Connection = { async init() { @@ -27,8 +26,8 @@ const Connection = { await import('../i18next'); this.clearListeners(); await loadConfig(); - // await Livechat.connection.connect(); this.addListeners(); + await Livechat.connection.connect(); this.clearAlerts(); } catch (e) { console.error('Connecting error: ', e); @@ -81,24 +80,20 @@ const Connection = { addListeners() { if (!connectedListener) { - connectedListener = Promise.resolve(Livechat.connection.on('connected', this.handleConnected)); + connectedListener = Livechat.connection.on('connected', this.handleConnected); } if (!disconnectedListener) { - disconnectedListener = Promise.resolve(Livechat.connection.on('disconnected', this.handleDisconnected)); + disconnectedListener = Livechat.connection.on('disconnected', this.handleDisconnected); } }, clearListeners() { - if (connectedListener) { - connectedListener.then(removeListener); - connectedListener = false; - } + connectedListener?.(); + connectedListener = undefined; - if (disconnectedListener) { - disconnectedListener.then(removeListener); - disconnectedListener = false; - } + disconnectedListener?.(); + disconnectedListener = undefined; }, }; diff --git a/packages/livechat/src/lib/room.js b/packages/livechat/src/lib/room.js index af69cd8c4c18..67228d8a1610 100644 --- a/packages/livechat/src/lib/room.js +++ b/packages/livechat/src/lib/room.js @@ -19,8 +19,6 @@ import { handleTranscript } from './transcript'; const commands = new Commands(); export const closeChat = async ({ transcriptRequested } = {}) => { - Livechat.unsubscribeAll(); - if (!transcriptRequested) { await handleTranscript(); } @@ -120,6 +118,22 @@ const doPlaySound = async (message) => { await store.setState({ sound: { ...sound, play: true } }); }; +export const onAgentChange = async (agent) => { + await store.setState({ agent, queueInfo: null }); + parentCall('callback', ['assign-agent', normalizeAgent(agent)]); +}; + +export const onAgentStatusChange = (status) => { + const { agent } = store.state; + agent && store.setState({ agent: { ...agent, status } }); + parentCall('callback', ['agent-status-change', normalizeAgent(agent)]); +}; + +export const onQueuePositionChange = async (queueInfo) => { + await store.setState({ queueInfo }); + parentCall('callback', ['queue-position-change', queueInfo]); +}; + export const initRoom = async () => { const { state } = store; const { room } = state; @@ -134,7 +148,6 @@ export const initRoom = async () => { queueInfo, room: { _id: rid, servedBy }, } = state; - Livechat.subscribeRoom(rid); let roomAgent = agent; if (!roomAgent) { @@ -149,22 +162,6 @@ export const initRoom = async () => { parentCall('callback', 'queue-position-change', queueInfo); } - Livechat.onAgentChange(rid, async (agent) => { - await store.setState({ agent, queueInfo: null }); - parentCall('callback', 'assign-agent', normalizeAgent(agent)); - }); - - Livechat.onAgentStatusChange(rid, (status) => { - const { agent } = store.state; - agent && store.setState({ agent: { ...agent, status } }); - parentCall('callback', 'agent-status-change', normalizeAgent(agent)); - }); - - Livechat.onQueuePositionChange(rid, async (queueInfo) => { - await store.setState({ queueInfo }); - parentCall('callback', 'queue-position-change', queueInfo); - }); - setCookies(rid, token); }; @@ -183,7 +180,7 @@ const transformAgentInformationOnMessage = (message) => { return message; }; -Livechat.onUserActivity((username, activities) => { +export const onUserActivity = (username, activities) => { const isTyping = activities.includes('user-typing'); const { typing, user, agent } = store.state; @@ -203,9 +200,9 @@ Livechat.onUserActivity((username, activities) => { if (!isTyping) { return store.setState({ typing: typing.filter((u) => u !== username) }); } -}); +}; -Livechat.onMessage(async (originalMessage) => { +export const onMessage = async (originalMessage) => { let message = JSON.parse(JSON.stringify(originalMessage)); if (message.ts instanceof Date) { @@ -242,7 +239,7 @@ Livechat.onMessage(async (originalMessage) => { await processUnread(); await doPlaySound(message); -}); +}; export const getGreetingMessages = (messages) => messages && messages.filter((msg) => msg.trigger && msg.triggerAfterRegistration); export const getLatestCallMessage = (messages) => messages && messages.filter((msg) => isVideoCallMessage(msg)).pop(); diff --git a/packages/livechat/src/providers/ConnectionStatusProvider.tsx b/packages/livechat/src/providers/ConnectionStatusProvider.tsx new file mode 100644 index 000000000000..2a9a074d4455 --- /dev/null +++ b/packages/livechat/src/providers/ConnectionStatusProvider.tsx @@ -0,0 +1,42 @@ +import { ConnectionStatusContext } from '@rocket.chat/ui-contexts'; +import type { ReactNode } from 'react'; +import { useMemo } from 'react'; +import { useSyncExternalStore } from 'use-sync-external-store/shim'; + +import { useSDK } from './SDKProvider'; + +const ConnectionStatusProvider = ({ children }: { children: ReactNode }) => { + const sdk = useSDK(); + + const status = useSyncExternalStore( + (cb) => sdk.connection.on('connection', cb), + () => { + switch (sdk.connection.status) { + case 'connecting': + return 'connecting' as const; + case 'connected': + return 'connected' as const; + case 'failed': + return 'failed' as const; + case 'idle': + return 'waiting' as const; + default: + return 'offline' as const; + } + }, + ); + + const value = useMemo( + () => + ({ + status, + connected: status === 'connected', + reconnect: () => sdk.connection.reconnect(), + } as const), + [status, sdk], + ); + + return ; +}; + +export default ConnectionStatusProvider; diff --git a/packages/livechat/src/providers/SDKProvider.tsx b/packages/livechat/src/providers/SDKProvider.tsx new file mode 100644 index 000000000000..6f3090b8d0e4 --- /dev/null +++ b/packages/livechat/src/providers/SDKProvider.tsx @@ -0,0 +1,26 @@ +import type { DDPSDK } from '@rocket.chat/ddp-client'; +import React, { createContext, useMemo } from 'react'; + +import { Livechat } from '../api'; + +type SDKContextValue = { + sdk?: DDPSDK; +}; + +const SDKContext = createContext({}); + +export const useSDK = () => { + const context = React.useContext(SDKContext); + if (!context.sdk) { + throw new Error('useSDK must be used within a SDKProvider'); + } + return context.sdk; +}; + +const SDKProvider = ({ children }: { serverURL: string; children: React.ReactNode }) => { + const sdk = useMemo(() => Livechat, []); + + return {children}; +}; + +export default SDKProvider; diff --git a/packages/livechat/src/providers/ServerProvider.tsx b/packages/livechat/src/providers/ServerProvider.tsx new file mode 100644 index 000000000000..f12d9ace0789 --- /dev/null +++ b/packages/livechat/src/providers/ServerProvider.tsx @@ -0,0 +1,134 @@ +import type { Serialized } from '@rocket.chat/core-typings'; +import { Emitter } from '@rocket.chat/emitter'; +import type { Method, PathFor, OperationParams, OperationResult, UrlParams, PathPattern } from '@rocket.chat/rest-typings'; +import type { + ServerMethodName, + ServerMethodParameters, + ServerMethodReturn, + StreamerCallbackArgs, + UploadResult, + StreamNames, + StreamKeys, +} from '@rocket.chat/ui-contexts'; +import { ServerContext } from '@rocket.chat/ui-contexts'; +import { compile } from 'path-to-regexp'; +import React from 'react'; + +import { host } from '../components/App'; +import store from '../store'; +import { useSDK } from './SDKProvider'; + +const ServerProvider = ({ children }: { children: React.ReactNode }) => { + const sdk = useSDK(); + + const { token } = store.state; + + const absoluteUrl = (path: string): string => { + return `${host}${path}`; + }; + + const callMethod = ( + methodName: MethodName, + ...args: ServerMethodParameters + ): Promise> => sdk.client.callAsync(methodName, ...args); + + const callEndpoint = ({ + method, + pathPattern, + keys, + params, + }: { + method: TMethod; + pathPattern: TPathPattern; + keys: UrlParams; + params: OperationParams; + }): Promise>> => { + const compiledPath = compile(pathPattern, { encode: encodeURIComponent })(keys) as any; + + switch (method) { + case 'GET': + return sdk.rest.get(compiledPath, params as any) as any; + + case 'POST': + return sdk.rest.post(compiledPath, params as any) as any; + + case 'PUT': + return sdk.rest.put(compiledPath, params as never) as never; + + case 'DELETE': + return sdk.rest.delete(compiledPath, params as any) as any; + + default: + throw new Error('Invalid HTTP method'); + } + }; + + const uploadToEndpoint = (endpoint: PathFor<'POST'>, formData: any): Promise => sdk.rest.post(endpoint as any, formData); + + const getStream = >( + streamName: N, + _options?: { + retransmit?: boolean | undefined; + retransmitToSelf?: boolean | undefined; + }, + ): ((eventName: K, callback: (...args: StreamerCallbackArgs) => void) => () => void) => { + return (eventName, callback): (() => void) => { + return sdk.stream(streamName, [eventName, { visitorToken: token, token }], callback as (...args: any[]) => void).stop; + }; + }; + + const ee = new Emitter>(); + + const events = new Map void>(); + + const getSingleStream = >( + streamName: N, + _options?: { + retransmit?: boolean | undefined; + retransmitToSelf?: boolean | undefined; + }, + ): ((eventName: K, callback: (...args: StreamerCallbackArgs) => void) => () => void) => { + const stream = getStream(streamName); + return (eventName, callback): (() => void) => { + ee.on(`${streamName}/${eventName}`, callback); + + const handler = (...args: any[]): void => { + ee.emit(`${streamName}/${eventName}`, ...args); + }; + + const stop = (): void => { + // If someone is still listening, don't unsubscribe + ee.off(`${streamName}/${eventName}`, callback); + + if (ee.has(`${streamName}/${eventName}`)) { + return; + } + + const unsubscribe = events.get(`${streamName}/${eventName}`); + if (unsubscribe) { + unsubscribe(); + events.delete(`${streamName}/${eventName}`); + } + }; + + if (!events.has(`${streamName}/${eventName}`)) { + events.set(`${streamName}/${eventName}`, stream(eventName, handler)); + } + return stop; + }; + }; + + const contextValue = { + // info, + absoluteUrl, + callMethod, + callEndpoint, + uploadToEndpoint, + getStream, + getSingleStream, + }; + + return ; +}; + +export default ServerProvider; diff --git a/packages/livechat/src/routes/Chat/container.js b/packages/livechat/src/routes/Chat/container.js index a6a9ba8451c2..19886a547610 100644 --- a/packages/livechat/src/routes/Chat/container.js +++ b/packages/livechat/src/routes/Chat/container.js @@ -9,6 +9,14 @@ import { canRenderMessage } from '../../helpers/canRenderMessage'; import { debounce } from '../../helpers/debounce'; import { throttle } from '../../helpers/throttle'; import { upsert } from '../../helpers/upsert'; +import { + useAgentChangeSubscription, + useAgentStatusChangeSubscription, + useQueuePositionChangeSubscription, +} from '../../hooks/livechatRoomSubscriptionHooks'; +import { useDeleteMessageSubscription } from '../../hooks/useDeleteMessageSubscription'; +import { useRoomMessagesSubscription } from '../../hooks/useRoomMessagesSubscription'; +import { useUserActivitySubscription } from '../../hooks/useUserActivitySubscription'; import { normalizeQueueAlert } from '../../lib/api'; import constants from '../../lib/constants'; import { getLastReadMessage, loadConfig, processUnread, shouldMarkAsUnread } from '../../lib/main'; @@ -17,6 +25,22 @@ import { createToken } from '../../lib/random'; import { initRoom, closeChat, loadMessages, loadMoreMessages, defaultRoomParams, getGreetingMessages } from '../../lib/room'; import Chat from './component'; +const ChatWrapper = ({ children, rid }) => { + useRoomMessagesSubscription(rid); + + useUserActivitySubscription(rid); + + useDeleteMessageSubscription(rid); + + useAgentChangeSubscription(rid); + + useAgentStatusChangeSubscription(rid); + + useQueuePositionChangeSubscription(rid); + + return children; +}; + class ChatContainer extends Component { state = { room: null, @@ -353,22 +377,24 @@ class ChatContainer extends Component { } render = ({ user, ...props }) => ( - + + + ); } diff --git a/packages/livechat/src/store/index.tsx b/packages/livechat/src/store/index.tsx index 7f31ba739e04..4e7559e0ba28 100644 --- a/packages/livechat/src/store/index.tsx +++ b/packages/livechat/src/store/index.tsx @@ -1,6 +1,7 @@ import type { ILivechatVisitor, ILivechatVisitorDTO, Serialized } from '@rocket.chat/core-typings'; import type { ComponentChildren } from 'preact'; import { Component, createContext } from 'preact'; +import { useContext } from 'preact/hooks'; import type { CustomField } from '../components/Form/CustomFields'; import type { Agent } from '../definitions/agents'; @@ -87,7 +88,7 @@ export type StoreState = { expanded?: boolean; modal?: any; agent?: any; - room?: any; + room?: { _id: string }; noMoreMessages?: boolean; loading?: boolean; department?: string; @@ -221,3 +222,9 @@ export class Provider extends Component { export const { Consumer } = StoreContext; export default store; + +export const useStore = (): StoreContextValue => { + const store = useContext(StoreContext); + + return store; +}; diff --git a/yarn.lock b/yarn.lock index 41719f7f977b..757c559e9899 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8967,12 +8967,14 @@ __metadata: "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/ddp-client": "workspace:^" "@rocket.chat/eslint-config": "workspace:^" + "@rocket.chat/fuselage-hooks": ^0.33.0 "@rocket.chat/fuselage-tokens": ^0.33.0 "@rocket.chat/gazzodown": "workspace:^" "@rocket.chat/logo": ^0.31.29 "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/random": "workspace:~" "@rocket.chat/sdk": ^1.0.0-alpha.42 + "@rocket.chat/ui-contexts": "workspace:^" "@rocket.chat/ui-kit": "workspace:~" "@storybook/addon-essentials": ~6.5.16 "@storybook/addon-postcss": ~2.0.0 @@ -9013,6 +9015,7 @@ __metadata: mini-css-extract-plugin: ~1.6.2 mitt: ^2.1.0 npm-run-all: ^4.1.5 + path-to-regexp: ^6.2.1 postcss-css-variables: ^0.17.0 postcss-dir-pseudo-class: ^5.0.0 postcss-flexbugs-fixes: ^4.2.1 From 703cb7ebb0feec0613ab82a4f86d7244cb0458e5 Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Fri, 8 Mar 2024 16:32:33 -0300 Subject: [PATCH 121/207] regression: undefined guest causing issues (#31932) --- packages/livechat/src/lib/hooks.ts | 2 +- packages/livechat/src/store/index.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/livechat/src/lib/hooks.ts b/packages/livechat/src/lib/hooks.ts index 163e319733c6..f552eca94b48 100644 --- a/packages/livechat/src/lib/hooks.ts +++ b/packages/livechat/src/lib/hooks.ts @@ -36,7 +36,7 @@ const updateIframeGuestData = (data: Partial) => { const iframeGuest = { ...guest, ...data } as StoreState['guest']; - store.setState({ iframe: { ...iframe, guest: iframeGuest } }); + store.setState({ iframe: { ...iframe, guest: iframeGuest || {} } }); if (!user) { return; diff --git a/packages/livechat/src/store/index.tsx b/packages/livechat/src/store/index.tsx index 4e7559e0ba28..c6d5cd5ac089 100644 --- a/packages/livechat/src/store/index.tsx +++ b/packages/livechat/src/store/index.tsx @@ -60,7 +60,7 @@ export type StoreState = { enabled: boolean; }; iframe: { - guest?: Serialized; + guest: Serialized | Record; theme: { title?: string; color?: string; @@ -120,7 +120,7 @@ export const initialState = (): StoreState => ({ play: false, }, iframe: { - guest: undefined, + guest: {}, theme: {}, visible: true, }, From b876e4e0fc2175399c7229a00fb2c7220e7c6c22 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Fri, 8 Mar 2024 18:46:02 -0300 Subject: [PATCH 122/207] fix: convert network broker stopped lifecycle method to async (#31927) Co-authored-by: Diego Sampaio <8591547+sampaiodiego@users.noreply.github.com> --- .changeset/slimy-clocks-argue.md | 6 +++ .../search/server/search.internalService.ts | 4 +- apps/meteor/ee/server/NetworkBroker.ts | 4 +- apps/meteor/ee/server/startup/services.ts | 2 +- .../tests/unit/server/NetworkBroker.tests.ts | 39 +++++++++++++++++++ .../meteor/tests/mocks/server/BrokerMocked.ts | 10 +++-- .../hooks/BeforeSaveCheckMAC.tests.ts | 2 +- packages/core-services/src/LocalBroker.ts | 4 +- packages/core-services/src/lib/Api.ts | 4 +- .../core-services/src/types/IApiService.ts | 2 +- packages/core-services/src/types/IBroker.ts | 2 +- 11 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 .changeset/slimy-clocks-argue.md create mode 100644 apps/meteor/ee/tests/unit/server/NetworkBroker.tests.ts diff --git a/.changeset/slimy-clocks-argue.md b/.changeset/slimy-clocks-argue.md new file mode 100644 index 000000000000..35d4e9e3e433 --- /dev/null +++ b/.changeset/slimy-clocks-argue.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/core-services": patch +--- + +`stopped` lifecycle method was unexpectedly synchronous when using microservices, causing our code to create race conditions. diff --git a/apps/meteor/app/search/server/search.internalService.ts b/apps/meteor/app/search/server/search.internalService.ts index 97de49711284..2fbaa2e1da31 100644 --- a/apps/meteor/app/search/server/search.internalService.ts +++ b/apps/meteor/app/search/server/search.internalService.ts @@ -36,10 +36,10 @@ class Search extends ServiceClassInternal { const service = new Search(); -settings.watch('Search.Provider', () => { +settings.watch('Search.Provider', async () => { if (searchProviderService.activeProvider?.on) { api.registerService(service); } else { - api.destroyService(service); + await api.destroyService(service); } }); diff --git a/apps/meteor/ee/server/NetworkBroker.ts b/apps/meteor/ee/server/NetworkBroker.ts index 3af389e4f3e7..0fed6fca542d 100644 --- a/apps/meteor/ee/server/NetworkBroker.ts +++ b/apps/meteor/ee/server/NetworkBroker.ts @@ -71,12 +71,12 @@ export class NetworkBroker implements IBroker { return this.broker.call(method, data); } - destroyService(instance: IServiceClass): void { + async destroyService(instance: IServiceClass): Promise { const name = instance.getName(); if (!name) { return; } - void this.broker.destroyService(name); + await this.broker.destroyService(name); instance.removeAllListeners(); } diff --git a/apps/meteor/ee/server/startup/services.ts b/apps/meteor/ee/server/startup/services.ts index 86743602d1e1..2f63ddba42a8 100644 --- a/apps/meteor/ee/server/startup/services.ts +++ b/apps/meteor/ee/server/startup/services.ts @@ -33,7 +33,7 @@ if (!License.hasValidLicense()) { void License.onLicense('federation', async () => { const federationServiceEE = await FederationServiceEE.createFederationService(); if (federationService) { - api.destroyService(federationService); + await api.destroyService(federationService); } api.registerService(federationServiceEE); }); diff --git a/apps/meteor/ee/tests/unit/server/NetworkBroker.tests.ts b/apps/meteor/ee/tests/unit/server/NetworkBroker.tests.ts new file mode 100644 index 000000000000..1aac9c33fc33 --- /dev/null +++ b/apps/meteor/ee/tests/unit/server/NetworkBroker.tests.ts @@ -0,0 +1,39 @@ +import { ServiceClass } from '@rocket.chat/core-services'; +import { expect } from 'chai'; +import sinon from 'sinon'; + +import { BrokerMocked } from '../../../../tests/mocks/server/BrokerMocked'; +import { NetworkBroker } from '../../../server/NetworkBroker'; + +class DelayedStopBroker extends BrokerMocked { + async destroyService(name: string) { + const instance = this.services.get(name); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await instance.stopped(); + + await super.destroyService(name); + } +} + +const broker = new NetworkBroker(new DelayedStopBroker() as any); + +describe('NetworkBroker', () => { + it('should wait services to be fully destroyed', async () => { + const stoppedStub = sinon.stub(); + + const instance = new (class extends ServiceClass { + name = 'test'; + + async stopped() { + stoppedStub(); + } + })(); + + broker.createService(instance); + await broker.destroyService(instance); + + expect(stoppedStub.called).to.be.true; + }); +}); diff --git a/apps/meteor/tests/mocks/server/BrokerMocked.ts b/apps/meteor/tests/mocks/server/BrokerMocked.ts index dbab825acfad..fd89044bc781 100644 --- a/apps/meteor/tests/mocks/server/BrokerMocked.ts +++ b/apps/meteor/tests/mocks/server/BrokerMocked.ts @@ -1,12 +1,14 @@ export class BrokerMocked { actions: Record Promise> = {}; - destroyService(): void { - // no op + services: Map = new Map(); + + async destroyService(name: string): Promise { + this.services.delete(name); } - createService(): void { - // no op + createService(instance: any): void { + this.services.set(instance.name, instance); } async call(method: string, data: any): Promise { diff --git a/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveCheckMAC.tests.ts b/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveCheckMAC.tests.ts index 2ee0604d6f81..faed1cdc1609 100644 --- a/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveCheckMAC.tests.ts +++ b/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveCheckMAC.tests.ts @@ -36,7 +36,7 @@ const broker = new BrokerMocked(); describe('Check MAC', () => { before(() => { - api.setBroker(broker); + api.setBroker(broker as any); }); it('should do nothing if not omnichannel room', async () => { diff --git a/packages/core-services/src/LocalBroker.ts b/packages/core-services/src/LocalBroker.ts index 47f4aaba3c80..451ced1cf39f 100644 --- a/packages/core-services/src/LocalBroker.ts +++ b/packages/core-services/src/LocalBroker.ts @@ -34,7 +34,7 @@ export class LocalBroker implements IBroker { return this.call(method, data); } - destroyService(instance: ServiceClass): void { + async destroyService(instance: ServiceClass): Promise { const namespace = instance.getName(); instance.getEvents().forEach((event) => event.listeners.forEach((listener) => this.events.removeListener(event.eventName, listener))); @@ -51,7 +51,7 @@ export class LocalBroker implements IBroker { this.methods.delete(`${namespace}.${method}`); } instance.removeAllListeners(); - instance.stopped(); + await instance.stopped(); } createService(instance: IServiceClass): void { diff --git a/packages/core-services/src/lib/Api.ts b/packages/core-services/src/lib/Api.ts index 61a58301a0cc..d02dfe1f8a56 100644 --- a/packages/core-services/src/lib/Api.ts +++ b/packages/core-services/src/lib/Api.ts @@ -15,13 +15,13 @@ export class Api implements IApiService { this.services.forEach((service) => this.broker?.createService(service)); } - destroyService(instance: IServiceClass): void { + async destroyService(instance: IServiceClass): Promise { if (!this.services.has(instance)) { return; } if (this.broker) { - this.broker.destroyService(instance); + await this.broker.destroyService(instance); } this.services.delete(instance); diff --git a/packages/core-services/src/types/IApiService.ts b/packages/core-services/src/types/IApiService.ts index ef88d57713bc..802ae04ab5e2 100644 --- a/packages/core-services/src/types/IApiService.ts +++ b/packages/core-services/src/types/IApiService.ts @@ -5,7 +5,7 @@ import type { IServiceClass } from './ServiceClass'; export interface IApiService { setBroker(broker: IBroker): void; - destroyService(instance: IServiceClass): void; + destroyService(instance: IServiceClass): Promise; registerService(instance: IServiceClass): void; diff --git a/packages/core-services/src/types/IBroker.ts b/packages/core-services/src/types/IBroker.ts index cd1e0a3ded19..d75b25de3452 100644 --- a/packages/core-services/src/types/IBroker.ts +++ b/packages/core-services/src/types/IBroker.ts @@ -48,7 +48,7 @@ export interface IServiceMetrics { export interface IBroker { metrics?: IServiceMetrics; - destroyService(service: IServiceClass): void; + destroyService(service: IServiceClass): Promise; createService(service: IServiceClass, serviceDependencies?: string[]): void; call(method: string, data: any): Promise; waitAndCall(method: string, data: any): Promise; From 5ad65ff3da3ea0b6292b5cde3000ce81fc94b8ab Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 8 Mar 2024 20:51:43 -0300 Subject: [PATCH 123/207] feat(message-parser): add timestamps pattern (#31810) Co-authored-by: Diego Sampaio <8591547+sampaiodiego@users.noreply.github.com> --- .changeset/stupid-trains-trade.md | 24 ++++ .../client/components/GazzodownText.tsx | 7 + .../markup/elements/StrikeSpan.tsx | 1 + packages/gazzodown/.storybook/main.js | 8 ++ packages/gazzodown/package.json | 1 + packages/gazzodown/src/Markup.stories.tsx | 102 +++++++++----- .../gazzodown/src/MarkupInteractionContext.ts | 2 + .../gazzodown/src/elements/ImageElement.tsx | 1 + .../gazzodown/src/elements/InlineElements.tsx | 13 +- .../gazzodown/src/elements/StrikeSpan.tsx | 1 + .../src/elements/Timestamp/ErrorBoundary.tsx | 21 +++ .../src/elements/Timestamp/index.tsx | 128 ++++++++++++++++++ .../src/elements/Timestamp/timeago.ts | 41 ++++++ packages/i18n/src/locales/en.i18n.json | 4 +- packages/message-parser/README.md | 26 ++++ packages/message-parser/package.json | 2 +- packages/message-parser/src/definitions.ts | 11 ++ packages/message-parser/src/grammar.pegjs | 12 +- packages/message-parser/src/utils.ts | 15 ++ .../message-parser/tests/timestamp.test.ts | 27 ++++ .../src/hooks/useFeaturePreviewList.ts | 10 +- yarn.lock | 8 ++ 22 files changed, 424 insertions(+), 41 deletions(-) create mode 100644 .changeset/stupid-trains-trade.md create mode 100644 packages/gazzodown/src/elements/Timestamp/ErrorBoundary.tsx create mode 100644 packages/gazzodown/src/elements/Timestamp/index.tsx create mode 100644 packages/gazzodown/src/elements/Timestamp/timeago.ts create mode 100644 packages/message-parser/tests/timestamp.test.ts diff --git a/.changeset/stupid-trains-trade.md b/.changeset/stupid-trains-trade.md new file mode 100644 index 000000000000..7fe64964cd04 --- /dev/null +++ b/.changeset/stupid-trains-trade.md @@ -0,0 +1,24 @@ +--- +"@rocket.chat/message-parser": patch +--- + +feat(message-parser): add timestamps pattern + +### Usage + +Pattern: + +- {timestamp} is a Unix timestamp +- {format} is an optional parameter that can be used to customize the date and time format. + +#### Formats + +| Format | Description | Example | +| ------ | ------------------------- | --------------------------------------- | +| `t` | Short time | 12:00 AM | +| `T` | Long time | 12:00:00 AM | +| `d` | Short date | 12/31/2020 | +| `D` | Long date | Thursday, December 31, 2020 | +| `f` | Full date and time | Thursday, December 31, 2020 12:00 AM | +| `F` | Full date and time (long) | Thursday, December 31, 2020 12:00:00 AM | +| `R` | Relative time | 1 year ago | diff --git a/apps/meteor/client/components/GazzodownText.tsx b/apps/meteor/client/components/GazzodownText.tsx index f69bbe1abdc0..73d55495630c 100644 --- a/apps/meteor/client/components/GazzodownText.tsx +++ b/apps/meteor/client/components/GazzodownText.tsx @@ -1,7 +1,9 @@ import type { IRoom } from '@rocket.chat/core-typings'; +import { useLocalStorage } from '@rocket.chat/fuselage-hooks'; import type { ChannelMention, UserMention } from '@rocket.chat/gazzodown'; import { MarkupInteractionContext } from '@rocket.chat/gazzodown'; import { escapeRegExp } from '@rocket.chat/string-helpers'; +import { useFeaturePreview } from '@rocket.chat/ui-client'; import { useLayout, useRouter, useSetting, useUserPreference, useUserId } from '@rocket.chat/ui-contexts'; import type { UIEvent } from 'react'; import React, { useCallback, memo, useMemo } from 'react'; @@ -25,6 +27,9 @@ type GazzodownTextProps = { }; const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTextProps) => { + const enableTimestamp = useFeaturePreview('enable-timestamp-message-parser'); + const [userLanguage] = useLocalStorage('userLanguage', 'en'); + const highlights = useMessageListHighlights(); const { triggerProps, openUserCard } = useUserCard(); @@ -125,6 +130,8 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe ownUserId, showMentionSymbol, triggerProps, + enableTimestamp, + language: userLanguage, }} > {children} 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 78f87aaf314f..94ce0f113938 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 @@ -13,6 +13,7 @@ const styles = StyleSheet.create({ }); type MessageBlock = + | MessageParser.Timestamp | MessageParser.Emoji | MessageParser.ChannelMention | MessageParser.UserMention diff --git a/packages/gazzodown/.storybook/main.js b/packages/gazzodown/.storybook/main.js index d3b23dadc93d..de5a951bbded 100644 --- a/packages/gazzodown/.storybook/main.js +++ b/packages/gazzodown/.storybook/main.js @@ -9,4 +9,12 @@ module.exports = { typescript: { reactDocgen: 'react-docgen-typescript-plugin', }, + async webpackFinal(config) { + config.module.rules.push({ + test: /(date-fns).*\.(ts|js|mjs)x?$/, + include: /node_modules/, + loader: 'babel-loader', + }); + return config; + }, }; diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index dfe25325e7e6..0072ebfd8fd5 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -77,6 +77,7 @@ "react": "*" }, "dependencies": { + "date-fns": "^3.3.1", "highlight.js": "^11.5.1", "react-error-boundary": "^3.1.4" }, diff --git a/packages/gazzodown/src/Markup.stories.tsx b/packages/gazzodown/src/Markup.stories.tsx index 5c75d14e72ab..a481e550857c 100644 --- a/packages/gazzodown/src/Markup.stories.tsx +++ b/packages/gazzodown/src/Markup.stories.tsx @@ -7,6 +7,7 @@ import outdent from 'outdent'; import { ReactElement, Suspense } from 'react'; import Markup from './Markup'; +import { MarkupInteractionContext } from './MarkupInteractionContext'; export default { title: 'Markup', @@ -14,46 +15,48 @@ export default { decorators: [ (Story): ReactElement => ( - - - blockquote { - padding-inline: 8px; - border-radius: 2px; - border-width: 2px; - border-style: solid; - background-color: var(--rcx-color-neutral-100, ${colors.n100}); - border-color: var(--rcx-color-neutral-200, ${colors.n200}); - border-inline-start-color: var(--rcx-color-neutral-600, ${colors.n600}); - - &:hover, - &:focus { - background-color: var(--rcx-color-neutral-200, ${colors.n200}); - border-color: var(--rcx-color-neutral-300, ${colors.n300}); + + + + blockquote { + padding-inline: 8px; + border-radius: 2px; + border-width: 2px; + border-style: solid; + background-color: var(--rcx-color-neutral-100, ${colors.n100}); + border-color: var(--rcx-color-neutral-200, ${colors.n200}); border-inline-start-color: var(--rcx-color-neutral-600, ${colors.n600}); - } - } - > ul.task-list { - > li::before { - display: none; + &:hover, + &:focus { + background-color: var(--rcx-color-neutral-200, ${colors.n200}); + border-color: var(--rcx-color-neutral-300, ${colors.n300}); + border-inline-start-color: var(--rcx-color-neutral-600, ${colors.n600}); + } } - > li > .rcx-check-box > .rcx-check-box__input:focus + .rcx-check-box__fake { - z-index: 1; - } + > ul.task-list { + > li::before { + display: none; + } + + > li > .rcx-check-box > .rcx-check-box__input:focus + .rcx-check-box__fake { + z-index: 1; + } - list-style: none; - margin-inline-start: 0; - padding-inline-start: 0; - } - `} - > - - - - + list-style: none; + margin-inline-start: 0; + padding-inline-start: 0; + } + `} + > + + + + + {/* workaround? */} @@ -75,6 +78,35 @@ Empty.args = { tokens: [], }; +export const Timestamp = Template.bind({}); + +Timestamp.args = { + tokens: parse(`Short time: + Long time: + Short date: + Long date: + Full date: + Full date (long): + Relative time from past: { + const date = new Date(); + date.setHours(date.getHours() - 1); + return date.getTime(); + })()}:R> + Relative to Future: { + const date = new Date(); + date.setHours(date.getHours() + 1); + return date.getTime(); + })()}:R> + + Relative Seconds: { + const date = new Date(); + date.setSeconds(date.getSeconds() - 1); + return date.getTime(); + })()}:R> + +`), +}; + export const BigEmoji = Template.bind({}); BigEmoji.args = { tokens: [ diff --git a/packages/gazzodown/src/MarkupInteractionContext.ts b/packages/gazzodown/src/MarkupInteractionContext.ts index b2fb11f5378b..ebe842301800 100644 --- a/packages/gazzodown/src/MarkupInteractionContext.ts +++ b/packages/gazzodown/src/MarkupInteractionContext.ts @@ -22,6 +22,8 @@ type MarkupInteractionContextValue = { ownUserId?: string | null; showMentionSymbol?: boolean; triggerProps?: AriaButtonProps<'button'>; + enableTimestamp?: boolean; + language?: string; }; export const MarkupInteractionContext = createContext({}); diff --git a/packages/gazzodown/src/elements/ImageElement.tsx b/packages/gazzodown/src/elements/ImageElement.tsx index fc6859084766..52771e38540a 100644 --- a/packages/gazzodown/src/elements/ImageElement.tsx +++ b/packages/gazzodown/src/elements/ImageElement.tsx @@ -3,6 +3,7 @@ import { ReactElement, useMemo } from 'react'; const flattenMarkup = ( markup: + | MessageParser.Timestamp | MessageParser.Markup | MessageParser.InlineCode | MessageParser.Link diff --git a/packages/gazzodown/src/elements/InlineElements.tsx b/packages/gazzodown/src/elements/InlineElements.tsx index d3c13459cf3a..e263510f843a 100644 --- a/packages/gazzodown/src/elements/InlineElements.tsx +++ b/packages/gazzodown/src/elements/InlineElements.tsx @@ -12,12 +12,13 @@ import ItalicSpan from './ItalicSpan'; import LinkSpan from './LinkSpan'; import PlainSpan from './PlainSpan'; import StrikeSpan from './StrikeSpan'; +import Timestamp from './Timestamp'; const CodeElement = lazy(() => import('../code/CodeElement')); const KatexElement = lazy(() => import('../katex/KatexElement')); type InlineElementsProps = { - children: MessageParser.Inlines[]; + children: (MessageParser.Inlines | { fallback: MessageParser.Plain; type: undefined })[]; }; const InlineElements = ({ children }: InlineElementsProps): ReactElement => ( @@ -70,8 +71,16 @@ const InlineElements = ({ children }: InlineElementsProps): ReactElement => ( ); - default: + case 'TIMESTAMP': { + return ; + } + + default: { + if ('fallback' in child) { + return ; + } return null; + } } })} diff --git a/packages/gazzodown/src/elements/StrikeSpan.tsx b/packages/gazzodown/src/elements/StrikeSpan.tsx index 7e67c8b5f662..bbc4b899a40f 100644 --- a/packages/gazzodown/src/elements/StrikeSpan.tsx +++ b/packages/gazzodown/src/elements/StrikeSpan.tsx @@ -13,6 +13,7 @@ import PlainSpan from './PlainSpan'; const CodeElement = lazy(() => import('../code/CodeElement')); type MessageBlock = + | MessageParser.Timestamp | MessageParser.Emoji | MessageParser.ChannelMention | MessageParser.UserMention diff --git a/packages/gazzodown/src/elements/Timestamp/ErrorBoundary.tsx b/packages/gazzodown/src/elements/Timestamp/ErrorBoundary.tsx new file mode 100644 index 000000000000..55ef57ad1dee --- /dev/null +++ b/packages/gazzodown/src/elements/Timestamp/ErrorBoundary.tsx @@ -0,0 +1,21 @@ +import { Component } from 'react'; + +export class ErrorBoundary extends Component<{ fallback: React.ReactNode }, { hasError: boolean }> { + constructor(props: { fallback: React.ReactNode }) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError() { + return { hasError: true }; + } + + render() { + if (this.state.hasError) { + // You can render any custom fallback UI + return this.props.fallback; + } + + return this.props.children; + } +} diff --git a/packages/gazzodown/src/elements/Timestamp/index.tsx b/packages/gazzodown/src/elements/Timestamp/index.tsx new file mode 100644 index 000000000000..812c95c4b68f --- /dev/null +++ b/packages/gazzodown/src/elements/Timestamp/index.tsx @@ -0,0 +1,128 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { Tag } from '@rocket.chat/fuselage'; +import type * as MessageParser from '@rocket.chat/message-parser'; +import { format } from 'date-fns'; +import { useContext, useEffect, useState, type ReactElement } from 'react'; +import { ErrorBoundary } from 'react-error-boundary'; + +import { MarkupInteractionContext } from '../../MarkupInteractionContext'; +import { timeAgo } from './timeago'; + +type BoldSpanProps = { + children: MessageParser.Timestamp; +}; + +// | `f` | Full date and time | Thursday, December 31, 2020 12:00 AM | +// | `F` | Full date and time (long) | Thursday, December 31, 2020 12:00:00 AM | +// | `R` | Relative time | 1 year ago | + +const Timestamp = ({ children }: BoldSpanProps): ReactElement => { + const { enableTimestamp } = useContext(MarkupInteractionContext); + + if (!enableTimestamp) { + return <>{``}; + } + + switch (children.value.format) { + case 't': // Short time format + return ; + case 'T': // Long time format + return ; + case 'd': // Short date format + return ; + case 'D': // Long date format + return ; + case 'f': // Full date and time format + return ; + + case 'F': // Full date and time (long) format + return ; + + case 'R': // Relative time format + return ( + {new Date().toUTCString()}}> + + + ); + + default: + return ; + } +}; + +// eslint-disable-next-line react/no-multi-comp +const ShortTime = ({ value }: { value: number }) =>