From a4a2fedcc5df718a7494fb2ce0fbdc954e53dcc4 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Wed, 15 Sep 2021 10:16:28 -0300 Subject: [PATCH] Chore: Move client helpers (#23178) --- app/action-links/client/lib/actionLinks.js | 2 +- app/apps/client/RealAppsEngineUIHost.js | 2 +- app/apps/client/gameCenter/gameCenter.js | 3 +- app/apps/client/gameCenter/invitePlayers.js | 7 +- app/authorization/client/hasPermission.js | 80 ------------------ app/authorization/client/hasPermission.ts | 82 +++++++++++++++++++ app/autotranslate/client/lib/autotranslate.js | 6 +- app/callbacks/lib/callbacks.js | 2 +- app/chatpal-search/client/template/result.js | 7 +- app/e2e/client/logger.ts | 2 +- app/e2e/client/rocketchat.e2e.js | 2 +- app/emoji-custom/client/lib/emojiCustom.js | 3 - app/emoji/client/emojiParser.js | 2 +- app/emoji/client/index.js | 1 - app/emoji/client/lib/emojiRenderer.js | 27 ------ app/lib/client/UserDeleted.js | 12 --- app/lib/client/index.js | 5 -- app/lib/client/lib/formatDate.js | 53 ------------ app/lib/client/lib/index.js | 10 --- app/lib/client/lib/startup/commands.js | 22 ----- app/lib/client/lib/userRoles.js | 42 ---------- app/livechat/client/lib/dateHandler.js | 3 +- .../client/lib/stream/queueManager.js | 4 +- .../client/views/app/dialog/closeRoom.js | 3 +- .../client/views/app/livechatReadOnly.js | 8 +- .../client/views/app/tabbar/agentEdit.js | 3 +- .../client/views/app/tabbar/agentInfo.js | 3 +- .../client/views/app/tabbar/visitorInfo.js | 9 +- .../views/app/tabbar/visitorTranscript.js | 3 +- app/livestream/client/views/broadcastView.js | 4 +- app/livestream/client/views/liveStreamTab.js | 3 +- app/logger/client/logger.js | 2 +- .../client/actionButton.js | 2 +- app/message-pin/client/actionButton.js | 2 +- .../client/page/snippetPage.js | 4 +- app/message-star/client/actionButton.js | 2 +- app/models/client/models/Roles.js | 4 +- app/nrr/client/nrr.js | 14 +--- app/otr/client/rocketchat.otr.room.js | 4 +- app/search/client/provider/result.js | 2 +- app/slashcommands-status/lib/status.js | 3 +- app/slashcommands-topic/lib/topic.js | 3 +- .../client/components/ThreadComponent.tsx | 2 +- .../client/flextab/messageBoxFollow.js | 4 +- app/threads/client/flextab/thread.js | 5 +- app/threads/client/messageAction/follow.js | 5 +- app/threads/client/messageAction/unfollow.js | 5 +- .../client/tokenpassChannelSettings.js | 3 +- .../client/models/CachedCollection.js | 8 +- app/ui-login/client/login/form.js | 3 +- app/ui-master/client/body.js | 6 +- app/ui-master/client/main.js | 10 +-- app/ui-message/client/message.html | 2 +- app/ui-message/client/message.js | 16 ++-- .../client/messageBox/messageBox.js | 4 +- .../client/messageBox/messageBoxTyping.js | 2 +- app/ui-message/client/messageThread.js | 6 +- .../client/popup/messagePopupConfig.js | 6 +- .../client/popup/messagePopupEmoji.html | 2 +- app/ui-utils/client/config.js | 9 -- app/ui-utils/client/index.js | 8 -- app/ui-utils/client/lib/Layout.js | 17 ---- app/ui-utils/client/lib/MessageAction.js | 13 +-- app/ui-utils/client/lib/RoomHistoryManager.js | 10 +-- app/ui-utils/client/lib/RoomManager.js | 8 +- app/ui-utils/client/lib/avatar.js | 26 ------ app/ui-utils/client/lib/callMethod.js | 28 ------- app/ui-utils/client/lib/fireGlobalEvent.js | 21 ----- .../client/lib/getUidDirectMessage.js | 15 ---- app/ui-utils/client/lib/messageContext.js | 11 +-- app/ui-utils/client/lib/modal.js | 3 +- app/ui-utils/client/lib/openRoom.js | 14 ++-- app/ui-utils/client/lib/prependReplies.js | 23 ------ app/ui-utils/client/lib/rtl.js | 7 -- .../lib/warnUserDeletionMayRemoveRooms.js | 63 -------------- app/ui/client/components/header/header.js | 3 +- app/ui/client/components/icon.js | 2 +- app/ui/client/lib/chatMessages.js | 21 ++--- app/ui/client/lib/fileUpload.js | 2 +- app/ui/client/lib/iframeCommands.js | 2 +- app/ui/client/lib/notification.js | 2 +- app/ui/client/views/app/burger.js | 5 +- .../views/app/lib/getCommonRoomEvents.js | 16 ++-- app/ui/client/views/app/room.js | 11 +-- app/utils/client/index.js | 6 +- app/utils/client/lib/CustomTranslations.js | 22 ----- app/utils/client/lib/RestApiClient.js | 7 +- app/utils/client/lib/baseuri.js | 18 ---- app/utils/client/lib/canDeleteMessage.js | 41 ---------- app/utils/client/lib/handleError.js | 32 -------- app/utils/client/lib/waitUntilFind.ts | 14 ---- app/webrtc/client/WebRTCClass.js | 2 +- app/webrtc/client/screenShare.js | 2 +- client/.eslintrc.js | 10 ++- .../CreateDiscussion/CreateDiscussion.tsx | 2 +- client/components/Emoji.js | 2 +- client/components/MarkdownText.tsx | 2 +- client/components/Message/Body/Link.tsx | 2 +- .../modals/FileUploadModal/FilePreview.tsx | 2 +- .../modals/FileUploadModal/GenericPreview.tsx | 2 +- client/contexts/ServerContext/methods.ts | 3 + .../hooks/lists/useScrollableMessageList.ts | 2 +- client/lib/RoomManager.ts | 2 +- client/lib/{baseuri.ts => baseURI.ts} | 0 client/lib/utils/applyCustomTranslations.ts | 22 +++++ client/lib/utils/call.ts | 22 +++++ client/lib/utils/callWithErrorHandling.ts | 19 +++++ client/lib/utils/canDeleteMessage.ts | 52 ++++++++++++ client/lib/utils/fireGlobalEvent.ts | 26 ++++++ client/lib/{ => utils}/formatBytes.ts | 0 client/lib/utils/formatDate.ts | 13 +++ client/lib/utils/formatDateAndTime.ts | 24 ++++++ client/lib/utils/formatTime.ts | 24 ++++++ client/lib/utils/getAvatarAsPng.ts | 35 ++++++++ client/lib/utils/getConfig.ts | 6 ++ client/lib/{ => utils}/getDateRange.ts | 0 client/lib/utils/getUidDirectMessage.ts | 18 ++++ .../lib/{ => utils}/getUserEmailVerified.ts | 2 +- client/lib/{ => utils}/goToRoomById.ts | 10 +-- client/lib/utils/handleError.ts | 59 +++++++++++++ .../isIE11.js => client/lib/utils/isIE11.ts | 2 +- client/lib/utils/isLayoutEmbedded.ts | 3 + client/lib/utils/isRTL.ts | 8 ++ .../lib/utils/keyCodes.ts | 2 +- .../mapMessageFromApi.ts} | 4 +- client/lib/utils/prependReplies.ts | 31 +++++++ client/lib/utils/renderEmoji.ts | 14 ++++ client/lib/{ => utils}/renderMessageBody.ts | 4 +- client/lib/{ => utils}/renderMessageEmoji.ts | 4 +- client/lib/utils/timeAgo.ts | 27 ++++++ client/lib/utils/waitUntilFind.ts | 15 ++++ client/methods/deleteMessage.js | 2 +- client/providers/ToastMessagesProvider.tsx | 2 +- .../sidebar/header/CreateChannelWithData.js | 2 +- client/sidebar/header/CreateDirectMessage.tsx | 2 +- .../sidebar/hooks/useSidebarPaletteColor.js | 2 +- client/startup/UserDeleted.ts | 13 +++ client/startup/absoluteUrl.ts | 5 ++ .../startup/customTranslations.ts | 8 +- client/startup/e2e.ts | 6 +- client/startup/index.ts | 8 +- .../notifications/konchatNotifications.ts | 8 +- .../startup/rootUrlChange.ts | 28 +++---- client/startup/routes.ts | 2 +- client/startup/slashCommands.ts | 20 +++++ client/startup/startup.ts | 2 +- client/startup/unread.ts | 3 +- client/startup/userRoles.ts | 77 +++++++++++++++++ client/startup/userStatusManuallySet.ts | 2 +- .../templateHelpers/avatarUrlFromUsername.ts | 5 ++ client/templateHelpers/emojiUrlFromName.ts | 5 ++ client/templateHelpers/hasPermission.ts | 5 ++ client/templateHelpers/index.ts | 6 ++ client/templateHelpers/nrrargs.ts | 5 ++ client/templateHelpers/renderEmoji.ts | 5 ++ .../templateHelpers/userHasAllPermission.ts | 14 ++++ client/types/global.d.ts | 4 + client/types/meteor-htmljs.d.ts | 1 + client/views/account/AccountSidebar.js | 5 +- client/views/admin/sidebar/AdminSidebar.js | 5 +- client/views/admin/users/UserInfo.js | 2 +- client/views/blocks/MessageBlock.js | 2 +- client/views/blocks/textParsers.js | 2 +- client/views/hooks/useMembersList.ts | 2 +- .../RealTimeMonitoringPage.js | 2 +- .../sidebar/OmnichannelSidebar.tsx | 5 +- .../QuickActions/hooks/useQuickActions.tsx | 2 +- .../Discussions/DiscussionList.js | 2 +- .../Discussions/normalizeThreadMessage.js | 2 +- .../Discussions/useDiscussionsList.ts | 2 +- .../RoomFiles/hooks/useFilesList.ts | 2 +- .../views/room/contextualBar/Threads/Row.tsx | 11 ++- .../Threads/normalizeThreadMessage.js | 2 +- .../contextualBar/Threads/useThreadsList.ts | 2 +- .../UserInfo/UserInfoWithData.js | 2 +- .../teams/CreateTeamModal/CreateTeamModal.tsx | 2 +- .../channels/hooks/useTeamsChannelList.ts | 4 +- definition/IRole.ts | 6 +- ee/app/auditing/client/utils.js | 2 +- .../client/views/tabbar/cannedResponses.js | 3 +- ee/app/license/client/index.ts | 4 +- ee/client/.eslintrc.js | 10 ++- 182 files changed, 963 insertions(+), 867 deletions(-) delete mode 100644 app/authorization/client/hasPermission.js create mode 100644 app/authorization/client/hasPermission.ts delete mode 100644 app/emoji/client/lib/emojiRenderer.js delete mode 100644 app/lib/client/UserDeleted.js delete mode 100644 app/lib/client/lib/formatDate.js delete mode 100644 app/lib/client/lib/startup/commands.js delete mode 100644 app/lib/client/lib/userRoles.js delete mode 100644 app/ui-utils/client/config.js delete mode 100644 app/ui-utils/client/lib/Layout.js delete mode 100644 app/ui-utils/client/lib/avatar.js delete mode 100644 app/ui-utils/client/lib/callMethod.js delete mode 100644 app/ui-utils/client/lib/fireGlobalEvent.js delete mode 100644 app/ui-utils/client/lib/getUidDirectMessage.js delete mode 100644 app/ui-utils/client/lib/prependReplies.js delete mode 100644 app/ui-utils/client/lib/rtl.js delete mode 100644 app/ui-utils/client/lib/warnUserDeletionMayRemoveRooms.js delete mode 100644 app/utils/client/lib/CustomTranslations.js delete mode 100644 app/utils/client/lib/baseuri.js delete mode 100644 app/utils/client/lib/canDeleteMessage.js delete mode 100644 app/utils/client/lib/handleError.js delete mode 100644 app/utils/client/lib/waitUntilFind.ts rename client/lib/{baseuri.ts => baseURI.ts} (100%) create mode 100644 client/lib/utils/applyCustomTranslations.ts create mode 100644 client/lib/utils/call.ts create mode 100644 client/lib/utils/callWithErrorHandling.ts create mode 100644 client/lib/utils/canDeleteMessage.ts create mode 100644 client/lib/utils/fireGlobalEvent.ts rename client/lib/{ => utils}/formatBytes.ts (100%) create mode 100644 client/lib/utils/formatDate.ts create mode 100644 client/lib/utils/formatDateAndTime.ts create mode 100644 client/lib/utils/formatTime.ts create mode 100644 client/lib/utils/getAvatarAsPng.ts create mode 100644 client/lib/utils/getConfig.ts rename client/lib/{ => utils}/getDateRange.ts (100%) create mode 100644 client/lib/utils/getUidDirectMessage.ts rename client/lib/{ => utils}/getUserEmailVerified.ts (75%) rename client/lib/{ => utils}/goToRoomById.ts (61%) create mode 100644 client/lib/utils/handleError.ts rename app/ui-utils/client/lib/isIE11.js => client/lib/utils/isIE11.ts (90%) create mode 100644 client/lib/utils/isLayoutEmbedded.ts create mode 100644 client/lib/utils/isRTL.ts rename app/ui-utils/client/lib/keyCodes.js => client/lib/utils/keyCodes.ts (97%) rename client/lib/{fromApi.ts => utils/mapMessageFromApi.ts} (75%) create mode 100644 client/lib/utils/prependReplies.ts create mode 100644 client/lib/utils/renderEmoji.ts rename client/lib/{ => utils}/renderMessageBody.ts (78%) rename client/lib/{ => utils}/renderMessageEmoji.ts (52%) create mode 100644 client/lib/utils/timeAgo.ts create mode 100644 client/lib/utils/waitUntilFind.ts create mode 100644 client/startup/UserDeleted.ts create mode 100644 client/startup/absoluteUrl.ts rename app/lib/client/CustomTranslations.js => client/startup/customTranslations.ts (71%) rename app/lib/client/lib/settings.js => client/startup/rootUrlChange.ts (52%) create mode 100644 client/startup/slashCommands.ts create mode 100644 client/startup/userRoles.ts create mode 100644 client/templateHelpers/avatarUrlFromUsername.ts create mode 100644 client/templateHelpers/emojiUrlFromName.ts create mode 100644 client/templateHelpers/hasPermission.ts create mode 100644 client/templateHelpers/nrrargs.ts create mode 100644 client/templateHelpers/renderEmoji.ts create mode 100644 client/templateHelpers/userHasAllPermission.ts diff --git a/app/action-links/client/lib/actionLinks.js b/app/action-links/client/lib/actionLinks.js index b3d911398da..d0809e26de8 100644 --- a/app/action-links/client/lib/actionLinks.js +++ b/app/action-links/client/lib/actionLinks.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { handleError } from '../../../utils/client'; +import { handleError } from '../../../../client/lib/utils/handleError'; import { Messages, Subscriptions } from '../../../models/client'; // Action Links namespace creation. diff --git a/app/apps/client/RealAppsEngineUIHost.js b/app/apps/client/RealAppsEngineUIHost.js index 29802587f9b..d27fdf36ac0 100644 --- a/app/apps/client/RealAppsEngineUIHost.js +++ b/app/apps/client/RealAppsEngineUIHost.js @@ -4,8 +4,8 @@ import { AppsEngineUIHost } from '@rocket.chat/apps-engine/client/AppsEngineUIHo import { Rooms } from '../../models/client'; import { APIClient } from '../../utils/client'; -import { baseURI } from '../../utils/client/lib/baseuri'; import { getUserAvatarURL } from '../../utils/lib/getUserAvatarURL'; +import { baseURI } from '../../../client/lib/baseURI'; export class RealAppsEngineUIHost extends AppsEngineUIHost { constructor() { diff --git a/app/apps/client/gameCenter/gameCenter.js b/app/apps/client/gameCenter/gameCenter.js index 8ffee03cd9c..a23f34837d9 100644 --- a/app/apps/client/gameCenter/gameCenter.js +++ b/app/apps/client/gameCenter/gameCenter.js @@ -2,8 +2,9 @@ import { Template } from 'meteor/templating'; import { ReactiveVar } from 'meteor/reactive-var'; import { modal } from '../../../ui-utils/client'; -import { APIClient, t, handleError } from '../../../utils/client'; +import { APIClient, t } from '../../../utils/client'; import './gameCenter.html'; +import { handleError } from '../../../../client/lib/utils/handleError'; const getExternalComponents = async (instance) => { try { diff --git a/app/apps/client/gameCenter/invitePlayers.js b/app/apps/client/gameCenter/invitePlayers.js index 15579db899c..93f1d9c7309 100644 --- a/app/apps/client/gameCenter/invitePlayers.js +++ b/app/apps/client/gameCenter/invitePlayers.js @@ -10,7 +10,8 @@ import { Session } from 'meteor/session'; import { AutoComplete } from '../../../meteor-autocomplete/client'; import { roomTypes } from '../../../utils/client'; import { ChatRoom } from '../../../models/client'; -import { call, modal } from '../../../ui-utils/client'; +import { modal } from '../../../ui-utils/client'; +import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; import './invitePlayers.html'; @@ -77,7 +78,7 @@ Template.InvitePlayers.events({ const privateGroupName = `${ name.replace(/\s/g, '-') }-${ Random.id(10) }`; try { - const result = await call('createPrivateGroup', privateGroupName, users); + const result = await callWithErrorHandling('createPrivateGroup', privateGroupName, users); roomTypes.openRouteLink(result.t, result); @@ -90,7 +91,7 @@ Template.InvitePlayers.events({ return; } - call('sendMessage', { + callWithErrorHandling('sendMessage', { _id: Random.id(), rid: result.rid, msg: TAPi18n.__('Apps_Game_Center_Play_Game_Together', { name }), diff --git a/app/authorization/client/hasPermission.js b/app/authorization/client/hasPermission.js deleted file mode 100644 index a48549979c7..00000000000 --- a/app/authorization/client/hasPermission.js +++ /dev/null @@ -1,80 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Template } from 'meteor/templating'; - -import { ChatPermissions } from './lib/ChatPermissions'; -import * as Models from '../../models'; -import { AuthorizationUtils } from '../lib/AuthorizationUtils'; - -function atLeastOne(permissions = [], scope, userId) { - userId = userId || Meteor.userId(); - const user = Models.Users.findOneById(userId, { fields: { roles: 1 } }); - - return permissions.some((permissionId) => { - if (user && user.roles) { - if (AuthorizationUtils.isPermissionRestrictedForRoleList(permissionId, user.roles)) { - return false; - } - } - - const permission = ChatPermissions.findOne(permissionId, { fields: { roles: 1 } }); - const roles = (permission && permission.roles) || []; - - return roles.some((roleName) => { - const role = Models.Roles.findOne(roleName, { fields: { scope: 1 } }); - const roleScope = role && role.scope; - const model = Models[roleScope]; - - return model && model.isUserInRole && model.isUserInRole(userId, roleName, scope); - }); - }); -} - -function all(permissions = [], scope, userId) { - userId = userId || Meteor.userId(); - const user = Models.Users.findOneById(userId, { fields: { roles: 1 } }); - - return permissions.every((permissionId) => { - if (user && user.roles) { - if (AuthorizationUtils.isPermissionRestrictedForRoleList(permissionId, user.roles)) { - return false; - } - } - - const permission = ChatPermissions.findOne(permissionId, { fields: { roles: 1 } }); - const roles = (permission && permission.roles) || []; - - return roles.some((roleName) => { - const role = Models.Roles.findOne(roleName, { fields: { scope: 1 } }); - const roleScope = role && role.scope; - const model = Models[roleScope]; - - return model && model.isUserInRole && model.isUserInRole(userId, roleName, scope); - }); - }); -} - -function _hasPermission(permissions, scope, strategy, userId) { - userId = userId || Meteor.userId(); - if (!userId) { - return false; - } - - if (!Models.AuthzCachedCollection.ready.get()) { - return false; - } - - permissions = [].concat(permissions); - return strategy(permissions, scope, userId); -} - -Template.registerHelper('hasPermission', function(permission, scope) { - return _hasPermission(permission, scope, atLeastOne); -}); -Template.registerHelper('userHasAllPermission', function(userId, permission, scope) { - return _hasPermission(permission, scope, all, userId); -}); - -export const hasAllPermission = (permissions, scope) => _hasPermission(permissions, scope, all); -export const hasAtLeastOnePermission = (permissions, scope) => _hasPermission(permissions, scope, atLeastOne); -export const userHasAllPermission = (permissions, scope, userId) => _hasPermission(permissions, scope, all, userId); -export const hasPermission = hasAllPermission; diff --git a/app/authorization/client/hasPermission.ts b/app/authorization/client/hasPermission.ts new file mode 100644 index 00000000000..744903bc506 --- /dev/null +++ b/app/authorization/client/hasPermission.ts @@ -0,0 +1,82 @@ +import { Meteor } from 'meteor/meteor'; + +import { ChatPermissions } from './lib/ChatPermissions'; +import * as Models from '../../models/client'; +import { AuthorizationUtils } from '../lib/AuthorizationUtils'; +import { IUser } from '../../../definition/IUser'; +import { IRole } from '../../../definition/IRole'; +import { IPermission } from '../../../definition/IPermission'; + +const isValidScope = (scope: IRole['scope']): scope is keyof typeof Models => + typeof scope === 'string' && scope in Models; + +const createPermissionValidator = (quantifier: (predicate: (permissionId: IPermission['_id']) => boolean) => boolean) => + (permissionIds: IPermission['_id'][], scope: IRole['scope'], userId: IUser['_id']): boolean => { + const user: IUser | null = Models.Users.findOneById(userId, { fields: { roles: 1 } }); + + const checkEachPermission = quantifier.bind(permissionIds); + + return checkEachPermission((permissionId) => { + if (user?.roles) { + if (AuthorizationUtils.isPermissionRestrictedForRoleList(permissionId, user.roles)) { + return false; + } + } + + const permission: IPermission | null = ChatPermissions.findOne(permissionId, { fields: { roles: 1 } }); + const roles = permission?.roles ?? []; + + return roles.some((roleName) => { + const role = Models.Roles.findOne(roleName, { fields: { scope: 1 } }); + const roleScope = role?.scope; + + if (!isValidScope(roleScope)) { + return false; + } + + const model = Models[roleScope]; + return model.isUserInRole && model.isUserInRole(userId, roleName, scope); + }); + }); + }; + +const atLeastOne = createPermissionValidator(Array.prototype.some); + +const all = createPermissionValidator(Array.prototype.every); + +const validatePermissions = ( + permissions: IPermission['_id'] | IPermission['_id'][], + scope: IRole['scope'], + predicate: (permissionIds: IPermission['_id'][], scope: IRole['scope'], userId: IUser['_id']) => boolean, + userId?: IUser['_id'] | null, +): boolean => { + userId = userId ?? Meteor.userId(); + + if (!userId) { + return false; + } + + if (!Models.AuthzCachedCollection.ready.get()) { + return false; + } + + return predicate(([] as IPermission['_id'][]).concat(permissions), scope, userId); +}; + +export const hasAllPermission = ( + permissions: IPermission['_id'] | IPermission['_id'][], + scope?: IRole['scope'], +): boolean => validatePermissions(permissions, scope, all); + +export const hasAtLeastOnePermission = ( + permissions: IPermission['_id'] | IPermission['_id'][], + scope?: IRole['scope'], +): boolean => validatePermissions(permissions, scope, atLeastOne); + +export const userHasAllPermission = ( + permissions: IPermission['_id'] | IPermission['_id'][], + scope?: IRole['scope'], + userId?: IUser['_id'] | null, +): boolean => validatePermissions(permissions, scope, all, userId); + +export const hasPermission = hasAllPermission; diff --git a/app/autotranslate/client/lib/autotranslate.js b/app/autotranslate/client/lib/autotranslate.js index f711bc383a4..1b742b21010 100644 --- a/app/autotranslate/client/lib/autotranslate.js +++ b/app/autotranslate/client/lib/autotranslate.js @@ -5,7 +5,7 @@ import mem from 'mem'; import { Subscriptions, Messages } from '../../../models'; import { hasPermission } from '../../../authorization'; -import { call } from '../../../ui-utils/client'; +import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; let userLanguage = 'en'; let username = ''; @@ -76,8 +76,8 @@ export const AutoTranslate = { c.stop(); [this.providersMetadata, this.supportedLanguages] = await Promise.all([ - call('autoTranslate.getProviderUiMetadata'), - call('autoTranslate.getSupportedLanguages', 'en'), + callWithErrorHandling('autoTranslate.getProviderUiMetadata'), + callWithErrorHandling('autoTranslate.getSupportedLanguages', 'en'), ]); }); diff --git a/app/callbacks/lib/callbacks.js b/app/callbacks/lib/callbacks.js index 5def1fcfb9b..e91c1af3883 100644 --- a/app/callbacks/lib/callbacks.js +++ b/app/callbacks/lib/callbacks.js @@ -11,7 +11,7 @@ let logger = { }; if (Meteor.isClient) { - const { getConfig } = require('../../ui-utils/client/config'); + const { getConfig } = require('../../../client/lib/utils/getConfig'); timed = [getConfig('debug'), getConfig('timed-callbacks')].includes('true'); } diff --git a/app/chatpal-search/client/template/result.js b/app/chatpal-search/client/template/result.js index 2468ecc30c9..1ed9a1bcafd 100644 --- a/app/chatpal-search/client/template/result.js +++ b/app/chatpal-search/client/template/result.js @@ -2,10 +2,11 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { Template } from 'meteor/templating'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; -import { DateFormat } from '../../../lib'; import { roomTypes, getURL } from '../../../utils'; import { Subscriptions } from '../../../models'; import { getUserAvatarURL as getAvatarUrl } from '../../../utils/lib/getUserAvatarURL'; +import { formatTime } from '../../../../client/lib/utils/formatTime'; +import { formatDate } from '../../../../client/lib/utils/formatDate'; const getDMUrl = (username) => getURL(`/direct/${ username }`); @@ -116,10 +117,10 @@ Template.ChatpalSearchSingleMessage.helpers({ }, time() { - return DateFormat.formatTime(this.created); + return formatTime(this.created); }, date() { - return DateFormat.formatDate(this.created); + return formatDate(this.created); }, getAvatarUrl, }); diff --git a/app/e2e/client/logger.ts b/app/e2e/client/logger.ts index 1efd15d8347..50e43b63430 100644 --- a/app/e2e/client/logger.ts +++ b/app/e2e/client/logger.ts @@ -1,4 +1,4 @@ -import { getConfig } from '../../ui-utils/client/config'; +import { getConfig } from '../../../client/lib/utils/getConfig'; let debug: boolean | undefined = undefined; diff --git a/app/e2e/client/rocketchat.e2e.js b/app/e2e/client/rocketchat.e2e.js index 2c0541350ec..974eff7e82a 100644 --- a/app/e2e/client/rocketchat.e2e.js +++ b/app/e2e/client/rocketchat.e2e.js @@ -25,7 +25,7 @@ import { call } from '../../ui-utils'; import './events.js'; import './tabbar'; import { log, logError } from './logger'; -import { waitUntilFind } from '../../utils/client/lib/waitUntilFind'; +import { waitUntilFind } from '../../../client/lib/utils/waitUntilFind'; import { imperativeModal } from '../../../client/lib/imperativeModal'; import SaveE2EPasswordModal from './SaveE2EPasswordModal'; import EnterE2EPasswordModal from './EnterE2EPasswordModal'; diff --git a/app/emoji-custom/client/lib/emojiCustom.js b/app/emoji-custom/client/lib/emojiCustom.js index f1e8b440cfd..177fdfba30e 100644 --- a/app/emoji-custom/client/lib/emojiCustom.js +++ b/app/emoji-custom/client/lib/emojiCustom.js @@ -1,5 +1,4 @@ import { Meteor } from 'meteor/meteor'; -import { Blaze } from 'meteor/blaze'; import { Session } from 'meteor/session'; import { escapeRegExp } from '@rocket.chat/string-helpers'; @@ -26,8 +25,6 @@ export const getEmojiUrlFromName = function(name, extension) { return `${ path }/emoji-custom/${ encodeURIComponent(name) }.${ extension }?_dc=${ random }`; }; -Blaze.registerHelper('emojiUrlFromName', getEmojiUrlFromName); - export const deleteEmojiCustom = function(emojiData) { delete emoji.list[`:${ emojiData.name }:`]; const arrayIndex = emoji.packages.emojiCustom.emojisByCategory.rocket.indexOf(emojiData.name); diff --git a/app/emoji/client/emojiParser.js b/app/emoji/client/emojiParser.js index 71422578b13..770c9147b25 100644 --- a/app/emoji/client/emojiParser.js +++ b/app/emoji/client/emojiParser.js @@ -1,4 +1,4 @@ -import { isIE11 } from '../../ui-utils/client/lib/isIE11'; +import { isIE11 } from '../../../client/lib/utils/isIE11'; import { emoji } from '../lib/rocketchat'; /* diff --git a/app/emoji/client/index.js b/app/emoji/client/index.js index bf02d263352..c1678c647f1 100644 --- a/app/emoji/client/index.js +++ b/app/emoji/client/index.js @@ -1,4 +1,3 @@ export { EmojiPicker } from './lib/EmojiPicker'; -export { renderEmoji } from './lib/emojiRenderer'; export { emoji } from '../lib/rocketchat'; export { createEmojiMessageRenderer } from './emojiParser'; diff --git a/app/emoji/client/lib/emojiRenderer.js b/app/emoji/client/lib/emojiRenderer.js deleted file mode 100644 index d4b2ba2f499..00000000000 --- a/app/emoji/client/lib/emojiRenderer.js +++ /dev/null @@ -1,27 +0,0 @@ -import { Blaze } from 'meteor/blaze'; -import { Template } from 'meteor/templating'; -import { HTML } from 'meteor/htmljs'; - -import { emoji } from '../../lib/rocketchat'; -import { isSetNotNull } from '../function-isSet'; - -export const renderEmoji = function(_emoji) { - if (isSetNotNull(() => emoji.list[_emoji].emojiPackage)) { - const { emojiPackage } = emoji.list[_emoji]; - return emoji.packages[emojiPackage].render(_emoji); - } -}; - -Blaze.registerHelper('renderEmoji', renderEmoji); - -Template.registerHelper('renderEmoji', new Template('renderEmoji', function() { - const view = this; - const _emoji = Blaze.getData(view); - - if (isSetNotNull(() => emoji.list[_emoji].emojiPackage)) { - const { emojiPackage } = emoji.list[_emoji]; - return new HTML.Raw(emoji.packages[emojiPackage].render(_emoji)); - } - - return ''; -})); diff --git a/app/lib/client/UserDeleted.js b/app/lib/client/UserDeleted.js deleted file mode 100644 index 8be7d5ed5d4..00000000000 --- a/app/lib/client/UserDeleted.js +++ /dev/null @@ -1,12 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { ChatMessage } from '../../models'; -import { Notifications } from '../../notifications'; - -Meteor.startup(function() { - Notifications.onLogged('Users:Deleted', ({ userId }) => - ChatMessage.remove({ - 'u._id': userId, - }), - ); -}); diff --git a/app/lib/client/index.js b/app/lib/client/index.js index 64ed16e27b1..5619d577642 100644 --- a/app/lib/client/index.js +++ b/app/lib/client/index.js @@ -1,11 +1,6 @@ import '../lib/startup/settingsOnLoadSiteUrl'; import '../lib/MessageTypes'; -import './CustomTranslations'; import './OAuthProxy'; -import './UserDeleted'; -import './lib/startup/commands'; -import './lib/settings'; -import './lib/userRoles'; import './methods/sendMessage'; import './views/customFieldsForm.html'; import './views/customFieldsForm'; diff --git a/app/lib/client/lib/formatDate.js b/app/lib/client/lib/formatDate.js deleted file mode 100644 index 43251237c9a..00000000000 --- a/app/lib/client/lib/formatDate.js +++ /dev/null @@ -1,53 +0,0 @@ -import moment from 'moment'; -import mem from 'mem'; -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; - -import { getUserPreference, t } from '../../../utils'; -import { settings } from '../../../settings'; - -let lastDay = t('yesterday'); -let clockMode; -let sameDay; -const dayFormat = ['h:mm A', 'H:mm']; - -Meteor.startup(() => Tracker.autorun(() => { - clockMode = getUserPreference(Meteor.userId(), 'clockMode', false); - sameDay = dayFormat[clockMode - 1] || settings.get('Message_TimeFormat'); - lastDay = t('yesterday'); -})); - -export const formatTime = (time) => { - switch (clockMode) { - case 1: - case 2: - return moment(time).format(sameDay); - default: - return moment(time).format(settings.get('Message_TimeFormat')); - } -}; - -export const formatDateAndTime = (time) => { - switch (clockMode) { - case 1: - return moment(time).format('MMMM D, Y h:mm A'); - case 2: - return moment(time).format('MMMM D, Y H:mm'); - default: - return moment(time).format(settings.get('Message_TimeAndDateFormat')); - } -}; - -const sameElse = function(now) { - const diff = Math.ceil(this.diff(now, 'years', true)); - return diff < 0 ? 'MMM D YYYY' : 'MMM D'; -}; - -export const timeAgo = (date) => moment(date).calendar(null, { - lastDay: `[${ lastDay }]`, - sameDay, - lastWeek: 'dddd', - sameElse, -}); - -export const formatDate = mem((time) => moment(time).format(settings.get('Message_DateFormat')), { maxAge: 5000 }); diff --git a/app/lib/client/lib/index.js b/app/lib/client/lib/index.js index 4cc975c68ce..5cade0413ef 100644 --- a/app/lib/client/lib/index.js +++ b/app/lib/client/lib/index.js @@ -1,12 +1,2 @@ -/* - What is this file? Great question! To make Rocket.Chat more "modular" - and to make the "rocketchat:lib" package more of a core package - with the libraries, this index file contains the exported members - for the *client* pieces of code which does include the shared - library files. -*/ -import * as DateFormat from './formatDate'; - export { RocketChatAnnouncement } from './RocketChatAnnouncement'; export { LoginPresence } from './LoginPresence'; -export { DateFormat }; diff --git a/app/lib/client/lib/startup/commands.js b/app/lib/client/lib/startup/commands.js deleted file mode 100644 index 0d9a7179ae9..00000000000 --- a/app/lib/client/lib/startup/commands.js +++ /dev/null @@ -1,22 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; - -import { slashCommands, APIClient } from '../../../../utils'; - -// Track logins and when they login, get the commands -(() => { - let oldUserId = null; - - Tracker.autorun(() => { - const newUserId = Meteor.userId(); - if (oldUserId === null && newUserId) { - APIClient.v1.get('commands.list').then(function _loadedCommands(result) { - result.commands.forEach((command) => { - slashCommands.commands[command.command] = command; - }); - }); - } - - oldUserId = Meteor.userId(); - }); -})(); diff --git a/app/lib/client/lib/userRoles.js b/app/lib/client/lib/userRoles.js deleted file mode 100644 index c0414276b40..00000000000 --- a/app/lib/client/lib/userRoles.js +++ /dev/null @@ -1,42 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; - -import { UserRoles, RoomRoles, ChatMessage } from '../../../models'; -import { handleError } from '../../../utils'; -import { Notifications } from '../../../notifications'; - -Meteor.startup(function() { - Tracker.autorun(function() { - if (Meteor.userId()) { - Meteor.call('getUserRoles', (error, results) => { - if (error) { - return handleError(error); - } - - for (const record of results) { - UserRoles.upsert({ _id: record._id }, record); - } - }); - - Notifications.onLogged('roles-change', function(role) { - if (role.type === 'added') { - if (role.scope) { - RoomRoles.upsert({ rid: role.scope, 'u._id': role.u._id }, { $setOnInsert: { u: role.u }, $addToSet: { roles: role._id } }); - } else { - UserRoles.upsert({ _id: role.u._id }, { $addToSet: { roles: role._id }, $set: { username: role.u.username } }); - ChatMessage.update({ 'u._id': role.u._id }, { $addToSet: { roles: role._id } }, { multi: true }); - } - } else if (role.type === 'removed') { - if (role.scope) { - RoomRoles.update({ rid: role.scope, 'u._id': role.u._id }, { $pull: { roles: role._id } }); - } else { - UserRoles.update({ _id: role.u._id }, { $pull: { roles: role._id } }); - ChatMessage.update({ 'u._id': role.u._id }, { $pull: { roles: role._id } }, { multi: true }); - } - } else if (role.type === 'changed') { - ChatMessage.update({ roles: role._id }, { $inc: { rerender: 1 } }, { multi: true }); - } - }); - } - }); -}); diff --git a/app/livechat/client/lib/dateHandler.js b/app/livechat/client/lib/dateHandler.js index ac7d4167aa5..8639280b2ee 100644 --- a/app/livechat/client/lib/dateHandler.js +++ b/app/livechat/client/lib/dateHandler.js @@ -1,7 +1,6 @@ import moment from 'moment'; -import { handleError } from '../../../utils'; - +import { handleError } from '../../../../client/lib/utils/handleError'; /** * Check if given daterange matches any of pre-defined options diff --git a/app/livechat/client/lib/stream/queueManager.js b/app/livechat/client/lib/stream/queueManager.js index 14c2e31eae0..97e9bcf0fb6 100644 --- a/app/livechat/client/lib/stream/queueManager.js +++ b/app/livechat/client/lib/stream/queueManager.js @@ -3,7 +3,7 @@ import { Meteor } from 'meteor/meteor'; import { APIClient } from '../../../../utils/client'; import { LivechatInquiry } from '../../collections/LivechatInquiry'; import { inquiryDataStream } from './inquiry'; -import { call } from '../../../../ui-utils/client'; +import { callWithErrorHandling } from '../../../../../client/lib/utils/callWithErrorHandling'; import { getUserPreference } from '../../../../utils'; import { CustomSounds } from '../../../../custom-sounds/client/lib/CustomSounds'; @@ -79,7 +79,7 @@ const addGlobalListener = () => { const subscribe = async (userId) => { - const config = await call('livechat:getRoutingConfig'); + const config = await callWithErrorHandling('livechat:getRoutingConfig'); if (config && config.autoAssignAgent) { return; } diff --git a/app/livechat/client/views/app/dialog/closeRoom.js b/app/livechat/client/views/app/dialog/closeRoom.js index 2dac3bc7956..42a9f1f5128 100644 --- a/app/livechat/client/views/app/dialog/closeRoom.js +++ b/app/livechat/client/views/app/dialog/closeRoom.js @@ -5,9 +5,10 @@ import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { settings } from '../../../../../settings'; import { modal } from '../../../../../ui-utils/client'; -import { APIClient, handleError, t } from '../../../../../utils'; +import { APIClient, t } from '../../../../../utils'; import { hasRole } from '../../../../../authorization'; import './closeRoom.html'; +import { handleError } from '../../../../../../client/lib/utils/handleError'; const validateRoomComment = (comment) => { if (!settings.get('Livechat_request_comment_when_closing_conversation')) { diff --git a/app/livechat/client/views/app/livechatReadOnly.js b/app/livechat/client/views/app/livechatReadOnly.js index 765088722c2..c751c701e9e 100644 --- a/app/livechat/client/views/app/livechatReadOnly.js +++ b/app/livechat/client/views/app/livechatReadOnly.js @@ -4,7 +4,7 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { ChatRoom, CachedChatRoom } from '../../../../models'; -import { call } from '../../../../ui-utils/client'; +import { callWithErrorHandling } from '../../../../../client/lib/utils/callWithErrorHandling'; import './livechatReadOnly.html'; import { APIClient } from '../../../../utils/client'; import { inquiryDataStream } from '../../lib/stream/inquiry'; @@ -42,7 +42,7 @@ Template.livechatReadOnly.events({ const inquiry = instance.inquiry.get(); const { _id } = inquiry; - await call('livechat:takeInquiry', _id, { clientAction: true }); + await callWithErrorHandling('livechat:takeInquiry', _id, { clientAction: true }); instance.loadInquiry(inquiry.rid); }, @@ -52,7 +52,7 @@ Template.livechatReadOnly.events({ const room = instance.room.get(); - await call('livechat:resumeOnHold', room._id, { clientAction: true }); + await callWithErrorHandling('livechat:resumeOnHold', room._id, { clientAction: true }); }, }); @@ -64,7 +64,7 @@ Template.livechatReadOnly.onCreated(function() { this.preparing = new ReactiveVar(true); this.updateInquiry = async ({ clientAction, ...inquiry }) => { - if (clientAction === 'removed' || !await call('canAccessRoom', inquiry.rid, Meteor.userId())) { + if (clientAction === 'removed' || !await callWithErrorHandling('canAccessRoom', inquiry.rid, Meteor.userId())) { // this will force to refresh the room // since the client wont get notified of room changes when chats are on queue (no one assigned) // a better approach should be performed when refactoring these templates to use react diff --git a/app/livechat/client/views/app/tabbar/agentEdit.js b/app/livechat/client/views/app/tabbar/agentEdit.js index e46c66e3760..53a11f7ba3b 100644 --- a/app/livechat/client/views/app/tabbar/agentEdit.js +++ b/app/livechat/client/views/app/tabbar/agentEdit.js @@ -6,7 +6,8 @@ import toastr from 'toastr'; import { getCustomFormTemplate } from '../customTemplates/register'; import './agentEdit.html'; import { hasPermission } from '../../../../../authorization'; -import { t, handleError, APIClient } from '../../../../../utils/client'; +import { t, APIClient } from '../../../../../utils/client'; +import { handleError } from '../../../../../../client/lib/utils/handleError'; Template.agentEdit.helpers({ canEditDepartment() { diff --git a/app/livechat/client/views/app/tabbar/agentInfo.js b/app/livechat/client/views/app/tabbar/agentInfo.js index 6894586f32d..c90de337a12 100644 --- a/app/livechat/client/views/app/tabbar/agentInfo.js +++ b/app/livechat/client/views/app/tabbar/agentInfo.js @@ -9,8 +9,9 @@ import s from 'underscore.string'; import { getCustomFormTemplate } from '../customTemplates/register'; import './agentInfo.html'; import { modal } from '../../../../../ui-utils'; -import { t, handleError, APIClient } from '../../../../../utils/client'; +import { t, APIClient } from '../../../../../utils/client'; import { hasPermission } from '../../../../../authorization'; +import { handleError } from '../../../../../../client/lib/utils/handleError'; const customFieldsTemplate = () => getCustomFormTemplate('livechatAgentInfoForm'); diff --git a/app/livechat/client/views/app/tabbar/visitorInfo.js b/app/livechat/client/views/app/tabbar/visitorInfo.js index 56de7d9275c..a58246dbd16 100644 --- a/app/livechat/client/views/app/tabbar/visitorInfo.js +++ b/app/livechat/client/views/app/tabbar/visitorInfo.js @@ -11,14 +11,15 @@ import UAParser from 'ua-parser-js'; import { modal } from '../../../../../ui-utils'; import { Subscriptions } from '../../../../../models'; import { settings } from '../../../../../settings'; -import { t, handleError, roomTypes } from '../../../../../utils'; +import { t, roomTypes } from '../../../../../utils'; import { hasRole, hasPermission, hasAtLeastOnePermission } from '../../../../../authorization'; import './visitorInfo.html'; import { APIClient } from '../../../../../utils/client'; import { RoomManager } from '../../../../../ui-utils/client'; -import { DateFormat } from '../../../../../lib/client'; import { getCustomFormTemplate } from '../customTemplates/register'; import { Markdown } from '../../../../../markdown/client'; +import { handleError } from '../../../../../../client/lib/utils/handleError'; +import { formatDateAndTime } from '../../../../../../client/lib/utils/formatDateAndTime'; const isSubscribedToRoom = () => { const data = Template.currentData(); @@ -213,7 +214,7 @@ Template.visitorInfo.helpers({ roomClosedDateTime() { const { closedAt } = this; - return DateFormat.formatDateAndTime(closedAt); + return formatDateAndTime(closedAt); }, roomClosedBy() { @@ -249,7 +250,7 @@ Template.visitorInfo.helpers({ transcriptRequestedDateTime() { const { requestedAt } = this; - return DateFormat.formatDateAndTime(requestedAt); + return formatDateAndTime(requestedAt); }, markdown(text) { diff --git a/app/livechat/client/views/app/tabbar/visitorTranscript.js b/app/livechat/client/views/app/tabbar/visitorTranscript.js index ae06c775aff..ec26bdf0130 100644 --- a/app/livechat/client/views/app/tabbar/visitorTranscript.js +++ b/app/livechat/client/views/app/tabbar/visitorTranscript.js @@ -3,7 +3,8 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { Template } from 'meteor/templating'; import toastr from 'toastr'; -import { t, isEmail, handleError, roomTypes } from '../../../../../utils'; +import { handleError } from '../../../../../../client/lib/utils/handleError'; +import { t, isEmail, roomTypes } from '../../../../../utils'; import { APIClient } from '../../../../../utils/client'; import './visitorTranscript.html'; diff --git a/app/livestream/client/views/broadcastView.js b/app/livestream/client/views/broadcastView.js index 4fbe6fc554f..afa8f26142d 100644 --- a/app/livestream/client/views/broadcastView.js +++ b/app/livestream/client/views/broadcastView.js @@ -3,8 +3,8 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; -import { handleError } from '../../../utils'; -import { settings } from '../../../settings'; +import { handleError } from '../../../../client/lib/utils/handleError'; +import { settings } from '../../../settings/client'; const getMedia = () => navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; const createAndConnect = (url) => { diff --git a/app/livestream/client/views/liveStreamTab.js b/app/livestream/client/views/liveStreamTab.js index 19f64cde929..d73a31336fa 100644 --- a/app/livestream/client/views/liveStreamTab.js +++ b/app/livestream/client/views/liveStreamTab.js @@ -9,11 +9,12 @@ import toastr from 'toastr'; import { auth } from '../oauth.js'; import { RocketChatAnnouncement } from '../../../lib'; import { popout } from '../../../ui-utils'; -import { t, handleError } from '../../../utils'; +import { t } from '../../../utils'; import { settings } from '../../../settings'; import { callbacks } from '../../../callbacks'; import { hasAllPermission } from '../../../authorization'; import { Users, Rooms } from '../../../models'; +import { handleError } from '../../../../client/lib/utils/handleError'; export const call = (...args) => new Promise(function(resolve, reject) { Meteor.call(...args, function(err, result) { diff --git a/app/logger/client/logger.js b/app/logger/client/logger.js index 61eb0bf9c11..4e05f18ba3b 100644 --- a/app/logger/client/logger.js +++ b/app/logger/client/logger.js @@ -1,7 +1,7 @@ import { Template } from 'meteor/templating'; import _ from 'underscore'; -import { getConfig } from '../../ui-utils/client/config'; +import { getConfig } from '../../../client/lib/utils/getConfig'; Template.log = !!(getConfig('debug') || getConfig('debug-template')); diff --git a/app/message-mark-as-unread/client/actionButton.js b/app/message-mark-as-unread/client/actionButton.js index cc0ef68a695..6b79e608d31 100644 --- a/app/message-mark-as-unread/client/actionButton.js +++ b/app/message-mark-as-unread/client/actionButton.js @@ -3,9 +3,9 @@ import { FlowRouter } from 'meteor/kadira:flow-router'; import { RoomManager, MessageAction } from '../../ui-utils'; import { messageArgs } from '../../ui-utils/client/lib/messageArgs'; -import { handleError } from '../../utils'; import { ChatSubscription } from '../../models'; import { roomTypes } from '../../utils/client'; +import { handleError } from '../../../client/lib/utils/handleError'; Meteor.startup(() => { MessageAction.addButton({ diff --git a/app/message-pin/client/actionButton.js b/app/message-pin/client/actionButton.js index 07c0fde808d..9f5accfb832 100644 --- a/app/message-pin/client/actionButton.js +++ b/app/message-pin/client/actionButton.js @@ -6,11 +6,11 @@ import toastr from 'toastr'; import { RoomHistoryManager, MessageAction } from '../../ui-utils'; import { messageArgs } from '../../ui-utils/client/lib/messageArgs'; -import { handleError } from '../../utils'; import { settings } from '../../settings'; import { hasAtLeastOnePermission } from '../../authorization'; import { Rooms } from '../../models/client'; import { roomTypes } from '../../utils/client'; +import { handleError } from '../../../client/lib/utils/handleError'; Meteor.startup(function() { MessageAction.addButton({ diff --git a/app/message-snippet/client/page/snippetPage.js b/app/message-snippet/client/page/snippetPage.js index c4bd5d0b4a6..a65025e547c 100644 --- a/app/message-snippet/client/page/snippetPage.js +++ b/app/message-snippet/client/page/snippetPage.js @@ -3,10 +3,10 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { Template } from 'meteor/templating'; import moment from 'moment'; -import { DateFormat } from '../../../lib'; import { settings } from '../../../settings'; import { Markdown } from '../../../markdown/client'; import { APIClient } from '../../../utils/client'; +import { formatTime } from '../../../../client/lib/utils/formatTime'; Template.snippetPage.helpers({ snippet() { @@ -30,7 +30,7 @@ Template.snippetPage.helpers({ time() { const snippet = Template.instance().message.get(); if (snippet !== undefined) { - return DateFormat.formatTime(snippet.ts); + return formatTime(snippet.ts); } }, }); diff --git a/app/message-star/client/actionButton.js b/app/message-star/client/actionButton.js index 092e3e3c8dc..e77ab4486d5 100644 --- a/app/message-star/client/actionButton.js +++ b/app/message-star/client/actionButton.js @@ -4,12 +4,12 @@ import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { FlowRouter } from 'meteor/kadira:flow-router'; import toastr from 'toastr'; -import { handleError } from '../../utils'; import { settings } from '../../settings'; import { RoomHistoryManager, MessageAction } from '../../ui-utils'; import { messageArgs } from '../../ui-utils/client/lib/messageArgs'; import { Rooms } from '../../models/client'; import { roomTypes } from '../../utils/client'; +import { handleError } from '../../../client/lib/utils/handleError'; Meteor.startup(function() { MessageAction.addButton({ diff --git a/app/models/client/models/Roles.js b/app/models/client/models/Roles.js index ed4ed9fc71e..a5b63179e4e 100644 --- a/app/models/client/models/Roles.js +++ b/app/models/client/models/Roles.js @@ -3,9 +3,7 @@ import { ReactiveVar } from 'meteor/reactive-var'; import * as Models from '..'; -const Roles = new Mongo.Collection(null); - -Object.assign(Roles, { +const Roles = Object.assign(new Mongo.Collection(null), { findUsersInRole(name, scope, options) { const role = this.findOne(name); const roleScope = (role && role.scope) || 'Users'; diff --git a/app/nrr/client/nrr.js b/app/nrr/client/nrr.js index d783880c277..5ec665da590 100644 --- a/app/nrr/client/nrr.js +++ b/app/nrr/client/nrr.js @@ -17,7 +17,7 @@ const makeCursorReactive = function(obj) { } }; -Blaze.toHTMLWithDataNonReactive = function(content, data) { +const toHTMLWithDataNonReactive = function(content, data) { makeCursorReactive(data); if (data instanceof Spacebars.kw && Object.keys(data.hash).length > 0) { @@ -27,20 +27,14 @@ Blaze.toHTMLWithDataNonReactive = function(content, data) { return Tracker.nonreactive(() => Blaze.toHTMLWithData(content, data)); }; -Blaze.registerHelper('nrrargs', function(...args) { - return { - _arguments: args, - }; -}); - -Blaze.renderNonReactive = function(templateName, data) { +const renderNonReactive = function(templateName, data) { const { _arguments } = this.parentView.dataVar.get(); [templateName, data] = _arguments; return Tracker.nonreactive(() => { console.warn('Nrr template is deprecated'); - const view = new Blaze.View('nrr', () => HTML.Raw(Blaze.toHTMLWithDataNonReactive(Template[templateName], data))); + const view = new Blaze.View('nrr', () => HTML.Raw(toHTMLWithDataNonReactive(Template[templateName], data))); view.onViewReady(() => { const { onViewReady } = Template[templateName]; @@ -56,4 +50,4 @@ Blaze.renderNonReactive = function(templateName, data) { }); }; -Blaze.registerHelper('nrr', Blaze.Template('nrr', Blaze.renderNonReactive)); +Template.nrr = new Blaze.Template('nrr', renderNonReactive); diff --git a/app/otr/client/rocketchat.otr.room.js b/app/otr/client/rocketchat.otr.room.js index 6dac9aaa085..fa62a462784 100644 --- a/app/otr/client/rocketchat.otr.room.js +++ b/app/otr/client/rocketchat.otr.room.js @@ -10,9 +10,9 @@ import toastr from 'toastr'; import { OTR } from './rocketchat.otr'; import { Notifications } from '../../notifications'; -import { getUidDirectMessage } from '../../ui-utils/client/lib/getUidDirectMessage'; +import { getUidDirectMessage } from '../../../client/lib/utils/getUidDirectMessage'; import { Presence } from '../../../client/lib/presence'; -import { goToRoomById } from '../../../client/lib/goToRoomById'; +import { goToRoomById } from '../../../client/lib/utils/goToRoomById'; import { imperativeModal } from '../../../client/lib/imperativeModal'; import GenericModal from '../../../client/components/GenericModal'; diff --git a/app/search/client/provider/result.js b/app/search/client/provider/result.js index ff2cc972bf1..c5e71b6df3b 100644 --- a/app/search/client/provider/result.js +++ b/app/search/client/provider/result.js @@ -11,7 +11,7 @@ import { MessageAction, RoomHistoryManager } from '../../../ui-utils'; import { messageArgs } from '../../../ui-utils/client/lib/messageArgs'; import { Rooms } from '../../../models/client'; import { getCommonRoomEvents } from '../../../ui/client/views/app/lib/getCommonRoomEvents'; -import { goToRoomById } from '../../../../client/lib/goToRoomById'; +import { goToRoomById } from '../../../../client/lib/utils/goToRoomById'; Meteor.startup(function() { MessageAction.addButton({ diff --git a/app/slashcommands-status/lib/status.js b/app/slashcommands-status/lib/status.js index 9f7a4658846..359068a0cda 100644 --- a/app/slashcommands-status/lib/status.js +++ b/app/slashcommands-status/lib/status.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; -import { handleError, slashCommands } from '../../utils'; +import { slashCommands } from '../../utils'; import { api } from '../../../server/sdk/api'; function Status(command, params, item) { @@ -11,6 +11,7 @@ function Status(command, params, item) { Meteor.call('setUserStatus', null, params, (err) => { if (err) { if (Meteor.isClient) { + const { handleError } = require('../../../client/lib/utils/handleError'); return handleError(err); } diff --git a/app/slashcommands-topic/lib/topic.js b/app/slashcommands-topic/lib/topic.js index 3b34239ef16..dd904c54108 100644 --- a/app/slashcommands-topic/lib/topic.js +++ b/app/slashcommands-topic/lib/topic.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { handleError, slashCommands } from '../../utils'; +import { slashCommands } from '../../utils'; import { ChatRoom } from '../../models'; import { callbacks } from '../../callbacks'; import { hasPermission } from '../../authorization'; @@ -11,6 +11,7 @@ function Topic(command, params, item) { Meteor.call('saveRoomSettings', item.rid, 'roomTopic', params, (err) => { if (err) { if (Meteor.isClient) { + const { handleError } = require('../../../client/lib/utils/handleError'); return handleError(err); } throw err; diff --git a/app/threads/client/components/ThreadComponent.tsx b/app/threads/client/components/ThreadComponent.tsx index 6e30d2d57cd..409eb1254c7 100644 --- a/app/threads/client/components/ThreadComponent.tsx +++ b/app/threads/client/components/ThreadComponent.tsx @@ -16,7 +16,7 @@ import ThreadView from './ThreadView'; import { IMessage } from '../../../../definition/IMessage'; import { IRoom } from '../../../../definition/IRoom'; import { useTabBarOpenUserInfo } from '../../../../client/views/room/providers/ToolboxProvider'; -import { mapMessageFromApi } from '../../../../client/lib/fromApi'; +import { mapMessageFromApi } from '../../../../client/lib/utils/mapMessageFromApi'; const subscriptionFields = {}; diff --git a/app/threads/client/flextab/messageBoxFollow.js b/app/threads/client/flextab/messageBoxFollow.js index 42c485381d9..bfd724a3eb8 100644 --- a/app/threads/client/flextab/messageBoxFollow.js +++ b/app/threads/client/flextab/messageBoxFollow.js @@ -1,11 +1,11 @@ import { Template } from 'meteor/templating'; +import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; import './messageBoxFollow.html'; -import { call } from '../../../ui-utils/client'; Template.messageBoxFollow.events({ 'click .js-follow'() { const { tmid } = this; - call('followMessage', { mid: tmid }); + callWithErrorHandling('followMessage', { mid: tmid }); }, }); diff --git a/app/threads/client/flextab/thread.js b/app/threads/client/flextab/thread.js index b74f61ce94b..ecf508efbf9 100644 --- a/app/threads/client/flextab/thread.js +++ b/app/threads/client/flextab/thread.js @@ -8,7 +8,7 @@ import { Tracker } from 'meteor/tracker'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { chatMessages, ChatMessages } from '../../../ui'; -import { call, keyCodes } from '../../../ui-utils/client'; +import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; import { messageContext } from '../../../ui-utils/client/lib/messageContext'; import { upsertMessageBulk } from '../../../ui-utils/client/lib/RoomHistoryManager'; import { Messages } from '../../../models'; @@ -20,6 +20,7 @@ import { settings } from '../../../settings/client'; import { callbacks } from '../../../callbacks/client'; import './messageBoxFollow'; import { getCommonRoomEvents } from '../../../ui/client/views/app/lib/getCommonRoomEvents'; +import { keyCodes } from '../../../../client/lib/utils/keyCodes'; const sort = { ts: 1 }; @@ -243,7 +244,7 @@ Template.thread.onCreated(async function() { this.state.set('loading', true); - const messages = await call('getThreadMessages', { tmid }); + const messages = await callWithErrorHandling('getThreadMessages', { tmid }); upsertMessageBulk({ msgs: messages }, this.Threads); diff --git a/app/threads/client/messageAction/follow.js b/app/threads/client/messageAction/follow.js index b2f98475257..821c66d4e6a 100644 --- a/app/threads/client/messageAction/follow.js +++ b/app/threads/client/messageAction/follow.js @@ -5,9 +5,10 @@ import toastr from 'toastr'; import { Messages } from '../../../models/client'; import { settings } from '../../../settings/client'; -import { MessageAction, call } from '../../../ui-utils/client'; +import { MessageAction } from '../../../ui-utils/client'; import { messageArgs } from '../../../ui-utils/client/lib/messageArgs'; import { roomTypes } from '../../../utils/client'; +import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; Meteor.startup(function() { Tracker.autorun(() => { @@ -21,7 +22,7 @@ Meteor.startup(function() { context: ['message', 'message-mobile', 'threads'], async action() { const { msg } = messageArgs(this); - call('followMessage', { mid: msg._id }).then(() => + callWithErrorHandling('followMessage', { mid: msg._id }).then(() => toastr.success(TAPi18n.__('You_followed_this_message')), ); }, diff --git a/app/threads/client/messageAction/unfollow.js b/app/threads/client/messageAction/unfollow.js index 44e73434445..a77ea9ffdc5 100644 --- a/app/threads/client/messageAction/unfollow.js +++ b/app/threads/client/messageAction/unfollow.js @@ -5,7 +5,8 @@ import toastr from 'toastr'; import { Messages } from '../../../models/client'; import { settings } from '../../../settings/client'; -import { MessageAction, call } from '../../../ui-utils/client'; +import { MessageAction } from '../../../ui-utils/client'; +import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; import { messageArgs } from '../../../ui-utils/client/lib/messageArgs'; Meteor.startup(function() { @@ -20,7 +21,7 @@ Meteor.startup(function() { context: ['message', 'message-mobile', 'threads'], async action() { const { msg } = messageArgs(this); - call('unfollowMessage', { mid: msg._id }).then(() => + callWithErrorHandling('unfollowMessage', { mid: msg._id }).then(() => toastr.success(TAPi18n.__('You_unfollowed_this_message')), ); }, diff --git a/app/tokenpass/client/tokenpassChannelSettings.js b/app/tokenpass/client/tokenpassChannelSettings.js index 63525138d9f..91044cb68ef 100644 --- a/app/tokenpass/client/tokenpassChannelSettings.js +++ b/app/tokenpass/client/tokenpassChannelSettings.js @@ -4,8 +4,9 @@ import { Template } from 'meteor/templating'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import toastr from 'toastr'; -import { t, handleError } from '../../utils'; +import { t } from '../../utils'; import { ChatRoom } from '../../models'; +import { handleError } from '../../../client/lib/utils/handleError'; Template.channelSettings__tokenpass.helpers({ addDisabled() { diff --git a/app/ui-cached-collection/client/models/CachedCollection.js b/app/ui-cached-collection/client/models/CachedCollection.js index eec9930c90d..506768f79ea 100644 --- a/app/ui-cached-collection/client/models/CachedCollection.js +++ b/app/ui-cached-collection/client/models/CachedCollection.js @@ -10,8 +10,8 @@ import { Emitter } from '@rocket.chat/emitter'; import { callbacks } from '../../../callbacks'; import Notifications from '../../../notifications/client/lib/Notifications'; -import { getConfig } from '../../../ui-utils/client/config'; -import { callMethod } from '../../../ui-utils/client/lib/callMethod'; +import { getConfig } from '../../../../client/lib/utils/getConfig'; +import { call } from '../../../../client/lib/utils/call'; const wrap = (fn) => (...args) => new Promise((resolve, reject) => { fn(...args, (err, result) => { @@ -218,7 +218,7 @@ export class CachedCollection extends Emitter { async loadFromServer() { const startTime = new Date(); const lastTime = this.updatedAt; - const data = await callMethod(this.methodName); + const data = await call(this.methodName); this.log(`${ data.length } records loaded from server`); data.forEach((record) => { callbacks.run(`cachedCollection-loadFromServer-${ this.name }`, record, 'changed'); @@ -318,7 +318,7 @@ export class CachedCollection extends Emitter { this.log(`syncing from ${ this.updatedAt }`); - const data = await callMethod(this.syncMethodName, this.updatedAt); + const data = await call(this.syncMethodName, this.updatedAt); let changes = []; if (data.update && data.update.length > 0) { diff --git a/app/ui-login/client/login/form.js b/app/ui-login/client/login/form.js index 7cfa83b116f..dd9eda6c08b 100644 --- a/app/ui-login/client/login/form.js +++ b/app/ui-login/client/login/form.js @@ -10,7 +10,8 @@ import toastr from 'toastr'; import { settings } from '../../../settings'; import { callbacks } from '../../../callbacks'; -import { t, handleError } from '../../../utils'; +import { t } from '../../../utils'; +import { handleError } from '../../../../client/lib/utils/handleError'; Template.loginForm.helpers({ userName() { diff --git a/app/ui-master/client/body.js b/app/ui-master/client/body.js index 649e3cadab5..5f246e15365 100644 --- a/app/ui-master/client/body.js +++ b/app/ui-master/client/body.js @@ -7,12 +7,14 @@ import { Template } from 'meteor/templating'; import { t } from '../../utils/client'; import { chatMessages } from '../../ui'; -import { Layout, popover, fireGlobalEvent, RoomManager } from '../../ui-utils'; +import { popover, RoomManager } from '../../ui-utils'; import { settings } from '../../settings'; import { ChatSubscription } from '../../models'; import './body.html'; import { imperativeModal } from '../../../client/lib/imperativeModal'; import GenericModal from '../../../client/components/GenericModal'; +import { fireGlobalEvent } from '../../../client/lib/utils/fireGlobalEvent'; +import { isLayoutEmbedded } from '../../../client/lib/utils/isLayoutEmbedded'; Template.body.onRendered(function() { new Clipboard('.clipboard'); @@ -99,7 +101,7 @@ Template.body.onRendered(function() { }; this.autorun(() => { - if (Layout.isEmbedded()) { + if (isLayoutEmbedded()) { $(document.body).on('click', 'a', handleMessageLinkClick); } else { $(document.body).off('click', 'a', handleMessageLinkClick); diff --git a/app/ui-master/client/main.js b/app/ui-master/client/main.js index fd855764af5..46f6a826d52 100644 --- a/app/ui-master/client/main.js +++ b/app/ui-master/client/main.js @@ -5,22 +5,22 @@ import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; import { getUserPreference } from '../../utils/client'; -import { mainReady, Layout, iframeLogin } from '../../ui-utils'; +import { mainReady, iframeLogin } from '../../ui-utils'; import { settings } from '../../settings'; import { CachedChatSubscription, Roles, Users } from '../../models'; import { CachedCollectionManager } from '../../ui-cached-collection'; import { tooltip } from '../../ui/client/components/tooltip'; import { callbacks } from '../../callbacks/client'; import { isSyncReady } from '../../../client/lib/userData'; -import { fireGlobalEvent } from '../../ui-utils/client'; - +import { fireGlobalEvent } from '../../../client/lib/utils/fireGlobalEvent'; import './main.html'; +import { isLayoutEmbedded } from '../../../client/lib/utils/isLayoutEmbedded'; callbacks.add('afterLogoutCleanUp', () => fireGlobalEvent('Custom_Script_On_Logout'), callbacks.priority.LOW, 'custom-script-on-logout'); Template.main.helpers({ - removeSidenav: () => Layout.isEmbedded() && !/^\/admin/.test(FlowRouter.current().route.path), + removeSidenav: () => isLayoutEmbedded() && !/^\/admin/.test(FlowRouter.current().route.path), logged: () => { if (!!Meteor.userId() || (settings.get('Accounts_AllowAnonymousRead') === true && Session.get('forceLogin') !== true)) { document.documentElement.classList.add('noscroll'); @@ -78,7 +78,7 @@ Template.main.helpers({ fireGlobalEvent('Custom_Script_Logged_In'); }, embeddedVersion: () => { - if (Layout.isEmbedded()) { + if (isLayoutEmbedded()) { return 'embedded-view'; } }, diff --git a/app/ui-message/client/message.html b/app/ui-message/client/message.html index 5e3d05079d9..a5ade319b0c 100644 --- a/app/ui-message/client/message.html +++ b/app/ui-message/client/message.html @@ -26,7 +26,7 @@ {{#if msg.emoji}} {{else}} diff --git a/app/ui-message/client/message.js b/app/ui-message/client/message.js index ecf101d2121..e8adedbfc36 100644 --- a/app/ui-message/client/message.js +++ b/app/ui-message/client/message.js @@ -5,18 +5,20 @@ import { Template } from 'meteor/templating'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { escapeHTML } from '@rocket.chat/string-helpers'; -import { timeAgo, formatDateAndTime } from '../../lib/client/lib/formatDate'; -import { DateFormat } from '../../lib/client'; +import { timeAgo } from '../../../client/lib/utils/timeAgo'; +import { formatDateAndTime } from '../../../client/lib/utils/formatDateAndTime'; import { normalizeThreadTitle } from '../../threads/client/lib/normalizeThreadTitle'; import { MessageTypes, MessageAction } from '../../ui-utils/client'; import { RoomRoles, UserRoles, Roles } from '../../models/client'; import { Markdown } from '../../markdown/client'; import { t, roomTypes } from '../../utils'; -import './messageThread'; import { AutoTranslate } from '../../autotranslate/client'; import { renderMentions } from '../../mentions/client/client'; -import { renderMessageBody } from '../../../client/lib/renderMessageBody'; +import { renderMessageBody } from '../../../client/lib/utils/renderMessageBody'; import { settings } from '../../settings/client'; +import { formatTime } from '../../../client/lib/utils/formatTime'; +import { formatDate } from '../../../client/lib/utils/formatDate'; +import './messageThread'; import './message.html'; const renderBody = (msg, settings) => { @@ -203,11 +205,11 @@ Template.message.helpers({ time() { const { msg, timeAgo: useTimeAgo } = this; - return useTimeAgo ? timeAgo(msg.ts) : DateFormat.formatTime(msg.ts); + return useTimeAgo ? timeAgo(msg.ts) : formatTime(msg.ts); }, date() { const { msg } = this; - return DateFormat.formatDate(msg.ts); + return formatDate(msg.ts); }, isTemp() { const { msg } = this; @@ -250,7 +252,7 @@ Template.message.helpers({ }, editTime() { const { msg } = this; - return msg.editedAt ? DateFormat.formatDateAndTime(msg.editedAt) : ''; + return msg.editedAt ? formatDateAndTime(msg.editedAt) : ''; }, editedBy() { const { msg } = this; diff --git a/app/ui-message/client/messageBox/messageBox.js b/app/ui-message/client/messageBox/messageBox.js index 7fcf12f371a..d34756527e5 100644 --- a/app/ui-message/client/messageBox/messageBox.js +++ b/app/ui-message/client/messageBox/messageBox.js @@ -22,8 +22,6 @@ import { messageBox, popover, call, - keyCodes, - isRTL, } from '../../../ui-utils'; import { t, @@ -37,6 +35,8 @@ import './messageBoxAudioMessage'; import './messageBoxNotSubscribed'; import './messageBox.html'; import './messageBoxReadOnly'; +import { keyCodes } from '../../../../client/lib/utils/keyCodes'; +import { isRTL } from '../../../../client/lib/utils/isRTL'; Template.messageBox.onCreated(function() { this.state = new ReactiveDict(); diff --git a/app/ui-message/client/messageBox/messageBoxTyping.js b/app/ui-message/client/messageBox/messageBoxTyping.js index 8fa993112a0..ae5738f355a 100644 --- a/app/ui-message/client/messageBox/messageBoxTyping.js +++ b/app/ui-message/client/messageBox/messageBoxTyping.js @@ -2,7 +2,7 @@ import { Template } from 'meteor/templating'; import { MsgTyping } from '../../../ui'; import { t } from '../../../utils'; -import { getConfig } from '../../../ui-utils/client/config'; +import { getConfig } from '../../../../client/lib/utils/getConfig'; import './messageBoxTyping.html'; const maxUsernames = parseInt(getConfig('max-usernames-typing')) || 4; diff --git a/app/ui-message/client/messageThread.js b/app/ui-message/client/messageThread.js index d18ac495600..7f45f140c6d 100644 --- a/app/ui-message/client/messageThread.js +++ b/app/ui-message/client/messageThread.js @@ -2,10 +2,10 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { Template } from 'meteor/templating'; import _ from 'underscore'; -import { call } from '../../ui-utils/client'; +import { callWithErrorHandling } from '../../../client/lib/utils/callWithErrorHandling'; import { Messages } from '../../models/client'; -import './messageThread.html'; import { normalizeThreadTitle } from '../../threads/client/lib/normalizeThreadTitle'; +import './messageThread.html'; const findParentMessage = (() => { const waiting = []; @@ -15,7 +15,7 @@ const findParentMessage = (() => { const getMessages = _.debounce(async function() { const _tmp = [...waiting]; waiting.length = 0; - resolve(call('getMessages', _tmp)); + resolve(callWithErrorHandling('getMessages', _tmp)); pending = new Promise((r) => { resolve = r; }); }, 500); diff --git a/app/ui-message/client/popup/messagePopupConfig.js b/app/ui-message/client/popup/messagePopupConfig.js index 7a0deb077f7..6a37e6c94c5 100644 --- a/app/ui-message/client/popup/messagePopupConfig.js +++ b/app/ui-message/client/popup/messagePopupConfig.js @@ -12,7 +12,7 @@ import { Messages, Subscriptions } from '../../../models/client'; import { settings } from '../../../settings/client'; import { hasAllPermission, hasAtLeastOnePermission } from '../../../authorization/client'; import { EmojiPicker, emoji } from '../../../emoji'; -import { call } from '../../../ui-utils/client'; +import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; import { t, getUserPreference, slashCommands } from '../../../utils/client'; import { customMessagePopups } from './customMessagePopups'; import './messagePopupConfig.html'; @@ -61,7 +61,7 @@ const reloadUsersFromRoomMessages = (rid, template) => { const fetchUsersFromServer = _.throttle(async (filterText, records, rid, cb) => { const usernames = records.map(({ username }) => username); - const { users } = await call('spotlight', filterText, usernames, { users: true, mentions: true }, rid); + const { users } = await callWithErrorHandling('spotlight', filterText, usernames, { users: true, mentions: true }, rid); if (!users || users.length <= 0) { return; @@ -95,7 +95,7 @@ const fetchRoomsFromServer = _.throttle(async (filterText, records, rid, cb) => return; } - const { rooms } = await call('spotlight', filterText, null, { rooms: true, mentions: true }, rid); + const { rooms } = await callWithErrorHandling('spotlight', filterText, null, { rooms: true, mentions: true }, rid); if (!rooms || rooms.length <= 0) { return; diff --git a/app/ui-message/client/popup/messagePopupEmoji.html b/app/ui-message/client/popup/messagePopupEmoji.html index c554520afd6..c064ff72cbf 100644 --- a/app/ui-message/client/popup/messagePopupEmoji.html +++ b/app/ui-message/client/popup/messagePopupEmoji.html @@ -1,4 +1,4 @@ diff --git a/app/ui-utils/client/config.js b/app/ui-utils/client/config.js deleted file mode 100644 index ce484f86a7d..00000000000 --- a/app/ui-utils/client/config.js +++ /dev/null @@ -1,9 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -const url = new URL(window.location); -const keys = new Set(); - -export const getConfig = (key) => { - keys.add(key); - return url.searchParams.get(key) || Meteor._localStorage.getItem(`rc-config-${ key }`); -}; diff --git a/app/ui-utils/client/index.js b/app/ui-utils/client/index.js index b94a368d4ea..c9ef5475e9c 100644 --- a/app/ui-utils/client/index.js +++ b/app/ui-utils/client/index.js @@ -2,7 +2,6 @@ export { modal } from './lib/modal'; export { SideNav } from './lib/SideNav'; export { AccountBox } from './lib/AccountBox'; export { menu } from './lib/menu'; -export { call } from './lib/callMethod'; export { MessageAction } from './lib/MessageAction'; export { messageBox } from './lib/messageBox'; export { popover } from './lib/popover'; @@ -10,18 +9,11 @@ export { readMessage } from './lib/readMessages'; export { RoomManager } from './lib/RoomManager'; export { upsertMessage, RoomHistoryManager, normalizeThreadMessage } from './lib/RoomHistoryManager'; export { mainReady } from './lib/mainReady'; -export { Layout } from './lib/Layout'; export { IframeLogin, iframeLogin } from './lib/IframeLogin'; -export { fireGlobalEvent } from './lib/fireGlobalEvent'; -export { getAvatarAsPng } from './lib/avatar'; export { popout } from './lib/popout'; export { messageProperties } from '../lib/MessageProperties'; export { MessageTypes } from '../lib/MessageTypes'; export { Message } from '../lib/Message'; export { openRoom } from './lib/openRoom'; -export { warnUserDeletionMayRemoveRooms } from './lib/warnUserDeletionMayRemoveRooms'; -export * from './lib/rtl'; -export * from './lib/keyCodes'; -export * from './lib/prependReplies'; export * from './lib/collapseArrow'; export * from './lib/messageArgs'; diff --git a/app/ui-utils/client/lib/Layout.js b/app/ui-utils/client/lib/Layout.js deleted file mode 100644 index 2158d80f2a9..00000000000 --- a/app/ui-utils/client/lib/Layout.js +++ /dev/null @@ -1,17 +0,0 @@ -import { Tracker } from 'meteor/tracker'; -import { FlowRouter } from 'meteor/kadira:flow-router'; -import { ReactiveVar } from 'meteor/reactive-var'; - -export const Layout = new class RocketChatLayout { - constructor() { - this.embedded = new ReactiveVar(); - Tracker.autorun(() => { - this.layout = FlowRouter.getQueryParam('layout'); - this.embedded.set(this.layout === 'embedded'); - }); - } - - isEmbedded() { - return this.embedded.get(); - } -}(); diff --git a/app/ui-utils/client/lib/MessageAction.js b/app/ui-utils/client/lib/MessageAction.js index f48800e4301..fbcc4b2a4f6 100644 --- a/app/ui-utils/client/lib/MessageAction.js +++ b/app/ui-utils/client/lib/MessageAction.js @@ -10,21 +10,14 @@ import { Tracker } from 'meteor/tracker'; import { Session } from 'meteor/session'; import { messageArgs } from './messageArgs'; -import { roomTypes, canDeleteMessage } from '../../../utils/client'; +import { roomTypes } from '../../../utils/client'; import { Messages, Rooms, Subscriptions } from '../../../models/client'; import { hasAtLeastOnePermission, hasPermission } from '../../../authorization/client'; import { modal } from './modal'; import { imperativeModal } from '../../../../client/lib/imperativeModal'; import ReactionList from '../../../../client/components/modals/ReactionList'; - -const call = (method, ...args) => new Promise((resolve, reject) => { - Meteor.call(method, ...args, function(err, data) { - if (err) { - return reject(err); - } - resolve(data); - }); -}); +import { call } from '../../../../client/lib/utils/call'; +import { canDeleteMessage } from '../../../../client/lib/utils/canDeleteMessage'; export const addMessageToList = (messagesList, message) => { // checks if the message is not already on the list diff --git a/app/ui-utils/client/lib/RoomHistoryManager.js b/app/ui-utils/client/lib/RoomHistoryManager.js index 5e6a169b6d8..7a2e5e9af8f 100644 --- a/app/ui-utils/client/lib/RoomHistoryManager.js +++ b/app/ui-utils/client/lib/RoomHistoryManager.js @@ -10,10 +10,10 @@ import { escapeHTML } from '@rocket.chat/string-helpers'; import { promises } from '../../../promises/client'; import { RoomManager } from './RoomManager'; import { readMessage } from './readMessages'; -import { renderMessageBody } from '../../../../client/lib/renderMessageBody'; -import { getConfig } from '../config'; +import { renderMessageBody } from '../../../../client/lib/utils/renderMessageBody'; +import { getConfig } from '../../../../client/lib/utils/getConfig'; import { ChatMessage, ChatSubscription, ChatRoom } from '../../../models'; -import { call } from './callMethod'; +import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; import { filterMarkdown } from '../../../markdown/lib/markdown'; import { getUserPreference } from '../../../utils/client'; @@ -190,7 +190,7 @@ export const RoomHistoryManager = new class extends Emitter { } const showMessageInMainThread = getUserPreference(Meteor.userId(), 'showMessageInMainThread', false); - const result = await call('loadHistory', rid, ts, limit, ls, showMessageInMainThread); + const result = await callWithErrorHandling('loadHistory', rid, ts, limit, ls, showMessageInMainThread); this.unqueue(); @@ -269,7 +269,7 @@ export const RoomHistoryManager = new class extends Emitter { const { ts } = lastMessage; if (ts) { - const result = await call('loadNextMessages', rid, ts, limit); + const result = await callWithErrorHandling('loadNextMessages', rid, ts, limit); upsertMessageBulk({ msgs: Array.from(result.messages).filter((msg) => msg.t !== 'command'), subscription, diff --git a/app/ui-utils/client/lib/RoomManager.js b/app/ui-utils/client/lib/RoomManager.js index ed01f1766fd..014d1ac0b75 100644 --- a/app/ui-utils/client/lib/RoomManager.js +++ b/app/ui-utils/client/lib/RoomManager.js @@ -6,7 +6,7 @@ import { Blaze } from 'meteor/blaze'; import { FlowRouter } from 'meteor/kadira:flow-router'; import _ from 'underscore'; -import { fireGlobalEvent } from './fireGlobalEvent'; +import { fireGlobalEvent } from '../../../../client/lib/utils/fireGlobalEvent'; import { upsertMessage, RoomHistoryManager } from './RoomHistoryManager'; import { mainReady } from './mainReady'; import { menu } from './menu'; @@ -15,9 +15,9 @@ import { callbacks } from '../../../callbacks'; import { Notifications } from '../../../notifications'; import { CachedChatRoom, ChatMessage, ChatSubscription, CachedChatSubscription, ChatRoom } from '../../../models'; import { CachedCollectionManager } from '../../../ui-cached-collection'; -import { getConfig } from '../config'; +import { getConfig } from '../../../../client/lib/utils/getConfig'; import { ROOM_DATA_STREAM } from '../../../utils/stream/constants'; -import { call } from '..'; +import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; import { RoomManager as NewRoomManager } from '../../../../client/lib/RoomManager'; const maxRoomsOpen = parseInt(getConfig('maxRoomsOpen')) || 5; @@ -286,7 +286,7 @@ const loadMissedMessages = async function(rid) { try { - const result = await call('loadMissedMessages', rid, lastMessage.ts); + const result = await callWithErrorHandling('loadMissedMessages', rid, lastMessage.ts); if (result) { const subscription = ChatSubscription.findOne({ rid }); return Promise.all(Array.from(result).map((msg) => upsertMessage({ msg, subscription }))); diff --git a/app/ui-utils/client/lib/avatar.js b/app/ui-utils/client/lib/avatar.js deleted file mode 100644 index c7a038126d8..00000000000 --- a/app/ui-utils/client/lib/avatar.js +++ /dev/null @@ -1,26 +0,0 @@ -import { Blaze } from 'meteor/blaze'; - -import { getUserAvatarURL } from '../../../utils/lib/getUserAvatarURL'; - -Blaze.registerHelper('avatarUrlFromUsername', getUserAvatarURL); - -export const getAvatarAsPng = function(username, cb) { - const image = new Image(); - image.src = getUserAvatarURL(username); - image.onload = function() { - const canvas = document.createElement('canvas'); - canvas.width = image.width; - canvas.height = image.height; - const context = canvas.getContext('2d'); - context.drawImage(image, 0, 0); - try { - return cb(canvas.toDataURL('image/png')); - } catch (e) { - return cb(''); - } - }; - image.onerror = function() { - return cb(''); - }; - return image.onerror; -}; diff --git a/app/ui-utils/client/lib/callMethod.js b/app/ui-utils/client/lib/callMethod.js deleted file mode 100644 index c1b05108ad8..00000000000 --- a/app/ui-utils/client/lib/callMethod.js +++ /dev/null @@ -1,28 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { handleError } from '../../../utils/client/lib/handleError'; - -/** - * Wraps a Meteor method into a Promise. - * This is particularly useful for creating information dialogs after execution of a Meteor method - * @param {The Meteor method to be calls} method - * @param {the method's parameters} params - */ -export const call = (method, ...params) => new Promise((resolve, reject) => { - Meteor.call(method, ...params, (err, result) => { - if (err) { - handleError(err); - return reject(err); - } - return resolve(result); - }); -}); - -export const callMethod = (method, ...params) => new Promise((resolve, reject) => { - Meteor.call(method, ...params, (err, result) => { - if (err) { - return reject(err); - } - return resolve(result); - }); -}); diff --git a/app/ui-utils/client/lib/fireGlobalEvent.js b/app/ui-utils/client/lib/fireGlobalEvent.js deleted file mode 100644 index c179a29496b..00000000000 --- a/app/ui-utils/client/lib/fireGlobalEvent.js +++ /dev/null @@ -1,21 +0,0 @@ -import { Tracker } from 'meteor/tracker'; - -import { settings } from '../../../settings'; - -export const fireGlobalEvent = function _fireGlobalEvent(eventName, params) { - window.dispatchEvent(new CustomEvent(eventName, { detail: params })); - - Tracker.autorun((computation) => { - const enabled = settings.get('Iframe_Integration_send_enable'); - if (enabled === undefined) { - return; - } - computation.stop(); - if (enabled) { - parent.postMessage({ - eventName, - data: params, - }, settings.get('Iframe_Integration_send_target_origin')); - } - }); -}; diff --git a/app/ui-utils/client/lib/getUidDirectMessage.js b/app/ui-utils/client/lib/getUidDirectMessage.js deleted file mode 100644 index ecfe3b48fc9..00000000000 --- a/app/ui-utils/client/lib/getUidDirectMessage.js +++ /dev/null @@ -1,15 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { Rooms } from '../../../models/client'; - -export const getUidDirectMessage = (rid, userId = Meteor.userId()) => { - const room = Rooms.findOne({ _id: rid }, { fields: { uids: 1 } }); - - if (!room || !room.uids || room.uids.length > 2) { - return false; - } - - const other = room && room.uids.filter((uid) => uid !== userId); - - return other && other[0]; -}; diff --git a/app/ui-utils/client/lib/messageContext.js b/app/ui-utils/client/lib/messageContext.js index 89712cb48cb..82cf52c2d1d 100644 --- a/app/ui-utils/client/lib/messageContext.js +++ b/app/ui-utils/client/lib/messageContext.js @@ -6,12 +6,13 @@ import { Tracker } from 'meteor/tracker'; import { Subscriptions, Rooms, Users } from '../../../models/client'; import { hasPermission } from '../../../authorization/client'; import { settings } from '../../../settings/client'; -import { getUserPreference, roomTypes, handleError } from '../../../utils/client'; +import { getUserPreference, roomTypes } from '../../../utils/client'; import { AutoTranslate } from '../../../autotranslate/client'; -import { Layout } from './Layout'; -import { fireGlobalEvent } from './fireGlobalEvent'; +import { fireGlobalEvent } from '../../../../client/lib/utils/fireGlobalEvent'; import { actionLinks } from '../../../action-links/client'; -import { goToRoomById } from '../../../../client/lib/goToRoomById'; +import { goToRoomById } from '../../../../client/lib/utils/goToRoomById'; +import { isLayoutEmbedded } from '../../../../client/lib/utils/isLayoutEmbedded'; +import { handleError } from '../../../../client/lib/utils/handleError'; const fields = { name: 1, username: 1, 'settings.preferences.enableMessageParserEarlyAdoption': 1, 'settings.preferences.showMessageInMainThread': 1, 'settings.preferences.autoImageLoad': 1, 'settings.preferences.saveMobileBandwidth': 1, 'settings.preferences.collapseMediaByDefault': 1, 'settings.preferences.hideRoles': 1 }; @@ -34,7 +35,7 @@ export function messageContext({ rid } = Template.instance()) { e.stopPropagation(); }; - const runAction = Layout.isEmbedded() ? (msg, e) => { + const runAction = isLayoutEmbedded() ? (msg, e) => { const { actionlink } = e.currentTarget.dataset; return fireGlobalEvent('click-action-link', { actionlink, diff --git a/app/ui-utils/client/lib/modal.js b/app/ui-utils/client/lib/modal.js index 1971ba908b0..d7d12baadb4 100644 --- a/app/ui-utils/client/lib/modal.js +++ b/app/ui-utils/client/lib/modal.js @@ -2,8 +2,9 @@ import { Meteor } from 'meteor/meteor'; import { Blaze } from 'meteor/blaze'; import { Template } from 'meteor/templating'; -import { t, getUserPreference, handleError } from '../../../utils'; +import { t, getUserPreference } from '../../../utils'; import './modal.html'; +import { handleError } from '../../../../client/lib/utils/handleError'; let modalStack = []; diff --git a/app/ui-utils/client/lib/openRoom.js b/app/ui-utils/client/lib/openRoom.js index 7d4261f7c29..469145623eb 100644 --- a/app/ui-utils/client/lib/openRoom.js +++ b/app/ui-utils/client/lib/openRoom.js @@ -9,10 +9,12 @@ import { Messages, ChatSubscription } from '../../../models'; import { settings } from '../../../settings'; import { callbacks } from '../../../callbacks'; import { roomTypes } from '../../../utils'; -import { call, callMethod } from './callMethod'; -import { RoomManager, fireGlobalEvent, RoomHistoryManager } from '..'; +import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; +import { call } from '../../../../client/lib/utils/call'; +import { RoomManager, RoomHistoryManager } from '..'; import { RoomManager as NewRoomManager } from '../../../../client/lib/RoomManager'; import { Rooms } from '../../../models/client'; +import { fireGlobalEvent } from '../../../../client/lib/utils/fireGlobalEvent'; window.currentTracker = undefined; @@ -34,7 +36,7 @@ export const openRoom = async function(type, name, render = true) { } try { - const room = roomTypes.findRoom(type, name, user) || await callMethod('getRoomByTypeAndName', type, name); + const room = roomTypes.findRoom(type, name, user) || await call('getRoomByTypeAndName', type, name); Rooms.upsert({ _id: room._id }, _.omit(room, '_id')); if (room._id !== name && type === 'd') { // Redirect old url using username to rid @@ -67,14 +69,14 @@ export const openRoom = async function(type, name, render = true) { // update user's room subscription const sub = ChatSubscription.findOne({ rid: room._id }); if (sub && sub.open === false) { - call('openRoom', room._id); + callWithErrorHandling('openRoom', room._id); } if (FlowRouter.getQueryParam('msg')) { const messageId = FlowRouter.getQueryParam('msg'); const msg = { _id: messageId, rid: room._id }; - const message = Messages.findOne({ _id: msg._id }) || (await call('getMessages', [msg._id]))[0]; + const message = Messages.findOne({ _id: msg._id }) || (await callWithErrorHandling('getMessages', [msg._id]))[0]; if (message && (message.tmid || message.tcount)) { return FlowRouter.setParams({ tab: 'thread', context: message.tmid || message._id }); @@ -90,7 +92,7 @@ export const openRoom = async function(type, name, render = true) { } catch (error) { c.stop(); if (type === 'd') { - const result = await call('createDirectMessage', ...name.split(', ')); + const result = await callWithErrorHandling('createDirectMessage', ...name.split(', ')); if (result) { return FlowRouter.go('direct', { rid: result.rid }, FlowRouter.current().queryParams); } diff --git a/app/ui-utils/client/lib/prependReplies.js b/app/ui-utils/client/lib/prependReplies.js deleted file mode 100644 index eb9b3a8fd5e..00000000000 --- a/app/ui-utils/client/lib/prependReplies.js +++ /dev/null @@ -1,23 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { MessageAction } from './MessageAction'; -import { Rooms, Users } from '../../../models/client'; - -export const prependReplies = async (msg, replies = [], mention = false) => { - const { username } = Users.findOne({ _id: Meteor.userId() }, { fields: { username: 1 } }); - - const chunks = await Promise.all(replies.map(async ({ _id, rid, u }) => { - const permalink = await MessageAction.getPermaLink(_id); - const room = Rooms.findOne(rid, { fields: { t: 1 } }); - - let chunk = `[ ](${ permalink })`; - if (room.t === 'd' && u.username !== username && mention) { - chunk += ` @${ u.username }`; - } - - return chunk; - })); - - chunks.push(msg); - return chunks.join(' '); -}; diff --git a/app/ui-utils/client/lib/rtl.js b/app/ui-utils/client/lib/rtl.js deleted file mode 100644 index 64a55f8dbc1..00000000000 --- a/app/ui-utils/client/lib/rtl.js +++ /dev/null @@ -1,7 +0,0 @@ -// http://stackoverflow.com/a/14824756 - -const ltrChars = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF'; -const rtlChars = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC'; -const rtlRegExp = new RegExp(`^[^${ ltrChars }]*[${ rtlChars }]`); - -export const isRTL = (text) => rtlRegExp.test(text); diff --git a/app/ui-utils/client/lib/warnUserDeletionMayRemoveRooms.js b/app/ui-utils/client/lib/warnUserDeletionMayRemoveRooms.js deleted file mode 100644 index 31c36278c99..00000000000 --- a/app/ui-utils/client/lib/warnUserDeletionMayRemoveRooms.js +++ /dev/null @@ -1,63 +0,0 @@ -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; - -import { t } from '../../../utils/client'; -import { modal } from './modal'; - -export const warnUserDeletionMayRemoveRooms = async function(userId, callbackFn, { warningKey, confirmButtonKey, closeOnConfirm = false, skipModalIfEmpty = false, shouldChangeOwner, shouldBeRemoved }) { - let warningText = warningKey ? t(warningKey) : false; - - if (shouldBeRemoved.length + shouldChangeOwner.length === 0 && skipModalIfEmpty) { - callbackFn(); - return; - } - - if (shouldChangeOwner.length > 0) { - let newText; - - if (shouldChangeOwner.length === 1) { - newText = TAPi18n.__('A_new_owner_will_be_assigned_automatically_to_the__roomName__room', { roomName: shouldChangeOwner.pop() }); - } else if (shouldChangeOwner.length <= 5) { - newText = TAPi18n.__('A_new_owner_will_be_assigned_automatically_to_those__count__rooms__rooms__', { count: shouldChangeOwner.length, rooms: shouldChangeOwner.join(', ') }); - } else { - newText = TAPi18n.__('A_new_owner_will_be_assigned_automatically_to__count__rooms', { count: shouldChangeOwner.length }); - } - - if (warningText) { - warningText = `${ warningText }

 

${ newText }

`; - } else { - warningText = newText; - } - } - - if (shouldBeRemoved.length > 0) { - let newText; - - if (shouldBeRemoved.length === 1) { - newText = TAPi18n.__('The_empty_room__roomName__will_be_removed_automatically', { roomName: shouldBeRemoved.pop() }); - } else if (shouldBeRemoved.length <= 5) { - newText = TAPi18n.__('__count__empty_rooms_will_be_removed_automatically__rooms__', { count: shouldBeRemoved.length, rooms: shouldBeRemoved.join(', ') }); - } else { - newText = TAPi18n.__('__count__empty_rooms_will_be_removed_automatically', { count: shouldBeRemoved.length }); - } - - if (warningText) { - warningText = `${ warningText }

 

${ newText }

`; - } else { - warningText = newText; - } - } - - modal.open({ - title: t('Are_you_sure'), - text: warningText, - type: 'warning', - showCancelButton: true, - confirmButtonColor: '#DD6B55', - confirmButtonText: t(confirmButtonKey || 'Yes_delete_it'), - cancelButtonText: t('Cancel'), - closeOnConfirm, - html: true, - }, () => { - callbackFn(); - }); -}; diff --git a/app/ui/client/components/header/header.js b/app/ui/client/components/header/header.js index 205d52a227f..98f427ac2f3 100644 --- a/app/ui/client/components/header/header.js +++ b/app/ui/client/components/header/header.js @@ -1,6 +1,7 @@ import { Template } from 'meteor/templating'; -import { fireGlobalEvent } from '../../../../ui-utils'; +import { fireGlobalEvent } from '../../../../../client/lib/utils/fireGlobalEvent'; + import './header.html'; Template.header.helpers({ diff --git a/app/ui/client/components/icon.js b/app/ui/client/components/icon.js index 45ef63a494b..e49bec52392 100644 --- a/app/ui/client/components/icon.js +++ b/app/ui/client/components/icon.js @@ -1,7 +1,7 @@ import { FlowRouter } from 'meteor/kadira:flow-router'; import { Template } from 'meteor/templating'; -import { baseURI } from '../../../utils/client/lib/baseuri'; +import { baseURI } from '../../../../client/lib/baseURI'; import './icon.html'; diff --git a/app/ui/client/lib/chatMessages.js b/app/ui/client/lib/chatMessages.js index 35042378fbf..24afc442a02 100644 --- a/app/ui/client/lib/chatMessages.js +++ b/app/ui/client/lib/chatMessages.js @@ -11,15 +11,12 @@ import { escapeHTML } from '@rocket.chat/string-helpers'; import { KonchatNotification } from './notification'; import { MsgTyping } from './msgTyping'; import { fileUpload } from './fileUpload'; -import { t, slashCommands, handleError } from '../../../utils/client'; +import { t, slashCommands } from '../../../utils/client'; import { messageProperties, MessageTypes, readMessage, modal, - call, - keyCodes, - prependReplies, } from '../../../ui-utils/client'; import { settings } from '../../../settings/client'; import { callbacks } from '../../../callbacks/client'; @@ -30,6 +27,10 @@ import { emoji } from '../../../emoji/client'; import { generateTriggerId } from '../../../ui-message/client/ActionManager'; import { imperativeModal } from '../../../../client/lib/imperativeModal'; import GenericModal from '../../../../client/components/GenericModal'; +import { keyCodes } from '../../../../client/lib/utils/keyCodes'; +import { prependReplies } from '../../../../client/lib/utils/prependReplies'; +import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; +import { handleError } from '../../../../client/lib/utils/handleError'; const messageBoxState = { @@ -102,7 +103,7 @@ export class ChatMessages { return; } - const message = Messages.findOne(mid) || await call('getSingleMessage', mid); + const message = Messages.findOne(mid) || await callWithErrorHandling('getSingleMessage', mid); if (!message) { return; } @@ -257,7 +258,7 @@ export class ChatMessages { MsgTyping.stop(rid); if (!ChatSubscription.findOne({ rid })) { - await call('joinRoom', rid); + await callWithErrorHandling('joinRoom', rid); } messageBoxState.save({ rid, tmid }, this.input); @@ -345,7 +346,7 @@ export class ChatMessages { return; } - await call('sendMessage', message); + await callWithErrorHandling('sendMessage', message); } async processSetReaction({ rid, tmid, msg }) { @@ -359,7 +360,7 @@ export class ChatMessages { } const lastMessage = this.collection.findOne({ rid, tmid }, { fields: { ts: 1 }, sort: { ts: -1 } }); - await call('setReaction', reaction, lastMessage._id); + await callWithErrorHandling('setReaction', reaction, lastMessage._id); return true; } @@ -406,7 +407,7 @@ export class ChatMessages { } this.clearEditing(); - await call('updateMessage', message); + await callWithErrorHandling('updateMessage', message); return true; } @@ -521,7 +522,7 @@ export class ChatMessages { } - await call('deleteMessage', { _id }); + await callWithErrorHandling('deleteMessage', { _id }); } keydown(event) { diff --git a/app/ui/client/lib/fileUpload.js b/app/ui/client/lib/fileUpload.js index 2f008ea0a9a..a5f26c65712 100644 --- a/app/ui/client/lib/fileUpload.js +++ b/app/ui/client/lib/fileUpload.js @@ -4,9 +4,9 @@ import { Random } from 'meteor/random'; import { settings } from '../../../settings/client'; import { fileUploadIsValidContentType, APIClient } from '../../../utils'; -import { prependReplies } from '../../../ui-utils'; import { imperativeModal } from '../../../../client/lib/imperativeModal'; import FileUploadModal from '../../../../client/components/modals/FileUploadModal'; +import { prependReplies } from '../../../../client/lib/utils/prependReplies'; export const uploadFileWithMessage = async (rid, tmid, { description, fileName, msg, file }) => { const data = new FormData(); diff --git a/app/ui/client/lib/iframeCommands.js b/app/ui/client/lib/iframeCommands.js index 6af2f986c30..ded9ac783b0 100644 --- a/app/ui/client/lib/iframeCommands.js +++ b/app/ui/client/lib/iframeCommands.js @@ -7,8 +7,8 @@ import { escapeRegExp } from '@rocket.chat/string-helpers'; import { AccountBox } from '../../../ui-utils'; import { settings } from '../../../settings'; import { callbacks } from '../../../callbacks'; -import { baseURI } from '../../../utils/client/lib/baseuri.js'; import { add, remove } from '../../../../client/views/room/lib/Toolbox/IframeButtons'; +import { baseURI } from '../../../../client/lib/baseURI'; const commands = { go(data) { diff --git a/app/ui/client/lib/notification.js b/app/ui/client/lib/notification.js index b249b451a1a..c656851eb08 100644 --- a/app/ui/client/lib/notification.js +++ b/app/ui/client/lib/notification.js @@ -12,9 +12,9 @@ import { e2e } from '../../../e2e/client'; import { Users, ChatSubscription } from '../../../models'; import { getUserPreference } from '../../../utils'; import { getUserAvatarURL } from '../../../utils/lib/getUserAvatarURL'; -import { getAvatarAsPng } from '../../../ui-utils'; import { promises } from '../../../promises/client'; import { CustomSounds } from '../../../custom-sounds/client/lib/CustomSounds'; +import { getAvatarAsPng } from '../../../../client/lib/utils/getAvatarAsPng'; export const KonchatNotification = { notificationStatus: new ReactiveVar(), diff --git a/app/ui/client/views/app/burger.js b/app/ui/client/views/app/burger.js index a7e34daa2b8..53d2e5d5269 100644 --- a/app/ui/client/views/app/burger.js +++ b/app/ui/client/views/app/burger.js @@ -3,7 +3,8 @@ import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; import { ChatSubscription } from '../../../../models/client'; -import { Layout, menu } from '../../../../ui-utils/client'; +import { menu } from '../../../../ui-utils/client'; +import { isLayoutEmbedded } from '../../../../../client/lib/utils/isLayoutEmbedded'; import { getUserPreference } from '../../../../utils'; Template.burger.helpers({ @@ -50,7 +51,7 @@ Template.burger.helpers({ }, embeddedVersion() { - return Layout.isEmbedded(); + return isLayoutEmbedded(); }, }); diff --git a/app/ui/client/views/app/lib/getCommonRoomEvents.js b/app/ui/client/views/app/lib/getCommonRoomEvents.js index c273067e36e..869464451fa 100644 --- a/app/ui/client/views/app/lib/getCommonRoomEvents.js +++ b/app/ui/client/views/app/lib/getCommonRoomEvents.js @@ -4,15 +4,13 @@ import { Random } from 'meteor/random'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { - fireGlobalEvent, popover, - Layout, MessageAction, } from '../../../../../ui-utils/client'; import { addMessageToList, } from '../../../../../ui-utils/client/lib/MessageAction'; -import { call } from '../../../../../ui-utils/client/lib/callMethod'; +import { callWithErrorHandling } from '../../../../../../client/lib/utils/callWithErrorHandling'; import { promises } from '../../../../../promises/client'; import { isURL } from '../../../../../utils/lib/isURL'; import { openUserCard } from '../../../lib/UserCard'; @@ -21,7 +19,9 @@ import { ChatMessage, Rooms, Messages } from '../../../../../models'; import { t } from '../../../../../utils/client'; import { chatMessages } from '../room'; import { EmojiEvents } from '../../../../../reactions/client/init'; -import { goToRoomById } from '../../../../../../client/lib/goToRoomById'; +import { goToRoomById } from '../../../../../../client/lib/utils/goToRoomById'; +import { fireGlobalEvent } from '../../../../../../client/lib/utils/fireGlobalEvent'; +import { isLayoutEmbedded } from '../../../../../../client/lib/utils/isLayoutEmbedded'; const mountPopover = (e, i, outerContext) => { let context = $(e.target).parents('.message').data('context'); @@ -163,13 +163,13 @@ export const getCommonRoomEvents = () => ({ e.preventDefault(); e.stopPropagation(); const { msg } = messageArgs(this); - call('followMessage', { mid: msg._id }); + callWithErrorHandling('followMessage', { mid: msg._id }); }, 'click .js-unfollow-thread'(e) { e.preventDefault(); e.stopPropagation(); const { msg } = messageArgs(this); - call('unfollowMessage', { mid: msg._id }); + callWithErrorHandling('unfollowMessage', { mid: msg._id }); }, 'click .js-open-thread'(event) { event.preventDefault(); @@ -259,7 +259,7 @@ export const getCommonRoomEvents = () => ({ return; } - await call('sendMessage', msgObject); + await callWithErrorHandling('sendMessage', msgObject); }, 'click .message-actions__menu'(e, template) { const messageContext = messageArgs(this); @@ -311,7 +311,7 @@ export const getCommonRoomEvents = () => ({ const { currentTarget: { dataset: { channel, group, username } } } = e; if (channel) { - if (Layout.isEmbedded()) { + if (isLayoutEmbedded()) { fireGlobalEvent('click-mention-link', { path: FlowRouter.path('channel', { name: channel }), channel }); } goToRoomById(channel); diff --git a/app/ui/client/views/app/room.js b/app/ui/client/views/app/room.js index 7914043a6c5..2abbeb94d25 100644 --- a/app/ui/client/views/app/room.js +++ b/app/ui/client/views/app/room.js @@ -10,15 +10,13 @@ import { FlowRouter } from 'meteor/kadira:flow-router'; import { Session } from 'meteor/session'; import { Template } from 'meteor/templating'; -import { t, roomTypes, getUserPreference, handleError } from '../../../../utils/client'; +import { t, roomTypes, getUserPreference } from '../../../../utils/client'; import { WebRTC } from '../../../../webrtc/client'; import { ChatMessage, RoomRoles, Users, Subscriptions, Rooms } from '../../../../models'; import { - fireGlobalEvent, RoomHistoryManager, RoomManager, readMessage, - Layout, } from '../../../../ui-utils/client'; import { messageContext } from '../../../../ui-utils/client/lib/messageContext'; import { messageArgs } from '../../../../ui-utils/client/lib/messageArgs'; @@ -30,6 +28,9 @@ import { fileUpload } from '../../lib/fileUpload'; import './room.html'; import { getCommonRoomEvents } from './lib/getCommonRoomEvents'; import { RoomManager as NewRoomManager } from '../../../../../client/lib/RoomManager'; +import { fireGlobalEvent } from '../../../../../client/lib/utils/fireGlobalEvent'; +import { isLayoutEmbedded } from '../../../../../client/lib/utils/isLayoutEmbedded'; +import { handleError } from '../../../../../client/lib/utils/handleError'; export const chatMessages = {}; @@ -39,7 +40,7 @@ const userCanDrop = (_id) => !roomTypes.readOnly(_id, Users.findOne({ _id: Meteo export const openProfileTab = (e, tabBar, username) => { e.stopPropagation(); - if (Layout.isEmbedded()) { + if (isLayoutEmbedded()) { fireGlobalEvent('click-user-card-message', { username }); e.preventDefault(); return; @@ -272,7 +273,7 @@ Template.roomOld.helpers({ messageboxData() { const { sendToBottomIfNecessary, subscription } = Template.instance(); const { _id: rid } = this; - const isEmbedded = Layout.isEmbedded(); + const isEmbedded = isLayoutEmbedded(); const showFormattingTips = settings.get('Message_ShowFormattingTips'); return { diff --git a/app/utils/client/index.js b/app/utils/client/index.js index 4ada524ca0b..98078f775fe 100644 --- a/app/utils/client/index.js +++ b/app/utils/client/index.js @@ -2,7 +2,6 @@ export { t, isRtl } from '../lib/tapi18n'; export { getDefaultSubscriptionPref } from '../lib/getDefaultSubscriptionPref'; export { Info } from '../rocketchat.info'; export { isEmail } from '../lib/isEmail'; -export { handleError } from './lib/handleError'; export { getUserPreference } from '../lib/getUserPreference'; export { fileUploadMediaWhiteList, fileUploadIsValidContentType } from '../lib/fileUploadRestrictions'; export { roomTypes } from './lib/roomTypes'; @@ -11,12 +10,9 @@ export { RoomTypesCommon } from '../lib/RoomTypesCommon'; export { getUserAvatarURL } from '../lib/getUserAvatarURL'; export { slashCommands } from '../lib/slashCommand'; export { getUserNotificationPreference } from '../lib/getUserNotificationPreference'; -export { applyCustomTranslations } from './lib/CustomTranslations'; export { getAvatarColor } from '../lib/getAvatarColor'; export { getURL } from '../lib/getURL'; export { placeholders } from '../lib/placeholders'; export { templateVarHandler } from '../lib/templateVarHandler'; -export { APIClient, mountArrayQueryParameters } from './lib/RestApiClient'; -export { canDeleteMessage } from './lib/canDeleteMessage'; +export { APIClient } from './lib/RestApiClient'; export { secondsToHHMMSS } from '../lib/timeConverter'; -export { waitUntilFind } from './lib/waitUntilFind'; diff --git a/app/utils/client/lib/CustomTranslations.js b/app/utils/client/lib/CustomTranslations.js deleted file mode 100644 index 7754e9a609e..00000000000 --- a/app/utils/client/lib/CustomTranslations.js +++ /dev/null @@ -1,22 +0,0 @@ -import { TAPi18n, TAPi18next } from 'meteor/rocketchat:tap-i18n'; - -import { settings } from '../../../settings'; - -export function applyCustomTranslations() { - let CustomTranslations = settings.get('Custom_Translations'); - if (typeof CustomTranslations === 'string' && CustomTranslations.trim() !== '') { - try { - CustomTranslations = JSON.parse(CustomTranslations); - - for (const lang in CustomTranslations) { - if (CustomTranslations.hasOwnProperty(lang)) { - const translations = CustomTranslations[lang]; - TAPi18next.addResourceBundle(lang, 'project', translations); - } - } - TAPi18n._language_changed_tracker.changed(); - } catch (e) { - console.error('Invalid setting Custom_Translations', e); - } - } -} diff --git a/app/utils/client/lib/RestApiClient.js b/app/utils/client/lib/RestApiClient.js index ba23a1cf3c0..5dae274c803 100644 --- a/app/utils/client/lib/RestApiClient.js +++ b/app/utils/client/lib/RestApiClient.js @@ -1,13 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; -import { baseURI } from './baseuri'; import { process2faReturn } from '../../../2fa/client/callWithTwoFactorRequired'; - -export const mountArrayQueryParameters = (label, array) => array.reduce((acc, item) => { - acc += `${ label }[]=${ item }&`; - return acc; -}, ''); +import { baseURI } from '../../../../client/lib/baseURI'; export const APIClient = { delete(endpoint, params) { diff --git a/app/utils/client/lib/baseuri.js b/app/utils/client/lib/baseuri.js deleted file mode 100644 index a0545b56ecd..00000000000 --- a/app/utils/client/lib/baseuri.js +++ /dev/null @@ -1,18 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -export const baseURI = (() => { - if (document.baseURI) { return document.baseURI; } - - // Should be exactly one tag: - // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base - const base = document.getElementsByTagName('base'); - - // Return location from BASE tag. - if (base.length > 0) { return base[0].href; } - - // Else use implementation of documentURI: - // http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-baseURI - return document.URL; -})(); - -Meteor.absoluteUrl.defaultOptions = { ...Meteor.absoluteUrl.defaultOptions, rootUrl: baseURI }; diff --git a/app/utils/client/lib/canDeleteMessage.js b/app/utils/client/lib/canDeleteMessage.js deleted file mode 100644 index 3f35d597452..00000000000 --- a/app/utils/client/lib/canDeleteMessage.js +++ /dev/null @@ -1,41 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import moment from 'moment'; - -import { hasPermission } from '../../../authorization/client'; -import { settings } from '../../../settings/client'; - -export const canDeleteMessage = ({ rid, ts, uid }) => { - const userId = Meteor.userId(); - - const forceDelete = hasPermission('force-delete-message', rid); - if (forceDelete) { - return true; - } - - const isDeleteAllowed = settings.get('Message_AllowDeleting'); - if (!isDeleteAllowed) { - return false; - } - - const allowed = hasPermission('delete-message', rid); - - const deleteOwn = allowed || (uid === userId && hasPermission('delete-own-message')); - if (!allowed && !deleteOwn) { - return false; - } - - const blockDeleteInMinutes = settings.get('Message_AllowDeleting_BlockDeleteInMinutes'); - if (blockDeleteInMinutes != null && blockDeleteInMinutes !== 0) { - let msgTs; - if (ts != null) { - msgTs = moment(ts); - } - let currentTsDiff; - if (msgTs != null) { - currentTsDiff = moment().diff(msgTs, 'minutes'); - } - return currentTsDiff < blockDeleteInMinutes; - } - - return true; -}; diff --git a/app/utils/client/lib/handleError.js b/app/utils/client/lib/handleError.js deleted file mode 100644 index 120c3b7d41f..00000000000 --- a/app/utils/client/lib/handleError.js +++ /dev/null @@ -1,32 +0,0 @@ -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; -import _ from 'underscore'; -import toastr from 'toastr'; -import { escapeHTML } from '@rocket.chat/string-helpers'; - -export const handleError = function(error, useToastr = true) { - if (error.xhr) { - error = error.xhr.responseJSON || {}; - } - - if (_.isObject(error.details)) { - for (const key in error.details) { - if (error.details.hasOwnProperty(key)) { - error.details[key] = TAPi18n.__(error.details[key]); - } - } - } - - if (useToastr) { - if (error.toastrShowed) { - return; - } - const details = Object.entries(error.details || {}) - .reduce((obj, [key, value]) => ({ ...obj, [key]: escapeHTML(value) }), {}); - const message = TAPi18n.__(error.error || error.message, details); - const title = details.errorTitle && TAPi18n.__(details.errorTitle); - - return toastr.error(message, title); - } - - return escapeHTML(TAPi18n.__(error.error || error.message, error.details)); -}; diff --git a/app/utils/client/lib/waitUntilFind.ts b/app/utils/client/lib/waitUntilFind.ts deleted file mode 100644 index bb07282ef16..00000000000 --- a/app/utils/client/lib/waitUntilFind.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Tracker } from 'meteor/tracker'; - -export const waitUntilFind = (fn: () => T | undefined): Promise => new Promise((resolve) => { - Tracker.autorun((c) => { - const result = fn(); - - if (result === undefined) { - return; - } - - c.stop(); - resolve(result); - }); -}); diff --git a/app/webrtc/client/WebRTCClass.js b/app/webrtc/client/WebRTCClass.js index 67610a6c0b6..1d8485b245d 100644 --- a/app/webrtc/client/WebRTCClass.js +++ b/app/webrtc/client/WebRTCClass.js @@ -11,7 +11,7 @@ import { settings } from '../../settings'; import { modal } from '../../ui-utils'; import { ChatSubscription } from '../../models'; import { WEB_RTC_EVENTS } from '..'; -import { goToRoomById } from '../../../client/lib/goToRoomById'; +import { goToRoomById } from '../../../client/lib/utils/goToRoomById'; class WebRTCTransportClass extends Emitter { constructor(webrtcInstance) { diff --git a/app/webrtc/client/screenShare.js b/app/webrtc/client/screenShare.js index 827d1fb3eba..7099474be78 100644 --- a/app/webrtc/client/screenShare.js +++ b/app/webrtc/client/screenShare.js @@ -1,4 +1,4 @@ -import { fireGlobalEvent } from '../../ui-utils'; +import { fireGlobalEvent } from '../../../client/lib/utils/fireGlobalEvent'; export const ChromeScreenShare = { callbacks: {}, diff --git a/client/.eslintrc.js b/client/.eslintrc.js index d5bb431b989..4d6bf74449b 100644 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -16,7 +16,10 @@ module.exports = { }, ], 'jsx-quotes': ['error', 'prefer-single'], - 'new-cap': ['error', { capIsNewExceptions: ['HTML.Comment', 'HTML.DIV', 'SHA256'] }], + 'new-cap': [ + 'error', + { capIsNewExceptions: ['HTML.Comment', 'HTML.Raw', 'HTML.DIV', 'SHA256'] }, + ], 'prefer-arrow-callback': ['error', { allowNamedFunctions: true }], 'prettier/prettier': 2, 'react/display-name': 'error', @@ -83,7 +86,10 @@ module.exports = { }, ], 'jsx-quotes': ['error', 'prefer-single'], - 'new-cap': ['error', { capIsNewExceptions: ['HTML.Comment', 'HTML.DIV', 'SHA256'] }], + 'new-cap': [ + 'error', + { capIsNewExceptions: ['HTML.Comment', 'HTML.Raw', 'HTML.DIV', 'SHA256'] }, + ], 'no-extra-parens': 'off', 'no-spaced-func': 'off', 'no-unused-vars': 'off', diff --git a/client/components/CreateDiscussion/CreateDiscussion.tsx b/client/components/CreateDiscussion/CreateDiscussion.tsx index c792e2348fa..6f2601cb7b4 100644 --- a/client/components/CreateDiscussion/CreateDiscussion.tsx +++ b/client/components/CreateDiscussion/CreateDiscussion.tsx @@ -19,7 +19,7 @@ import { IUser } from '../../../definition/IUser'; import { useTranslation } from '../../contexts/TranslationContext'; import { useEndpointActionExperimental } from '../../hooks/useEndpointActionExperimental'; import { useForm } from '../../hooks/useForm'; -import { goToRoomById } from '../../lib/goToRoomById'; +import { goToRoomById } from '../../lib/utils/goToRoomById'; import RoomAutoComplete from '../RoomAutoComplete'; import UserAutoCompleteMultiple from '../UserAutoCompleteMultiple'; import DefaultParentRoomField from './DefaultParentRoomField'; diff --git a/client/components/Emoji.js b/client/components/Emoji.js index 5298b9c3c17..b5e94bfe6ab 100644 --- a/client/components/Emoji.js +++ b/client/components/Emoji.js @@ -1,6 +1,6 @@ import React from 'react'; -import { renderEmoji } from '../../app/emoji/client/index'; +import { renderEmoji } from '../lib/utils/renderEmoji'; function Emoji({ emojiHandle, className = undefined }) { const markup = { __html: `${renderEmoji(emojiHandle)}` }; diff --git a/client/components/MarkdownText.tsx b/client/components/MarkdownText.tsx index d89000c6db8..4baefbab19d 100644 --- a/client/components/MarkdownText.tsx +++ b/client/components/MarkdownText.tsx @@ -3,7 +3,7 @@ import dompurify from 'dompurify'; import marked from 'marked'; import React, { ComponentProps, FC, useMemo } from 'react'; -import { renderMessageEmoji } from '../lib/renderMessageEmoji'; +import { renderMessageEmoji } from '../lib/utils/renderMessageEmoji'; type MarkdownTextParams = { content: string; diff --git a/client/components/Message/Body/Link.tsx b/client/components/Message/Body/Link.tsx index 5ecc831933b..4b8ec157cd0 100644 --- a/client/components/Message/Body/Link.tsx +++ b/client/components/Message/Body/Link.tsx @@ -1,7 +1,7 @@ import { Link as ASTLink } from '@rocket.chat/message-parser'; import React, { FC } from 'react'; -import { baseURI } from '../../../lib/baseuri'; +import { baseURI } from '../../../lib/baseURI'; import Bold from './Bold'; import Italic from './Italic'; import Strike from './Strike'; diff --git a/client/components/modals/FileUploadModal/FilePreview.tsx b/client/components/modals/FileUploadModal/FilePreview.tsx index f518dc7ebf4..4045a438bc6 100644 --- a/client/components/modals/FileUploadModal/FilePreview.tsx +++ b/client/components/modals/FileUploadModal/FilePreview.tsx @@ -1,6 +1,6 @@ import React, { ReactElement } from 'react'; -import { isIE11 } from '../../../../app/ui-utils/client/lib/isIE11'; +import { isIE11 } from '../../../lib/utils/isIE11'; import GenericPreview from './GenericPreview'; import MediaPreview from './MediaPreview'; diff --git a/client/components/modals/FileUploadModal/GenericPreview.tsx b/client/components/modals/FileUploadModal/GenericPreview.tsx index 26c5884eb14..4f74d98aa8d 100644 --- a/client/components/modals/FileUploadModal/GenericPreview.tsx +++ b/client/components/modals/FileUploadModal/GenericPreview.tsx @@ -1,7 +1,7 @@ import { Box, Icon } from '@rocket.chat/fuselage'; import React, { ReactElement } from 'react'; -import { formatBytes } from '../../../lib/formatBytes'; +import { formatBytes } from '../../../lib/utils/formatBytes'; const GenericPreview = ({ file }: { file: File }): ReactElement => ( diff --git a/client/contexts/ServerContext/methods.ts b/client/contexts/ServerContext/methods.ts index 21e85d22d40..7b20792e0b9 100644 --- a/client/contexts/ServerContext/methods.ts +++ b/client/contexts/ServerContext/methods.ts @@ -1,3 +1,4 @@ +import { IRoom } from '../../../definition/IRoom'; import { FollowMessageMethod } from './methods/followMessage'; import { UnsubscribeMethod as MailerUnsubscribeMethod } from './methods/mailer/unsubscribe'; import { RoomNameExistsMethod } from './methods/roomNameExists'; @@ -58,6 +59,7 @@ export type ServerMethods = { 'jitsi:generateAccessToken': (...args: any[]) => any; 'jitsi:updateTimeout': (...args: any[]) => any; 'leaveRoom': (...args: any[]) => any; + 'license:getModules': () => string[]; 'license:getTags': (...args: any[]) => any; 'livechat:addMonitor': (...args: any[]) => any; 'livechat:changeLivechatStatus': (...args: any[]) => any; @@ -132,6 +134,7 @@ export type ServerMethods = { 'updateOutgoingIntegration': (...args: any[]) => any; 'uploadCustomSound': (...args: any[]) => any; 'Mailer:unsubscribe': MailerUnsubscribeMethod; + 'getRoomById': (rid: IRoom['_id']) => IRoom; }; export type ServerMethodName = keyof ServerMethods; diff --git a/client/hooks/lists/useScrollableMessageList.ts b/client/hooks/lists/useScrollableMessageList.ts index e6f9d43c4dd..6daf5371a30 100644 --- a/client/hooks/lists/useScrollableMessageList.ts +++ b/client/hooks/lists/useScrollableMessageList.ts @@ -2,9 +2,9 @@ import { useCallback } from 'react'; import { IMessage } from '../../../definition/IMessage'; import { Serialized } from '../../../definition/Serialized'; -import { mapMessageFromApi } from '../../lib/fromApi'; import { MessageList } from '../../lib/lists/MessageList'; import { RecordListBatchChanges } from '../../lib/lists/RecordList'; +import { mapMessageFromApi } from '../../lib/utils/mapMessageFromApi'; import { useScrollableRecordList } from './useScrollableRecordList'; export const useScrollableMessageList = ( diff --git a/client/lib/RoomManager.ts b/client/lib/RoomManager.ts index 4cf4c13957b..78a4104bdde 100644 --- a/client/lib/RoomManager.ts +++ b/client/lib/RoomManager.ts @@ -2,11 +2,11 @@ import { Emitter } from '@rocket.chat/emitter'; import { useEffect, useMemo } from 'react'; import { useSubscription, Subscription, Unsubscribe } from 'use-subscription'; -import { getConfig } from '../../app/ui-utils/client/config'; import { IRoom } from '../../definition/IRoom'; import { useUserId, useUserRoom, useUserSubscription } from '../contexts/UserContext'; import { useAsyncState } from '../hooks/useAsyncState'; import { AsyncState } from './asyncState'; +import { getConfig } from './utils/getConfig'; const debug = !!(getConfig('debug') || getConfig('debug-RoomStore')); diff --git a/client/lib/baseuri.ts b/client/lib/baseURI.ts similarity index 100% rename from client/lib/baseuri.ts rename to client/lib/baseURI.ts diff --git a/client/lib/utils/applyCustomTranslations.ts b/client/lib/utils/applyCustomTranslations.ts new file mode 100644 index 00000000000..5cc4272e25e --- /dev/null +++ b/client/lib/utils/applyCustomTranslations.ts @@ -0,0 +1,22 @@ +import { TAPi18n, TAPi18next } from 'meteor/rocketchat:tap-i18n'; + +import { settings } from '../../../app/settings/client'; + +export const applyCustomTranslations = (): void => { + const customTranslations: string | undefined = settings.get('Custom_Translations'); + + if (!customTranslations) { + return; + } + + try { + const parsedCustomTranslations: Record = JSON.parse(customTranslations); + + for (const [lang, translations] of Object.entries(parsedCustomTranslations)) { + TAPi18next.addResourceBundle(lang, 'project', translations); + } + TAPi18n._language_changed_tracker.changed(); + } catch (e) { + console.error('Invalid setting Custom_Translations', e); + } +}; diff --git a/client/lib/utils/call.ts b/client/lib/utils/call.ts new file mode 100644 index 00000000000..3530e755c0d --- /dev/null +++ b/client/lib/utils/call.ts @@ -0,0 +1,22 @@ +import { Meteor } from 'meteor/meteor'; + +import { + ServerMethodName, + ServerMethodParameters, + ServerMethodReturn, +} from '../../contexts/ServerContext'; + +export const call = ( + method: M, + ...params: ServerMethodParameters +): Promise> => + new Promise((resolve, reject) => { + Meteor.call(method, ...params, (error: Error, result: ServerMethodReturn) => { + if (error) { + reject(error); + return; + } + + resolve(result); + }); + }); diff --git a/client/lib/utils/callWithErrorHandling.ts b/client/lib/utils/callWithErrorHandling.ts new file mode 100644 index 00000000000..1b0f2b8cfe8 --- /dev/null +++ b/client/lib/utils/callWithErrorHandling.ts @@ -0,0 +1,19 @@ +import { + ServerMethodName, + ServerMethodParameters, + ServerMethodReturn, +} from '../../contexts/ServerContext'; +import { call } from './call'; +import { handleError } from './handleError'; + +export const callWithErrorHandling = async ( + method: M, + ...params: ServerMethodParameters +): Promise> => { + try { + return await call(method, ...params); + } catch (error) { + handleError(error); + throw error; + } +}; diff --git a/client/lib/utils/canDeleteMessage.ts b/client/lib/utils/canDeleteMessage.ts new file mode 100644 index 00000000000..8402daf2b81 --- /dev/null +++ b/client/lib/utils/canDeleteMessage.ts @@ -0,0 +1,52 @@ +import { Meteor } from 'meteor/meteor'; +import moment, { MomentInput } from 'moment'; + +import { hasPermission } from '../../../app/authorization/client'; +import { settings } from '../../../app/settings/client'; +import { IRoom } from '../../../definition/IRoom'; +import { IUser } from '../../../definition/IUser'; + +export const canDeleteMessage = ({ + rid, + ts, + uid, +}: { + rid: IRoom['_id']; + ts: MomentInput; + uid: IUser['_id']; +}): boolean => { + const userId = Meteor.userId(); + + const forceDelete = hasPermission('force-delete-message', rid); + if (forceDelete) { + return true; + } + + const isDeleteAllowed: boolean | undefined = settings.get('Message_AllowDeleting'); + if (!isDeleteAllowed) { + return false; + } + + const allowed = hasPermission('delete-message', rid); + + const deleteOwn = allowed || (uid === userId && hasPermission('delete-own-message')); + if (!allowed && !deleteOwn) { + return false; + } + + const blockDeleteInMinutes: number | undefined = settings.get( + 'Message_AllowDeleting_BlockDeleteInMinutes', + ); + if (blockDeleteInMinutes && blockDeleteInMinutes !== 0) { + if (!ts) { + return false; + } + + const msgTs = moment(ts); + const now = moment(); + + return now.diff(msgTs, 'minutes') < blockDeleteInMinutes; + } + + return true; +}; diff --git a/client/lib/utils/fireGlobalEvent.ts b/client/lib/utils/fireGlobalEvent.ts new file mode 100644 index 00000000000..ca6b1cba2be --- /dev/null +++ b/client/lib/utils/fireGlobalEvent.ts @@ -0,0 +1,26 @@ +import { Tracker } from 'meteor/tracker'; + +import { settings } from '../../../app/settings/client'; + +export const fireGlobalEvent = (eventName: string, detail: unknown): void => { + window.dispatchEvent(new CustomEvent(eventName, { detail })); + + Tracker.autorun((computation) => { + const enabled = settings.get('Iframe_Integration_send_enable'); + if (enabled === undefined) { + return; + } + + computation.stop(); + + if (enabled) { + parent.postMessage( + { + eventName, + data: detail, + }, + settings.get('Iframe_Integration_send_target_origin'), + ); + } + }); +}; diff --git a/client/lib/formatBytes.ts b/client/lib/utils/formatBytes.ts similarity index 100% rename from client/lib/formatBytes.ts rename to client/lib/utils/formatBytes.ts diff --git a/client/lib/utils/formatDate.ts b/client/lib/utils/formatDate.ts new file mode 100644 index 00000000000..52962e95db0 --- /dev/null +++ b/client/lib/utils/formatDate.ts @@ -0,0 +1,13 @@ +import mem from 'mem'; +import { Tracker } from 'meteor/tracker'; +import moment, { MomentInput } from 'moment'; + +import { settings } from '../../../app/settings/client'; + +export const formatDate = mem( + (time: MomentInput) => { + const messageDateFormat = Tracker.nonreactive(() => settings.get('Message_DateFormat')); + return moment(time).format(messageDateFormat); + }, + { maxAge: 5000 }, +); diff --git a/client/lib/utils/formatDateAndTime.ts b/client/lib/utils/formatDateAndTime.ts new file mode 100644 index 00000000000..c9bd6469de8 --- /dev/null +++ b/client/lib/utils/formatDateAndTime.ts @@ -0,0 +1,24 @@ +import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; +import moment, { MomentInput } from 'moment'; + +import { settings } from '../../../app/settings/client'; +import { getUserPreference } from '../../../app/utils/lib/getUserPreference'; + +export const formatDateAndTime = (time: MomentInput): string => { + const clockMode = Tracker.nonreactive(() => + getUserPreference(Meteor.userId(), 'clockMode', false), + ); + const messageTimeAndDateFormat = Tracker.nonreactive(() => + settings.get('Message_TimeAndDateFormat'), + ); + + switch (clockMode) { + case 1: + return moment(time).format('MMMM D, Y h:mm A'); + case 2: + return moment(time).format('MMMM D, Y H:mm'); + default: + return moment(time).format(messageTimeAndDateFormat); + } +}; diff --git a/client/lib/utils/formatTime.ts b/client/lib/utils/formatTime.ts new file mode 100644 index 00000000000..e80573f0d0a --- /dev/null +++ b/client/lib/utils/formatTime.ts @@ -0,0 +1,24 @@ +import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; +import moment, { MomentInput } from 'moment'; + +import { settings } from '../../../app/settings/client'; +import { getUserPreference } from '../../../app/utils/lib/getUserPreference'; + +const dayFormat = ['h:mm A', 'H:mm']; + +export const formatTime = (time: MomentInput): string => { + const clockMode = Tracker.nonreactive(() => + getUserPreference(Meteor.userId(), 'clockMode', false), + ); + const messageTimeFormat = Tracker.nonreactive(() => settings.get('Message_TimeFormat')); + const sameDay = dayFormat[clockMode - 1] || messageTimeFormat; + + switch (clockMode) { + case 1: + case 2: + return moment(time).format(sameDay); + default: + return moment(time).format(messageTimeFormat); + } +}; diff --git a/client/lib/utils/getAvatarAsPng.ts b/client/lib/utils/getAvatarAsPng.ts new file mode 100644 index 00000000000..90e5f147b92 --- /dev/null +++ b/client/lib/utils/getAvatarAsPng.ts @@ -0,0 +1,35 @@ +import { getUserAvatarURL } from '../../../app/utils/lib/getUserAvatarURL'; +import { IUser } from '../../../definition/IUser'; + +export const getAvatarAsPng = ( + username: IUser['username'], + cb: (dataURL: string) => void, +): (() => void) => { + const image = new Image(); + + const onLoad = (): void => { + const canvas = document.createElement('canvas'); + canvas.width = image.width; + canvas.height = image.height; + const context = canvas.getContext('2d'); + + if (!context) { + throw new Error('failed to get canvas context'); + } + + context.drawImage(image, 0, 0); + try { + return cb(canvas.toDataURL('image/png')); + } catch (e) { + return cb(''); + } + }; + + const onError = (): void => cb(''); + + image.onload = onLoad; + image.onerror = onError; + image.src = getUserAvatarURL(username); + + return onError; +}; diff --git a/client/lib/utils/getConfig.ts b/client/lib/utils/getConfig.ts new file mode 100644 index 00000000000..f49c729bc90 --- /dev/null +++ b/client/lib/utils/getConfig.ts @@ -0,0 +1,6 @@ +import { Meteor } from 'meteor/meteor'; + +export const getConfig = (key: string): string | null => { + const searchParams = new URLSearchParams(window.location.search); + return searchParams.get(key) || Meteor._localStorage.getItem(`rc-config-${key}`); +}; diff --git a/client/lib/getDateRange.ts b/client/lib/utils/getDateRange.ts similarity index 100% rename from client/lib/getDateRange.ts rename to client/lib/utils/getDateRange.ts diff --git a/client/lib/utils/getUidDirectMessage.ts b/client/lib/utils/getUidDirectMessage.ts new file mode 100644 index 00000000000..b60b13c64d2 --- /dev/null +++ b/client/lib/utils/getUidDirectMessage.ts @@ -0,0 +1,18 @@ +import { Meteor } from 'meteor/meteor'; + +import { Rooms } from '../../../app/models/client'; +import { IRoom } from '../../../definition/IRoom'; +import { IUser } from '../../../definition/IUser'; + +export const getUidDirectMessage = ( + rid: IRoom['_id'], + userId: IUser['_id'] | null = Meteor.userId(), +): string | undefined => { + const room: IRoom | null = Rooms.findOne({ _id: rid }, { fields: { uids: 1 } }); + + if (!room || !room.uids || room.uids.length > 2) { + return undefined; + } + + return room.uids.filter((uid) => uid !== userId)[0]; +}; diff --git a/client/lib/getUserEmailVerified.ts b/client/lib/utils/getUserEmailVerified.ts similarity index 75% rename from client/lib/getUserEmailVerified.ts rename to client/lib/utils/getUserEmailVerified.ts index 3102613f8b0..efae8b7abde 100644 --- a/client/lib/getUserEmailVerified.ts +++ b/client/lib/utils/getUserEmailVerified.ts @@ -1,4 +1,4 @@ -import type { IUser } from '../../definition/IUser'; +import type { IUser } from '../../../definition/IUser'; export const getUserEmailVerified = (user: IUser): boolean | undefined => Array.isArray(user.emails) ? user.emails.find(({ verified }) => !!verified)?.verified : undefined; diff --git a/client/lib/goToRoomById.ts b/client/lib/utils/goToRoomById.ts similarity index 61% rename from client/lib/goToRoomById.ts rename to client/lib/utils/goToRoomById.ts index 1be5bd2ce67..fcc9a7948f8 100644 --- a/client/lib/goToRoomById.ts +++ b/client/lib/utils/goToRoomById.ts @@ -1,12 +1,12 @@ import { memoize } from '@rocket.chat/memo'; import { FlowRouter } from 'meteor/kadira:flow-router'; -import { ChatSubscription } from '../../app/models/client'; -import { call } from '../../app/ui-utils/client'; -import { roomTypes } from '../../app/utils/client'; -import { IRoom } from '../../definition/IRoom'; +import { ChatSubscription } from '../../../app/models/client'; +import { roomTypes } from '../../../app/utils/client'; +import { IRoom } from '../../../definition/IRoom'; +import { callWithErrorHandling } from './callWithErrorHandling'; -const getRoomById = memoize((rid: IRoom['_id']) => call('getRoomById', rid)); +const getRoomById = memoize((rid: IRoom['_id']) => callWithErrorHandling('getRoomById', rid)); export const goToRoomById = async (rid: IRoom['_id']): Promise => { if (!rid) { diff --git a/client/lib/utils/handleError.ts b/client/lib/utils/handleError.ts new file mode 100644 index 00000000000..13410c9edc1 --- /dev/null +++ b/client/lib/utils/handleError.ts @@ -0,0 +1,59 @@ +import { escapeHTML } from '@rocket.chat/string-helpers'; +import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; +import toastr from 'toastr'; + +const hasXHR = (error: {}): error is { xhr: JQuery.jqXHR } => 'xhr' in error; + +const hasDetails = (error: {}): error is { details: Record } => + 'details' in error && typeof (error as { details: unknown }).details === 'object'; + +const hasToastrShowed = (error: {}): error is { toastrShowed: true } => + 'toastrShowed' in error && (error as { toastrShowed: unknown }).toastrShowed === true; + +const hasError = (error: {}): error is { error: string } => + 'error' in error && typeof (error as { error: unknown }).error === 'string'; + +const hasMessage = (error: {}): error is { message: string } => + 'error' in error && typeof (error as { message: unknown }).message === 'string'; + +const hasErrorTitle = ( + details: Record, +): details is Record & { errorTitle: string } => + 'errorTitle' in details && + typeof (details as Record & { errorTitle: unknown }) === 'string'; + +export const handleError = ( + error: {}, + useToastr = true, +): JQuery | string | undefined => { + if (hasXHR(error) && error.xhr.responseJSON) { + return handleError(error.xhr.responseJSON, useToastr); + } + + const message = + (hasError(error) && error.error) || (hasMessage(error) && error.message) || undefined; + const details = hasDetails(error) ? error.details : {}; + + if (useToastr) { + if (hasToastrShowed(error)) { + return; + } + + return toastr.error( + TAPi18n.__( + message, + Object.fromEntries( + Object.entries(details).map(([key, value]) => [key, escapeHTML(TAPi18n.__(value))]), + ), + ), + hasErrorTitle(details) ? TAPi18n.__(details.errorTitle) : undefined, + ); + } + + return escapeHTML( + TAPi18n.__( + message, + Object.fromEntries(Object.entries(details).map(([key, value]) => [key, TAPi18n.__(value)])), + ), + ); +}; diff --git a/app/ui-utils/client/lib/isIE11.js b/client/lib/utils/isIE11.ts similarity index 90% rename from app/ui-utils/client/lib/isIE11.js rename to client/lib/utils/isIE11.ts index c494c5c6320..c4e1001d92e 100644 --- a/app/ui-utils/client/lib/isIE11.js +++ b/client/lib/utils/isIE11.ts @@ -1,4 +1,4 @@ -export const isIE11 = () => { +export const isIE11 = (): boolean => { const { userAgent } = window.navigator; const msieIdx = userAgent.indexOf('MSIE'); diff --git a/client/lib/utils/isLayoutEmbedded.ts b/client/lib/utils/isLayoutEmbedded.ts new file mode 100644 index 00000000000..07743a49f72 --- /dev/null +++ b/client/lib/utils/isLayoutEmbedded.ts @@ -0,0 +1,3 @@ +import { FlowRouter } from 'meteor/kadira:flow-router'; + +export const isLayoutEmbedded = (): boolean => FlowRouter.getQueryParam('layout') === 'embedded'; diff --git a/client/lib/utils/isRTL.ts b/client/lib/utils/isRTL.ts new file mode 100644 index 00000000000..57185e2746e --- /dev/null +++ b/client/lib/utils/isRTL.ts @@ -0,0 +1,8 @@ +// http://stackoverflow.com/a/14824756 + +const ltrChars = + 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF'; +const rtlChars = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC'; +const rtlRegExp = new RegExp(`^[^${ltrChars}]*[${rtlChars}]`); + +export const isRTL = (text: string): boolean => rtlRegExp.test(text); diff --git a/app/ui-utils/client/lib/keyCodes.js b/client/lib/utils/keyCodes.ts similarity index 97% rename from app/ui-utils/client/lib/keyCodes.js rename to client/lib/utils/keyCodes.ts index 265559014c1..39abb48d2c5 100644 --- a/app/ui-utils/client/lib/keyCodes.js +++ b/client/lib/utils/keyCodes.ts @@ -33,4 +33,4 @@ export const keyCodes = { F12: 123, NUM_LOCK: 144, SCROLL_LOCK: 145, -}; +} as const; diff --git a/client/lib/fromApi.ts b/client/lib/utils/mapMessageFromApi.ts similarity index 75% rename from client/lib/fromApi.ts rename to client/lib/utils/mapMessageFromApi.ts index 06f1224067d..83a1c494e87 100644 --- a/client/lib/fromApi.ts +++ b/client/lib/utils/mapMessageFromApi.ts @@ -1,5 +1,5 @@ -import { IMessage } from '../../definition/IMessage'; -import { Serialized } from '../../definition/Serialized'; +import { IMessage } from '../../../definition/IMessage'; +import { Serialized } from '../../../definition/Serialized'; export const mapMessageFromApi = ({ attachments = [], diff --git a/client/lib/utils/prependReplies.ts b/client/lib/utils/prependReplies.ts new file mode 100644 index 00000000000..d4dcf72cc63 --- /dev/null +++ b/client/lib/utils/prependReplies.ts @@ -0,0 +1,31 @@ +import { Meteor } from 'meteor/meteor'; + +import { Rooms, Users } from '../../../app/models/client'; +import { MessageAction } from '../../../app/ui-utils/client/lib/MessageAction'; +import { IMessage } from '../../../definition/IMessage'; +import { IRoom } from '../../../definition/IRoom'; + +export const prependReplies = async ( + msg: string, + replies: IMessage[] = [], + mention = false, +): Promise => { + const { username } = Users.findOne({ _id: Meteor.userId() }, { fields: { username: 1 } }); + + const chunks = await Promise.all( + replies.map(async ({ _id, rid, u }) => { + const permalink = await MessageAction.getPermaLink(_id); + const room: IRoom | null = Rooms.findOne(rid, { fields: { t: 1 } }); + + let chunk = `[ ](${permalink})`; + if (room?.t === 'd' && u.username !== username && mention) { + chunk += ` @${u.username}`; + } + + return chunk; + }), + ); + + chunks.push(msg); + return chunks.join(' '); +}; diff --git a/client/lib/utils/renderEmoji.ts b/client/lib/utils/renderEmoji.ts new file mode 100644 index 00000000000..5d19eb7c775 --- /dev/null +++ b/client/lib/utils/renderEmoji.ts @@ -0,0 +1,14 @@ +import { emoji } from '../../../app/emoji/lib/rocketchat'; + +const emojiList = emoji.list as Record; +const emojiPackages = emoji.packages as Record; + +export const renderEmoji = (emojiName: string): string | undefined => { + const emojiPackageName = emojiList[emojiName]?.emojiPackage; + if (emojiPackageName) { + const emojiPackage = emojiPackages[emojiPackageName]; + return emojiPackage.render(emojiName); + } + + return undefined; +}; diff --git a/client/lib/renderMessageBody.ts b/client/lib/utils/renderMessageBody.ts similarity index 78% rename from client/lib/renderMessageBody.ts rename to client/lib/utils/renderMessageBody.ts index 876fcba7aed..59f84fc92d5 100644 --- a/client/lib/renderMessageBody.ts +++ b/client/lib/utils/renderMessageBody.ts @@ -1,7 +1,7 @@ import { escapeHTML } from '@rocket.chat/string-helpers'; -import { callbacks } from '../../app/callbacks/lib/callbacks'; -import { IMessage } from '../../definition/IMessage'; +import { callbacks } from '../../../app/callbacks/lib/callbacks'; +import { IMessage } from '../../../definition/IMessage'; export const renderMessageBody = & { html?: string }>( message: T, diff --git a/client/lib/renderMessageEmoji.ts b/client/lib/utils/renderMessageEmoji.ts similarity index 52% rename from client/lib/renderMessageEmoji.ts rename to client/lib/utils/renderMessageEmoji.ts index d6adc294fff..3201f9114c2 100644 --- a/client/lib/renderMessageEmoji.ts +++ b/client/lib/utils/renderMessageEmoji.ts @@ -1,5 +1,5 @@ -import { emojiParser } from '../../app/emoji/client/emojiParser.js'; -import { IMessage } from '../../definition/IMessage'; +import { emojiParser } from '../../../app/emoji/client/emojiParser.js'; +import { IMessage } from '../../../definition/IMessage'; export const renderMessageEmoji = & { html?: string }>( message: T, diff --git a/client/lib/utils/timeAgo.ts b/client/lib/utils/timeAgo.ts new file mode 100644 index 00000000000..df082f99586 --- /dev/null +++ b/client/lib/utils/timeAgo.ts @@ -0,0 +1,27 @@ +import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; +import moment, { Moment, MomentInput } from 'moment'; + +import { settings } from '../../../app/settings/client'; +import { t } from '../../../app/utils/client'; +import { getUserPreference } from '../../../app/utils/lib/getUserPreference'; + +const dayFormat = ['h:mm A', 'H:mm']; + +export const timeAgo = (date: MomentInput): string => { + const clockMode = Tracker.nonreactive(() => + getUserPreference(Meteor.userId(), 'clockMode', false), + ); + const messageTimeFormat = Tracker.nonreactive(() => settings.get('Message_TimeFormat')); + const sameDay = dayFormat[clockMode - 1] || messageTimeFormat; + + return moment(date).calendar(null, { + lastDay: `[${t('yesterday')}]`, + sameDay, + lastWeek: 'dddd', + sameElse(this: Moment, now) { + const diff = Math.ceil(this.diff(now, 'years', true)); + return diff < 0 ? 'MMM D YYYY' : 'MMM D'; + }, + }); +}; diff --git a/client/lib/utils/waitUntilFind.ts b/client/lib/utils/waitUntilFind.ts new file mode 100644 index 00000000000..67ace08c314 --- /dev/null +++ b/client/lib/utils/waitUntilFind.ts @@ -0,0 +1,15 @@ +import { Tracker } from 'meteor/tracker'; + +export const waitUntilFind = (fn: () => T | undefined): Promise => + new Promise((resolve) => { + Tracker.autorun((c) => { + const result = fn(); + + if (result === undefined) { + return; + } + + c.stop(); + resolve(result); + }); + }); diff --git a/client/methods/deleteMessage.js b/client/methods/deleteMessage.js index dad84129af2..cae28e6b33e 100644 --- a/client/methods/deleteMessage.js +++ b/client/methods/deleteMessage.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { ChatMessage } from '../../app/models/client'; -import { canDeleteMessage } from '../../app/utils/client'; +import { canDeleteMessage } from '../lib/utils/canDeleteMessage'; Meteor.methods({ deleteMessage(msg) { diff --git a/client/providers/ToastMessagesProvider.tsx b/client/providers/ToastMessagesProvider.tsx index 30bccf4cdf8..5813d47c570 100644 --- a/client/providers/ToastMessagesProvider.tsx +++ b/client/providers/ToastMessagesProvider.tsx @@ -1,8 +1,8 @@ import React, { FC } from 'react'; import toastr from 'toastr'; -import { handleError } from '../../app/utils/client'; import { ToastMessagesContext, ToastMessagePayload } from '../contexts/ToastMessagesContext'; +import { handleError } from '../lib/utils/handleError'; const dispatch = ({ type, message, title, options }: ToastMessagePayload): void => { if (type === 'error' && typeof message === 'object') { diff --git a/client/sidebar/header/CreateChannelWithData.js b/client/sidebar/header/CreateChannelWithData.js index 40cf5c2ceb8..4be3119dcec 100644 --- a/client/sidebar/header/CreateChannelWithData.js +++ b/client/sidebar/header/CreateChannelWithData.js @@ -5,7 +5,7 @@ import { usePermission } from '../../contexts/AuthorizationContext'; import { useSetting } from '../../contexts/SettingsContext'; import { useEndpointActionExperimental } from '../../hooks/useEndpointActionExperimental'; import { useForm } from '../../hooks/useForm'; -import { goToRoomById } from '../../lib/goToRoomById'; +import { goToRoomById } from '../../lib/utils/goToRoomById'; import CreateChannel from './CreateChannel'; const CreateChannelWithData = ({ onClose, teamId = '', reload }) => { diff --git a/client/sidebar/header/CreateDirectMessage.tsx b/client/sidebar/header/CreateDirectMessage.tsx index 3835399ac3e..fecc80f43bd 100644 --- a/client/sidebar/header/CreateDirectMessage.tsx +++ b/client/sidebar/header/CreateDirectMessage.tsx @@ -6,7 +6,7 @@ import { IUser } from '../../../definition/IUser'; import UserAutoCompleteMultiple from '../../components/UserAutoCompleteMultiple'; import { useTranslation } from '../../contexts/TranslationContext'; import { useEndpointActionExperimental } from '../../hooks/useEndpointActionExperimental'; -import { goToRoomById } from '../../lib/goToRoomById'; +import { goToRoomById } from '../../lib/utils/goToRoomById'; type Username = IUser['username']; diff --git a/client/sidebar/hooks/useSidebarPaletteColor.js b/client/sidebar/hooks/useSidebarPaletteColor.js index e7535bcd178..e6b1c7cb572 100644 --- a/client/sidebar/hooks/useSidebarPaletteColor.js +++ b/client/sidebar/hooks/useSidebarPaletteColor.js @@ -1,8 +1,8 @@ import colors from '@rocket.chat/fuselage-tokens/colors'; import { useLayoutEffect, useEffect, useMemo } from 'react'; -import { isIE11 } from '../../../app/ui-utils/client/lib/isIE11.js'; import { useSettings } from '../../contexts/SettingsContext'; +import { isIE11 } from '../../lib/utils/isIE11'; const isInternetExplorer11 = isIE11(); diff --git a/client/startup/UserDeleted.ts b/client/startup/UserDeleted.ts new file mode 100644 index 00000000000..eaa1d61b29a --- /dev/null +++ b/client/startup/UserDeleted.ts @@ -0,0 +1,13 @@ +import { Meteor } from 'meteor/meteor'; + +import { ChatMessage } from '../../app/models/client'; +import { Notifications } from '../../app/notifications/client'; +import { IUser } from '../../definition/IUser'; + +Meteor.startup(() => { + Notifications.onLogged('Users:Deleted', ({ userId }: { userId: IUser['_id'] }) => { + ChatMessage.remove({ + 'u._id': userId, + }); + }); +}); diff --git a/client/startup/absoluteUrl.ts b/client/startup/absoluteUrl.ts new file mode 100644 index 00000000000..0092639a5c1 --- /dev/null +++ b/client/startup/absoluteUrl.ts @@ -0,0 +1,5 @@ +import { Meteor } from 'meteor/meteor'; + +import { baseURI } from '../lib/baseURI'; + +Meteor.absoluteUrl.defaultOptions = { ...Meteor.absoluteUrl.defaultOptions, rootUrl: baseURI }; diff --git a/app/lib/client/CustomTranslations.js b/client/startup/customTranslations.ts similarity index 71% rename from app/lib/client/CustomTranslations.js rename to client/startup/customTranslations.ts index 6c128512c4d..fe6a5e0c6e9 100644 --- a/app/lib/client/CustomTranslations.js +++ b/client/startup/customTranslations.ts @@ -1,12 +1,12 @@ import { Meteor } from 'meteor/meteor'; -import { Session } from 'meteor/session'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; +import { Session } from 'meteor/session'; import { Tracker } from 'meteor/tracker'; -import { applyCustomTranslations } from '../../utils'; +import { applyCustomTranslations } from '../lib/utils/applyCustomTranslations'; -Meteor.startup(function() { - Tracker.autorun(function() { +Meteor.startup(() => { + Tracker.autorun(() => { // Re apply translations if tap language was changed Session.get(TAPi18n._loaded_lang_session_key); applyCustomTranslations(); diff --git a/client/startup/e2e.ts b/client/startup/e2e.ts index c4db9afe62c..cabc116b617 100644 --- a/client/startup/e2e.ts +++ b/client/startup/e2e.ts @@ -7,11 +7,11 @@ import { Subscriptions, Rooms } from '../../app/models/client'; import { Notifications } from '../../app/notifications/client'; import { promises } from '../../app/promises/client'; import { settings } from '../../app/settings/client'; -import { Layout } from '../../app/ui-utils/client'; -import { waitUntilFind } from '../../app/utils/client'; import { IMessage } from '../../definition/IMessage'; import { IRoom } from '../../definition/IRoom'; import { ISubscription } from '../../definition/ISubscription'; +import { isLayoutEmbedded } from '../lib/utils/isLayoutEmbedded'; +import { waitUntilFind } from '../lib/utils/waitUntilFind'; const handle = async (roomId: IRoom['_id'], keyId: string): Promise => { const e2eRoom = await e2e.getInstanceByRoomId(roomId); @@ -28,7 +28,7 @@ Meteor.startup(() => { return; } - const adminEmbedded = Layout.isEmbedded() && FlowRouter.current().path.startsWith('/admin'); + const adminEmbedded = isLayoutEmbedded() && FlowRouter.current().path.startsWith('/admin'); if (!adminEmbedded && settings.get('E2E_Enable') && window.crypto) { e2e.startClient(); diff --git a/client/startup/index.ts b/client/startup/index.ts index 5871e792aed..45b84b684b9 100644 --- a/client/startup/index.ts +++ b/client/startup/index.ts @@ -1,11 +1,12 @@ +import './absoluteUrl'; import './appRoot'; import './banners'; import './contextualBar'; +import './customTranslations'; import './e2e'; import './emailVerification'; import './i18n'; import './listenActiveUsers'; -import './routes'; import './loginViaQuery'; import './messageTypes'; import './notifications'; @@ -14,11 +15,16 @@ import './readReceipt'; import './renderMessage'; import './renderNotification'; import './roomObserve'; +import './rootUrlChange'; +import './routes'; import './setupWizard'; +import './slashCommands'; import './startup'; import './streamMessage'; import './theme'; import './unread'; +import './UserDeleted'; +import './userRoles'; import './userSetUtcOffset'; import './usersObserve'; import './userStatusManuallySet'; diff --git a/client/startup/notifications/konchatNotifications.ts b/client/startup/notifications/konchatNotifications.ts index f1696df9252..b20b2ebf7be 100644 --- a/client/startup/notifications/konchatNotifications.ts +++ b/client/startup/notifications/konchatNotifications.ts @@ -5,12 +5,14 @@ import { Tracker } from 'meteor/tracker'; import { CachedChatSubscription } from '../../../app/models/client'; import { Notifications } from '../../../app/notifications/client'; -import { fireGlobalEvent, readMessage, Layout } from '../../../app/ui-utils/client'; +import { readMessage } from '../../../app/ui-utils/client'; import { KonchatNotification } from '../../../app/ui/client'; import { getUserPreference } from '../../../app/utils/client'; import { IMessage } from '../../../definition/IMessage'; import { IRoom } from '../../../definition/IRoom'; import { ISubscription } from '../../../definition/ISubscription'; +import { fireGlobalEvent } from '../../lib/utils/fireGlobalEvent'; +import { isLayoutEmbedded } from '../../lib/utils/isLayoutEmbedded'; const notifyNewRoom = (sub: ISubscription): void => { if (Session.equals(`user_${Meteor.userId()}_status`, 'busy')) { @@ -52,7 +54,7 @@ function notifyNewMessageAudio(rid: string): void { const messageIsInOpenedRoom = openedRoomId === rid; const muteFocusedConversations = getUserPreference(Meteor.userId(), 'muteFocusedConversations'); - if (Layout.isEmbedded()) { + if (isLayoutEmbedded()) { if (!hasFocus && messageIsInOpenedRoom) { // Play a notification sound KonchatNotification.newMessage(rid); @@ -85,7 +87,7 @@ Meteor.startup(() => { hasFocus, }); - if (Layout.isEmbedded()) { + if (isLayoutEmbedded()) { if (!hasFocus && messageIsInOpenedRoom) { // Show a notification. KonchatNotification.showDesktop(notification); diff --git a/app/lib/client/lib/settings.js b/client/startup/rootUrlChange.ts similarity index 52% rename from app/lib/client/lib/settings.js rename to client/startup/rootUrlChange.ts index 5d909e0a2d3..33d04e5c033 100644 --- a/app/lib/client/lib/settings.js +++ b/client/startup/rootUrlChange.ts @@ -2,16 +2,16 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import toastr from 'toastr'; -import { t } from '../../../utils'; -import { settings } from '../../../settings'; -import { hasRole } from '../../../authorization'; -import { Roles } from '../../../models/client'; -import { imperativeModal } from '../../../../client/lib/imperativeModal'; -import UrlChangeModal from '../../../../client/components/UrlChangeModal'; -import { isSyncReady } from '../../../../client/lib/userData'; - -Meteor.startup(function() { - Tracker.autorun(function(c) { +import { hasRole } from '../../app/authorization/client'; +import { Roles } from '../../app/models/client'; +import { settings } from '../../app/settings/client'; +import { t } from '../../app/utils/client'; +import UrlChangeModal from '../components/UrlChangeModal'; +import { imperativeModal } from '../lib/imperativeModal'; +import { isSyncReady } from '../lib/userData'; + +Meteor.startup(() => { + Tracker.autorun((c) => { if (!Meteor.userId()) { return; } @@ -29,11 +29,11 @@ Meteor.startup(function() { return; } - const currentUrl = location.origin + __meteor_runtime_config__.ROOT_URL_PATH_PREFIX; - if (__meteor_runtime_config__.ROOT_URL.replace(/\/$/, '') !== currentUrl) { - const confirm = () => { + const currentUrl = location.origin + window.__meteor_runtime_config__.ROOT_URL_PATH_PREFIX; + if (window.__meteor_runtime_config__.ROOT_URL.replace(/\/$/, '') !== currentUrl) { + const confirm = (): void => { imperativeModal.close(); - Meteor.call('saveSetting', 'Site_Url', currentUrl, function() { + Meteor.call('saveSetting', 'Site_Url', currentUrl, () => { toastr.success(t('Saved')); }); }; diff --git a/client/startup/routes.ts b/client/startup/routes.ts index cd29aaeb8ab..de691f81dd9 100644 --- a/client/startup/routes.ts +++ b/client/startup/routes.ts @@ -6,10 +6,10 @@ import { lazy } from 'react'; import toastr from 'toastr'; import { KonchatNotification } from '../../app/ui/client'; -import { handleError } from '../../app/utils/client'; import { IUser } from '../../definition/IUser'; import { appLayout } from '../lib/appLayout'; import { createTemplateForComponent } from '../lib/portals/createTemplateForComponent'; +import { handleError } from '../lib/utils/handleError'; const SetupWizardRoute = lazy(() => import('../views/setupWizard/SetupWizardRoute')); const MailerUnsubscriptionPage = lazy(() => import('../views/mailer/MailerUnsubscriptionPage')); diff --git a/client/startup/slashCommands.ts b/client/startup/slashCommands.ts new file mode 100644 index 00000000000..982cb04468e --- /dev/null +++ b/client/startup/slashCommands.ts @@ -0,0 +1,20 @@ +import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; + +import { slashCommands, APIClient } from '../../app/utils/client'; +import { IUser } from '../../definition/IUser'; + +let oldUserId: IUser['_id'] | null = null; + +Tracker.autorun(() => { + const newUserId = Meteor.userId(); + if (oldUserId === null && newUserId) { + APIClient.v1.get('commands.list').then((result) => { + result.commands.forEach((command: { command: string }) => { + slashCommands.commands[command.command] = command; + }); + }); + } + + oldUserId = Meteor.userId(); +}); diff --git a/client/startup/startup.ts b/client/startup/startup.ts index 2cc59de0543..365474d81a7 100644 --- a/client/startup/startup.ts +++ b/client/startup/startup.ts @@ -8,12 +8,12 @@ import toastr from 'toastr'; import { hasPermission } from '../../app/authorization/client'; import hljs from '../../app/markdown/lib/hljs'; -import { fireGlobalEvent } from '../../app/ui-utils/client'; import { getUserPreference, t } from '../../app/utils/client'; import 'highlight.js/styles/github.css'; import { UserStatus } from '../../definition/UserStatus'; import * as banners from '../lib/banners'; import { synchronizeUserData } from '../lib/userData'; +import { fireGlobalEvent } from '../lib/utils/fireGlobalEvent'; hljs.initHighlightingOnLoad(); diff --git a/client/startup/unread.ts b/client/startup/unread.ts index 39760d8e7bc..14fc66bf096 100644 --- a/client/startup/unread.ts +++ b/client/startup/unread.ts @@ -5,9 +5,10 @@ import { Tracker } from 'meteor/tracker'; import { Favico } from '../../app/favico/client'; import { ChatSubscription, ChatRoom } from '../../app/models/client'; import { settings } from '../../app/settings/client'; -import { menu, fireGlobalEvent } from '../../app/ui-utils/client'; +import { menu } from '../../app/ui-utils/client'; import { getUserPreference } from '../../app/utils/client'; import { ISubscription } from '../../definition/ISubscription'; +import { fireGlobalEvent } from '../lib/utils/fireGlobalEvent'; const fetchSubscriptions = (): ISubscription[] => ChatSubscription.find( diff --git a/client/startup/userRoles.ts b/client/startup/userRoles.ts new file mode 100644 index 00000000000..c76512de309 --- /dev/null +++ b/client/startup/userRoles.ts @@ -0,0 +1,77 @@ +import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; + +import { UserRoles, RoomRoles, ChatMessage } from '../../app/models/client'; +import { Notifications } from '../../app/notifications/client'; +import { IRocketChatRecord } from '../../definition/IRocketChatRecord'; +import { IRole, IUser } from '../../definition/IUser'; +import { handleError } from '../lib/utils/handleError'; + +Meteor.startup(() => { + Tracker.autorun(() => { + if (Meteor.userId()) { + Meteor.call('getUserRoles', (error: Error, results: IRocketChatRecord[]) => { + if (error) { + return handleError(error); + } + + for (const record of results) { + UserRoles.upsert({ _id: record._id }, record); + } + }); + + Notifications.onLogged( + 'roles-change', + (role: { + type: 'added' | 'removed' | 'changed'; + _id: IRole['_id']; + u: Partial; + scope: IRole['scope']; + }) => { + if (role.type === 'added') { + if (role.scope) { + RoomRoles.upsert( + { 'rid': role.scope, 'u._id': role.u._id }, + { $setOnInsert: { u: role.u }, $addToSet: { roles: role._id } }, + ); + } else { + UserRoles.upsert( + { _id: role.u._id }, + { $addToSet: { roles: role._id }, $set: { username: role.u.username } }, + ); + ChatMessage.update( + { 'u._id': role.u._id }, + { $addToSet: { roles: role._id } }, + { multi: true }, + ); + } + + return; + } + + if (role.type === 'removed') { + if (role.scope) { + RoomRoles.update( + { 'rid': role.scope, 'u._id': role.u._id }, + { $pull: { roles: role._id } }, + ); + } else { + UserRoles.update({ _id: role.u._id }, { $pull: { roles: role._id } }); + ChatMessage.update( + { 'u._id': role.u._id }, + { $pull: { roles: role._id } }, + { multi: true }, + ); + } + + return; + } + + if (role.type === 'changed') { + ChatMessage.update({ roles: role._id }, { $inc: { rerender: 1 } }, { multi: true }); + } + }, + ); + } + }); +}); diff --git a/client/startup/userStatusManuallySet.ts b/client/startup/userStatusManuallySet.ts index 718833cd6f8..46c052c8839 100644 --- a/client/startup/userStatusManuallySet.ts +++ b/client/startup/userStatusManuallySet.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../app/callbacks/client'; -import { fireGlobalEvent } from '../../app/ui-utils/client'; import { UserStatus } from '../../definition/UserStatus'; +import { fireGlobalEvent } from '../lib/utils/fireGlobalEvent'; /* fire user state change globally, to listen on desktop electron client */ Meteor.startup(() => { diff --git a/client/templateHelpers/avatarUrlFromUsername.ts b/client/templateHelpers/avatarUrlFromUsername.ts new file mode 100644 index 00000000000..bda7294c1d7 --- /dev/null +++ b/client/templateHelpers/avatarUrlFromUsername.ts @@ -0,0 +1,5 @@ +import { Template } from 'meteor/templating'; + +import { getUserAvatarURL } from '../../app/utils/lib/getUserAvatarURL'; + +Template.registerHelper('avatarUrlFromUsername', getUserAvatarURL); diff --git a/client/templateHelpers/emojiUrlFromName.ts b/client/templateHelpers/emojiUrlFromName.ts new file mode 100644 index 00000000000..f77fb8734b5 --- /dev/null +++ b/client/templateHelpers/emojiUrlFromName.ts @@ -0,0 +1,5 @@ +import { Template } from 'meteor/templating'; + +import { getEmojiUrlFromName } from '../../app/emoji-custom/client/lib/emojiCustom'; + +Template.registerHelper('emojiUrlFromName', getEmojiUrlFromName); diff --git a/client/templateHelpers/hasPermission.ts b/client/templateHelpers/hasPermission.ts new file mode 100644 index 00000000000..bc2077c2e3b --- /dev/null +++ b/client/templateHelpers/hasPermission.ts @@ -0,0 +1,5 @@ +import { Template } from 'meteor/templating'; + +import { hasAtLeastOnePermission } from '../../app/authorization/client'; + +Template.registerHelper('hasPermission', hasAtLeastOnePermission); diff --git a/client/templateHelpers/index.ts b/client/templateHelpers/index.ts index a5dd85a5da0..ea3a9e76ac7 100644 --- a/client/templateHelpers/index.ts +++ b/client/templateHelpers/index.ts @@ -1 +1,7 @@ import './log'; +import './renderEmoji'; +import './avatarUrlFromUsername'; +import './emojiUrlFromName'; +import './nrrargs'; +import './hasPermission'; +import './userHasAllPermission'; diff --git a/client/templateHelpers/nrrargs.ts b/client/templateHelpers/nrrargs.ts new file mode 100644 index 00000000000..ee40fa69598 --- /dev/null +++ b/client/templateHelpers/nrrargs.ts @@ -0,0 +1,5 @@ +import { Template } from 'meteor/templating'; + +Template.registerHelper('nrrargs', (...args: T) => ({ + _arguments: args, +})); diff --git a/client/templateHelpers/renderEmoji.ts b/client/templateHelpers/renderEmoji.ts new file mode 100644 index 00000000000..671edbd1350 --- /dev/null +++ b/client/templateHelpers/renderEmoji.ts @@ -0,0 +1,5 @@ +import { Template } from 'meteor/templating'; + +import { renderEmoji } from '../lib/utils/renderEmoji'; + +Template.registerHelper('renderEmoji', renderEmoji); diff --git a/client/templateHelpers/userHasAllPermission.ts b/client/templateHelpers/userHasAllPermission.ts new file mode 100644 index 00000000000..16669dfae3c --- /dev/null +++ b/client/templateHelpers/userHasAllPermission.ts @@ -0,0 +1,14 @@ +import { Template } from 'meteor/templating'; + +import { userHasAllPermission } from '../../app/authorization/client'; +import { IPermission } from '../../definition/IPermission'; +import { IRole, IUser } from '../../definition/IUser'; + +Template.registerHelper( + 'userHasAllPermission', + ( + userId: IUser['_id'] | null, + permission: IPermission['_id'] | IPermission['_id'][], + scope: IRole['scope'], + ) => userHasAllPermission(permission, scope, userId), +); diff --git a/client/types/global.d.ts b/client/types/global.d.ts index bb86b5513c6..9db2a7370da 100644 --- a/client/types/global.d.ts +++ b/client/types/global.d.ts @@ -14,6 +14,10 @@ interface Window { favico?: any; USE_REST_FOR_DDP_CALLS?: boolean; ECDH_Enabled?: boolean; + __meteor_runtime_config__: { + ROOT_URL_PATH_PREFIX: string; + ROOT_URL: string; + }; } interface PromiseConstructor { diff --git a/client/types/meteor-htmljs.d.ts b/client/types/meteor-htmljs.d.ts index c276e68e940..8e842977074 100644 --- a/client/types/meteor-htmljs.d.ts +++ b/client/types/meteor-htmljs.d.ts @@ -1,6 +1,7 @@ declare module 'meteor/htmljs' { namespace HTML { function Comment(value: string): unknown; + function Raw(value: string): unknown; function DIV(attributes?: Record): unknown; } } diff --git a/client/views/account/AccountSidebar.js b/client/views/account/AccountSidebar.js index 9c1ea5df4a1..03e5d8709fb 100644 --- a/client/views/account/AccountSidebar.js +++ b/client/views/account/AccountSidebar.js @@ -1,10 +1,11 @@ import React, { memo, useCallback, useEffect } from 'react'; import { useSubscription } from 'use-subscription'; -import { menu, SideNav, Layout } from '../../../app/ui-utils/client'; +import { menu, SideNav } from '../../../app/ui-utils/client'; import Sidebar from '../../components/Sidebar'; import { useRoutePath, useCurrentRoute } from '../../contexts/RouterContext'; import { useTranslation } from '../../contexts/TranslationContext'; +import { isLayoutEmbedded } from '../../lib/utils/isLayoutEmbedded'; import SettingsProvider from '../../providers/SettingsProvider'; import { itemsSubscription } from './sidebarItems'; @@ -14,7 +15,7 @@ const AccountSidebar = () => { const items = useSubscription(itemsSubscription); const closeFlex = useCallback(() => { - if (Layout.isEmbedded()) { + if (isLayoutEmbedded()) { menu.close(); return; } diff --git a/client/views/admin/sidebar/AdminSidebar.js b/client/views/admin/sidebar/AdminSidebar.js index 1849fb60edb..952a3bd6803 100644 --- a/client/views/admin/sidebar/AdminSidebar.js +++ b/client/views/admin/sidebar/AdminSidebar.js @@ -1,11 +1,12 @@ import React, { useCallback, useMemo, useEffect, memo } from 'react'; -import { menu, SideNav, Layout } from '../../../../app/ui-utils/client'; +import { menu, SideNav } from '../../../../app/ui-utils/client'; import PlanTag from '../../../components/PlanTag'; import Sidebar from '../../../components/Sidebar'; import { useAtLeastOnePermission } from '../../../contexts/AuthorizationContext'; import { useRoutePath, useCurrentRoute } from '../../../contexts/RouterContext'; import { useTranslation } from '../../../contexts/TranslationContext'; +import { isLayoutEmbedded } from '../../../lib/utils/isLayoutEmbedded'; import SettingsProvider from '../../../providers/SettingsProvider'; import AdminSidebarPages from './AdminSidebarPages'; import AdminSidebarSettings from './AdminSidebarSettings'; @@ -21,7 +22,7 @@ function AdminSidebar() { ); const closeAdminFlex = useCallback(() => { - if (Layout.isEmbedded()) { + if (isLayoutEmbedded()) { menu.close(); return; } diff --git a/client/views/admin/users/UserInfo.js b/client/views/admin/users/UserInfo.js index 63d94b1b4ce..6a526577ac6 100644 --- a/client/views/admin/users/UserInfo.js +++ b/client/views/admin/users/UserInfo.js @@ -10,7 +10,7 @@ import { useSetting } from '../../../contexts/SettingsContext'; import { useTranslation } from '../../../contexts/TranslationContext'; import { AsyncStatePhase } from '../../../hooks/useAsyncState'; import { useEndpointData } from '../../../hooks/useEndpointData'; -import { getUserEmailVerified } from '../../../lib/getUserEmailVerified'; +import { getUserEmailVerified } from '../../../lib/utils/getUserEmailVerified'; import UserInfo from '../../room/contextualBar/UserInfo/UserInfo'; import { UserInfoActions } from './UserInfoActions'; diff --git a/client/views/blocks/MessageBlock.js b/client/views/blocks/MessageBlock.js index 46279bd5de5..bfc980ae9b0 100644 --- a/client/views/blocks/MessageBlock.js +++ b/client/views/blocks/MessageBlock.js @@ -9,7 +9,7 @@ import React from 'react'; import * as ActionManager from '../../../app/ui-message/client/ActionManager'; import { useBlockRendered } from '../../components/Message/hooks/useBlockRendered'; -import { renderMessageBody } from '../../lib/renderMessageBody'; +import { renderMessageBody } from '../../lib/utils/renderMessageBody'; import './textParsers'; // TODO: move this to fuselage-ui-kit itself diff --git a/client/views/blocks/textParsers.js b/client/views/blocks/textParsers.js index 5e66e183ab4..1e1b0fbd581 100644 --- a/client/views/blocks/textParsers.js +++ b/client/views/blocks/textParsers.js @@ -3,7 +3,7 @@ import { messageParser, modalParser } from '@rocket.chat/fuselage-ui-kit'; import React from 'react'; -import { renderMessageBody } from '../../lib/renderMessageBody'; +import { renderMessageBody } from '../../lib/utils/renderMessageBody'; // TODO: move this to fuselage-ui-kit itself messageParser.text = ({ text, type } = {}) => { diff --git a/client/views/hooks/useMembersList.ts b/client/views/hooks/useMembersList.ts index a94948562a9..f223e26eddf 100644 --- a/client/views/hooks/useMembersList.ts +++ b/client/views/hooks/useMembersList.ts @@ -1,11 +1,11 @@ import { useCallback, useMemo, useState } from 'react'; -import { getConfig } from '../../../app/ui-utils/client/config'; import { IUser } from '../../../definition/IUser'; import { useEndpoint } from '../../contexts/ServerContext'; import { useScrollableRecordList } from '../../hooks/lists/useScrollableRecordList'; import { useComponentDidUpdate } from '../../hooks/useComponentDidUpdate'; import { RecordList } from '../../lib/lists/RecordList'; +import { getConfig } from '../../lib/utils/getConfig'; type MembersListOptions = { rid: string; diff --git a/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js b/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js index 868ecbd1606..0351dc7deb9 100644 --- a/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js +++ b/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js @@ -5,7 +5,7 @@ import React, { useRef, useState, useMemo, useEffect } from 'react'; import AutoCompleteDepartment from '../../../components/AutoCompleteDepartment'; import Page from '../../../components/Page'; import { useTranslation } from '../../../contexts/TranslationContext'; -import { getDateRange } from '../../../lib/getDateRange'; +import { getDateRange } from '../../../lib/utils/getDateRange'; import Label from '../components/Label'; import AgentStatusChart from './charts/AgentStatusChart'; import ChatDurationChart from './charts/ChatDurationChart'; diff --git a/client/views/omnichannel/sidebar/OmnichannelSidebar.tsx b/client/views/omnichannel/sidebar/OmnichannelSidebar.tsx index 4a557320bbc..cc248ab749e 100644 --- a/client/views/omnichannel/sidebar/OmnichannelSidebar.tsx +++ b/client/views/omnichannel/sidebar/OmnichannelSidebar.tsx @@ -1,10 +1,11 @@ import React, { useCallback, useEffect, FC, memo } from 'react'; import { useSubscription } from 'use-subscription'; -import { menu, SideNav, Layout } from '../../../../app/ui-utils/client'; +import { menu, SideNav } from '../../../../app/ui-utils/client'; import Sidebar from '../../../components/Sidebar'; import { useRoutePath, useCurrentRoute } from '../../../contexts/RouterContext'; import { useTranslation } from '../../../contexts/TranslationContext'; +import { isLayoutEmbedded } from '../../../lib/utils/isLayoutEmbedded'; import SettingsProvider from '../../../providers/SettingsProvider'; import { itemsSubscription } from '../sidebarItems'; @@ -13,7 +14,7 @@ const OmnichannelSidebar: FC = () => { const t = useTranslation(); const closeOmnichannelFlex = useCallback(() => { - if (Layout.isEmbedded()) { + if (isLayoutEmbedded()) { menu.close(); return; } diff --git a/client/views/room/Header/Omnichannel/QuickActions/hooks/useQuickActions.tsx b/client/views/room/Header/Omnichannel/QuickActions/hooks/useQuickActions.tsx index d4e4b974f0f..cc2a4a7e12c 100644 --- a/client/views/room/Header/Omnichannel/QuickActions/hooks/useQuickActions.tsx +++ b/client/views/room/Header/Omnichannel/QuickActions/hooks/useQuickActions.tsx @@ -5,7 +5,6 @@ import React, { useCallback, useState, useEffect } from 'react'; import toastr from 'toastr'; import { RoomManager } from '../../../../../../../app/ui-utils/client'; -import { handleError } from '../../../../../../../app/utils/client'; import { IOmnichannelRoom } from '../../../../../../../definition/IRoom'; import PlaceChatOnHoldModal from '../../../../../../../ee/app/livechat-enterprise/client/components/modals/PlaceChatOnHoldModal'; import CloseChatModal from '../../../../../../components/Omnichannel/modals/CloseChatModal'; @@ -20,6 +19,7 @@ import { useEndpoint, useMethod } from '../../../../../../contexts/ServerContext import { useSetting } from '../../../../../../contexts/SettingsContext'; import { useTranslation } from '../../../../../../contexts/TranslationContext'; import { useUserId } from '../../../../../../contexts/UserContext'; +import { handleError } from '../../../../../../lib/utils/handleError'; import { QuickActionsActionConfig, QuickActionsEnum } from '../../../../lib/QuickActions'; import { useQuickActionsContext } from '../../../../lib/QuickActions/QuickActionsContext'; diff --git a/client/views/room/contextualBar/Discussions/DiscussionList.js b/client/views/room/contextualBar/Discussions/DiscussionList.js index cdc5a315108..dfbbc5511dc 100644 --- a/client/views/room/contextualBar/Discussions/DiscussionList.js +++ b/client/views/room/contextualBar/Discussions/DiscussionList.js @@ -7,7 +7,7 @@ import ScrollableContentWrapper from '../../../../components/ScrollableContentWr import VerticalBar from '../../../../components/VerticalBar'; import { useSetting } from '../../../../contexts/SettingsContext'; import { useTranslation } from '../../../../contexts/TranslationContext'; -import { goToRoomById } from '../../../../lib/goToRoomById'; +import { goToRoomById } from '../../../../lib/utils/goToRoomById'; import Row from './Row'; import { withData } from './withData'; diff --git a/client/views/room/contextualBar/Discussions/normalizeThreadMessage.js b/client/views/room/contextualBar/Discussions/normalizeThreadMessage.js index 11835d0e7f2..7fd1a2fdd0c 100644 --- a/client/views/room/contextualBar/Discussions/normalizeThreadMessage.js +++ b/client/views/room/contextualBar/Discussions/normalizeThreadMessage.js @@ -1,6 +1,6 @@ import { escapeHTML } from '@rocket.chat/string-helpers'; -import { renderMessageBody } from '../../../../lib/renderMessageBody'; +import { renderMessageBody } from '../../../../lib/utils/renderMessageBody'; export const normalizeThreadMessage = ({ ...message }) => { if (message.msg) { diff --git a/client/views/room/contextualBar/Discussions/useDiscussionsList.ts b/client/views/room/contextualBar/Discussions/useDiscussionsList.ts index 593e35e9a4d..f4352aeeebe 100644 --- a/client/views/room/contextualBar/Discussions/useDiscussionsList.ts +++ b/client/views/room/contextualBar/Discussions/useDiscussionsList.ts @@ -1,11 +1,11 @@ import { useCallback, useMemo } from 'react'; -import { getConfig } from '../../../../../app/ui-utils/client/config'; import { IUser } from '../../../../../definition/IUser'; import { useEndpoint } from '../../../../contexts/ServerContext'; import { useScrollableMessageList } from '../../../../hooks/lists/useScrollableMessageList'; import { useStreamUpdatesForMessageList } from '../../../../hooks/lists/useStreamUpdatesForMessageList'; import { DiscussionsList, DiscussionsListOptions } from '../../../../lib/lists/DiscussionsList'; +import { getConfig } from '../../../../lib/utils/getConfig'; export const useDiscussionsList = ( options: DiscussionsListOptions, diff --git a/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts b/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts index e524b1d826e..8a144c64dbf 100644 --- a/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts +++ b/client/views/room/contextualBar/RoomFiles/hooks/useFilesList.ts @@ -1,12 +1,12 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; -import { getConfig } from '../../../../../../app/ui-utils/client/config'; import { useEndpoint } from '../../../../../contexts/ServerContext'; import { useUserRoom, useUserId } from '../../../../../contexts/UserContext'; import { useScrollableMessageList } from '../../../../../hooks/lists/useScrollableMessageList'; import { useStreamUpdatesForMessageList } from '../../../../../hooks/lists/useStreamUpdatesForMessageList'; import { useComponentDidUpdate } from '../../../../../hooks/useComponentDidUpdate'; import { FilesList, FilesListOptions } from '../../../../../lib/lists/FilesList'; +import { getConfig } from '../../../../../lib/utils/getConfig'; export const useFilesList = ( options: FilesListOptions, diff --git a/client/views/room/contextualBar/Threads/Row.tsx b/client/views/room/contextualBar/Threads/Row.tsx index 3b23cb85a38..65bf025761f 100644 --- a/client/views/room/contextualBar/Threads/Row.tsx +++ b/client/views/room/contextualBar/Threads/Row.tsx @@ -1,10 +1,10 @@ import React, { FC, memo, MouseEvent } from 'react'; -import { call } from '../../../../../app/ui-utils/client'; import { IMessage } from '../../../../../definition/IMessage'; import { useTranslation } from '../../../../contexts/TranslationContext'; import { useTimeAgo } from '../../../../hooks/useTimeAgo'; import { clickableItem } from '../../../../lib/clickableItem'; +import { callWithErrorHandling } from '../../../../lib/utils/callWithErrorHandling'; import ThreadListMessage from './components/Message'; import { mapProps } from './mapProps'; import { normalizeThreadMessage } from './normalizeThreadMessage'; @@ -17,9 +17,12 @@ const handleFollowButton = (e: MouseEvent, threadId: string): void const { following } = e.currentTarget.dataset; following && - call(![true, 'true'].includes(following) ? 'followMessage' : 'unfollowMessage', { - mid: threadId, - }); + callWithErrorHandling( + ![true, 'true'].includes(following) ? 'followMessage' : 'unfollowMessage', + { + mid: threadId, + }, + ); }; type ThreadRowProps = { diff --git a/client/views/room/contextualBar/Threads/normalizeThreadMessage.js b/client/views/room/contextualBar/Threads/normalizeThreadMessage.js index 11835d0e7f2..7fd1a2fdd0c 100644 --- a/client/views/room/contextualBar/Threads/normalizeThreadMessage.js +++ b/client/views/room/contextualBar/Threads/normalizeThreadMessage.js @@ -1,6 +1,6 @@ import { escapeHTML } from '@rocket.chat/string-helpers'; -import { renderMessageBody } from '../../../../lib/renderMessageBody'; +import { renderMessageBody } from '../../../../lib/utils/renderMessageBody'; export const normalizeThreadMessage = ({ ...message }) => { if (message.msg) { diff --git a/client/views/room/contextualBar/Threads/useThreadsList.ts b/client/views/room/contextualBar/Threads/useThreadsList.ts index 47308a4b8fb..3558e06de57 100644 --- a/client/views/room/contextualBar/Threads/useThreadsList.ts +++ b/client/views/room/contextualBar/Threads/useThreadsList.ts @@ -1,11 +1,11 @@ import { useCallback, useMemo } from 'react'; -import { getConfig } from '../../../../../app/ui-utils/client/config'; import { IUser } from '../../../../../definition/IUser'; import { useEndpoint } from '../../../../contexts/ServerContext'; import { useScrollableMessageList } from '../../../../hooks/lists/useScrollableMessageList'; import { useStreamUpdatesForMessageList } from '../../../../hooks/lists/useStreamUpdatesForMessageList'; import { ThreadsList, ThreadsListOptions } from '../../../../lib/lists/ThreadsList'; +import { getConfig } from '../../../../lib/utils/getConfig'; export const useThreadsList = ( options: ThreadsListOptions, diff --git a/client/views/room/contextualBar/UserInfo/UserInfoWithData.js b/client/views/room/contextualBar/UserInfo/UserInfoWithData.js index 50aff8312bb..4251ac5a544 100644 --- a/client/views/room/contextualBar/UserInfo/UserInfoWithData.js +++ b/client/views/room/contextualBar/UserInfo/UserInfoWithData.js @@ -11,7 +11,7 @@ import { useSetting } from '../../../../contexts/SettingsContext'; import { useTranslation } from '../../../../contexts/TranslationContext'; import { AsyncStatePhase } from '../../../../hooks/useAsyncState'; import { useEndpointData } from '../../../../hooks/useEndpointData'; -import { getUserEmailVerified } from '../../../../lib/getUserEmailVerified'; +import { getUserEmailVerified } from '../../../../lib/utils/getUserEmailVerified'; import UserInfo from './UserInfo'; import UserActions from './actions/UserActions'; diff --git a/client/views/teams/CreateTeamModal/CreateTeamModal.tsx b/client/views/teams/CreateTeamModal/CreateTeamModal.tsx index 5391299bd11..b3b0a85acd1 100644 --- a/client/views/teams/CreateTeamModal/CreateTeamModal.tsx +++ b/client/views/teams/CreateTeamModal/CreateTeamModal.tsx @@ -21,7 +21,7 @@ import { useSetting } from '../../../contexts/SettingsContext'; import { useTranslation } from '../../../contexts/TranslationContext'; import { useEndpointActionExperimental } from '../../../hooks/useEndpointActionExperimental'; import { useForm } from '../../../hooks/useForm'; -import { goToRoomById } from '../../../lib/goToRoomById'; +import { goToRoomById } from '../../../lib/utils/goToRoomById'; import TeamNameInput from './TeamNameInput'; import UsersInput from './UsersInput'; diff --git a/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts b/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts index bb8f8c37d39..6f144e0400a 100644 --- a/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts +++ b/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts @@ -1,12 +1,12 @@ import { useCallback, useMemo, useState } from 'react'; -import { getConfig } from '../../../../../../app/ui-utils/client/config'; import { IRoom } from '../../../../../../definition/IRoom'; import { useEndpoint } from '../../../../../contexts/ServerContext'; import { useScrollableRecordList } from '../../../../../hooks/lists/useScrollableRecordList'; import { useComponentDidUpdate } from '../../../../../hooks/useComponentDidUpdate'; -import { mapMessageFromApi } from '../../../../../lib/fromApi'; import { RecordList } from '../../../../../lib/lists/RecordList'; +import { getConfig } from '../../../../../lib/utils/getConfig'; +import { mapMessageFromApi } from '../../../../../lib/utils/mapMessageFromApi'; type TeamsChannelListOptions = { teamId: string; diff --git a/definition/IRole.ts b/definition/IRole.ts index 04af0654b28..ba1f0d4d5ed 100644 --- a/definition/IRole.ts +++ b/definition/IRole.ts @@ -1,5 +1 @@ -export interface IRole { - _id: string; - name: string; - _updatedAt?: Date; -} +export { IRole } from './IUser'; diff --git a/ee/app/auditing/client/utils.js b/ee/app/auditing/client/utils.js index ad87bee789b..90ed222b0da 100644 --- a/ee/app/auditing/client/utils.js +++ b/ee/app/auditing/client/utils.js @@ -1,4 +1,4 @@ -export { call } from '../../../../app/ui-utils/client/lib/callMethod'; +export { callWithErrorHandling as call } from '../../../../client/lib/utils/callWithErrorHandling'; export const convertDate = (date) => { const [y, m, d] = date.split('-'); diff --git a/ee/app/canned-responses/client/views/tabbar/cannedResponses.js b/ee/app/canned-responses/client/views/tabbar/cannedResponses.js index 8cb6eef3698..245d9654469 100644 --- a/ee/app/canned-responses/client/views/tabbar/cannedResponses.js +++ b/ee/app/canned-responses/client/views/tabbar/cannedResponses.js @@ -4,8 +4,9 @@ import { Template } from 'meteor/templating'; import toastr from 'toastr'; import { CannedResponse } from '../../collections/CannedResponse'; -import { t, handleError } from '../../../../../../app/utils'; +import { t } from '../../../../../../app/utils'; import { chatMessages } from '../../../../../../app/ui/client'; +import { handleError } from '../../../../../../client/lib/utils/handleError'; import './cannedResponses.html'; diff --git a/ee/app/license/client/index.ts b/ee/app/license/client/index.ts index c243ad6c93b..cb6aff1426b 100644 --- a/ee/app/license/client/index.ts +++ b/ee/app/license/client/index.ts @@ -1,10 +1,10 @@ import { CachedCollectionManager } from '../../../../app/ui-cached-collection'; -import { callMethod } from '../../../../app/ui-utils/client/lib/callMethod'; +import { call } from '../../../../client/lib/utils/call'; const allModules = new Promise>((resolve, reject) => { CachedCollectionManager.onLogin(async () => { try { - const features: string[] = await callMethod('license:getModules'); + const features: string[] = await call('license:getModules'); resolve(new Set(features)); } catch (e) { console.error('Error getting modules', e); diff --git a/ee/client/.eslintrc.js b/ee/client/.eslintrc.js index d5bb431b989..4d6bf74449b 100644 --- a/ee/client/.eslintrc.js +++ b/ee/client/.eslintrc.js @@ -16,7 +16,10 @@ module.exports = { }, ], 'jsx-quotes': ['error', 'prefer-single'], - 'new-cap': ['error', { capIsNewExceptions: ['HTML.Comment', 'HTML.DIV', 'SHA256'] }], + 'new-cap': [ + 'error', + { capIsNewExceptions: ['HTML.Comment', 'HTML.Raw', 'HTML.DIV', 'SHA256'] }, + ], 'prefer-arrow-callback': ['error', { allowNamedFunctions: true }], 'prettier/prettier': 2, 'react/display-name': 'error', @@ -83,7 +86,10 @@ module.exports = { }, ], 'jsx-quotes': ['error', 'prefer-single'], - 'new-cap': ['error', { capIsNewExceptions: ['HTML.Comment', 'HTML.DIV', 'SHA256'] }], + 'new-cap': [ + 'error', + { capIsNewExceptions: ['HTML.Comment', 'HTML.Raw', 'HTML.DIV', 'SHA256'] }, + ], 'no-extra-parens': 'off', 'no-spaced-func': 'off', 'no-unused-vars': 'off',