From fa085463eca354e27c70cec83c96c343519528ac Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Tue, 23 Mar 2021 20:26:24 -0300 Subject: [PATCH 001/124] Use client entrypoint in TypeScript --- client/{adapters.js => adapters.ts} | 0 .../{importPackages.js => importPackages.ts} | 40 +++++++++---------- client/{main.js => main.ts} | 0 package.json | 2 +- 4 files changed, 21 insertions(+), 21 deletions(-) rename client/{adapters.js => adapters.ts} (100%) rename client/{importPackages.js => importPackages.ts} (80%) rename client/{main.js => main.ts} (100%) diff --git a/client/adapters.js b/client/adapters.ts similarity index 100% rename from client/adapters.js rename to client/adapters.ts diff --git a/client/importPackages.js b/client/importPackages.ts similarity index 80% rename from client/importPackages.js rename to client/importPackages.ts index 6932b8b69020d..06f1a470051c8 100644 --- a/client/importPackages.js +++ b/client/importPackages.ts @@ -27,13 +27,13 @@ import '../app/importer-slack/client'; import '../app/importer-slack-users/client'; import '../app/integrations/client/startup'; import '../app/ldap/client'; -import '../app/lib'; +import '../app/lib/client'; import '../app/livestream/client'; -import '../app/logger'; +import '../app/logger/client'; import '../app/token-login/client'; import '../app/markdown/client'; import '../app/mentions-flextab/client'; -import '../app/message-attachments'; +import '../app/message-attachments/client'; import '../app/message-mark-as-unread/client'; import '../app/message-pin/client'; import '../app/message-snippet/client'; @@ -53,42 +53,42 @@ import '../app/slashcommands-invite/client'; import '../app/slashcommands-inviteall/client'; import '../app/slashcommands-join/client'; import '../app/slashcommands-kick/client'; -import '../app/slashcommands-open'; +import '../app/slashcommands-open/client'; import '../app/slashcommands-topic/client'; import '../app/slashcommands-unarchiveroom/client'; import '../app/tokenpass/client'; -import '../app/ui'; -import '../app/ui-account'; -import '../app/ui-clean-history'; -import '../app/ui-login'; +import '../app/ui/client'; +import '../app/ui-account/client'; +import '../app/ui-clean-history/client'; +import '../app/ui-login/client'; import '../app/ui-master/client'; -import '../app/ui-message'; -import '../app/ui-sidenav'; +import '../app/ui-message/client'; +import '../app/ui-sidenav/client'; import '../app/ui-vrecord/client'; import '../app/videobridge/client'; import '../app/webdav/client'; import '../app/webrtc/client'; import '../app/wordpress/client'; -import '../app/nrr'; +import '../app/nrr/client'; import '../app/meteor-accounts-saml/client'; import '../app/e2e/client'; import '../app/blockstack/client'; import '../app/version-check/client'; import '../app/search/client'; import '../app/chatpal-search/client'; -import '../app/lazy-load'; +import '../app/lazy-load/client'; import '../app/discussion/client'; import '../app/threads/client'; import '../app/mail-messages/client'; -import '../app/user-status'; -import '../app/utils'; -import '../app/settings'; -import '../app/models'; -import '../app/callbacks'; -import '../app/notifications'; +import '../app/user-status/client'; +import '../app/utils/client'; +import '../app/settings/client'; +import '../app/models/client'; +import '../app/callbacks/client'; +import '../app/notifications/client'; import '../app/promises/client'; -import '../app/ui-utils'; -import '../app/ui-cached-collection'; +import '../app/ui-utils/client'; +import '../app/ui-cached-collection/client'; import '../app/action-links/client'; import '../app/reactions/client'; import '../app/livechat/client'; diff --git a/client/main.js b/client/main.ts similarity index 100% rename from client/main.js rename to client/main.ts diff --git a/package.json b/package.json index 8ddc5242bbce9..40b2c7b035dd0 100644 --- a/package.json +++ b/package.json @@ -283,7 +283,7 @@ }, "meteor": { "mainModule": { - "client": "client/main.js", + "client": "client/main.ts", "server": "server/main.js" } }, From f1550b9f098781edfafac88684a787b8e814da60 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Wed, 24 Mar 2021 11:23:33 -0300 Subject: [PATCH 002/124] Migrate polyfills to TypeScript --- .../{index.js => childNodeRemove.ts} | 11 +---- client/polyfills/{cssVars.js => cssVars.ts} | 44 ++++++++++++++----- client/polyfills/customEventPolyfill.js | 16 ------- client/polyfills/customEventPolyfill.ts | 28 ++++++++++++ client/polyfills/index.ts | 6 +++ client/polyfills/objectFromEntries.ts | 3 ++ 6 files changed, 72 insertions(+), 36 deletions(-) rename client/polyfills/{index.js => childNodeRemove.ts} (52%) rename client/polyfills/{cssVars.js => cssVars.ts} (65%) delete mode 100644 client/polyfills/customEventPolyfill.js create mode 100644 client/polyfills/customEventPolyfill.ts create mode 100644 client/polyfills/index.ts create mode 100644 client/polyfills/objectFromEntries.ts diff --git a/client/polyfills/index.js b/client/polyfills/childNodeRemove.ts similarity index 52% rename from client/polyfills/index.js rename to client/polyfills/childNodeRemove.ts index b871e4139d366..5525b5446cac4 100644 --- a/client/polyfills/index.js +++ b/client/polyfills/childNodeRemove.ts @@ -1,16 +1,9 @@ -import '@rocket.chat/fuselage-polyfills'; -import 'url-polyfill'; -import './customEventPolyfill'; -import './cssVars'; - -Object.fromEntries = Object.fromEntries || function fromEntries(iterable) { - return [...iterable].reduce((obj, { 0: key, 1: val }) => Object.assign(obj, { [key]: val }), {}); -}; -(function(arr) { +(function(arr): void { arr.forEach(function(item) { if (item.hasOwnProperty('remove')) { return; } + Object.defineProperty(item, 'remove', { configurable: true, enumerable: true, diff --git a/client/polyfills/cssVars.js b/client/polyfills/cssVars.ts similarity index 65% rename from client/polyfills/cssVars.js rename to client/polyfills/cssVars.ts index c936c2569a5e0..ef1d4b0b1ded1 100644 --- a/client/polyfills/cssVars.js +++ b/client/polyfills/cssVars.ts @@ -1,24 +1,34 @@ import _ from 'underscore'; -const findDeclarations = (code) => (code.match(/(--[^:; ]+:..*?;)/g) ?? []) +type Variables = { + [name: string]: (variables: Variables) => string; +}; + +const findDeclarations = (code: string): [string, Variables[keyof Variables]][] => (code.match(/(--[^:; ]+:..*?;)/g) ?? []) .map((declaration) => { - const [, name, value] = /(.*?):\s*(.*?)\s*;/.exec(declaration); + const matches = /(.*?):\s*(.*?)\s*;/.exec(declaration); + + if (matches === null) { + throw new Error(); + } + + const [, name, value] = matches; return [ name, value.indexOf('var(') >= 0 - ? (variables) => value.replace(/var\((--.*?)\)/gm, (_, name) => variables[name]?.call(null, variables)) - : () => value, + ? (variables: Variables): string => value.replace(/var\((--.*?)\)/gm, (_, name) => variables[name]?.call(null, variables)) + : (): string => value, ]; }); -const replaceReferences = (code, variables) => +const replaceReferences = (code: string, variables: Variables): string => code.replace(/var\((--.*?)\)/gm, (_, name) => variables[name]?.call(null, variables)); -let cssVariablesElement; +let cssVariablesElement: HTMLElement; const originalCodes = new Map(); const update = _.debounce(() => { - const declarations = [].concat( + const declarations = ([] as [string, Variables[keyof Variables]][]).concat( ...Array.from(originalCodes.values(), findDeclarations), findDeclarations(cssVariablesElement.innerHTML), ); @@ -44,9 +54,14 @@ const update = _.debounce(() => { }); }, 100); -const findAndPatchFromLinkElements = () => { +const findAndPatchFromLinkElements = (): void => { Array.from(document.querySelectorAll('link[type="text/css"].__meteor-css__')).forEach(async (linkElement) => { const url = linkElement.getAttribute('href'); + + if (url === null) { + return; + } + try { const response = await fetch(url); const code = await response.text(); @@ -59,13 +74,20 @@ const findAndPatchFromLinkElements = () => { }); }; -const waitAndInitialize = () => { +const waitAndInitialize = (): void => { if (document.readyState !== 'complete') { requestAnimationFrame(waitAndInitialize); return; } - cssVariablesElement = document.getElementById('css-variables'); + const element = document.getElementById('css-variables'); + + if (element === null) { + requestAnimationFrame(waitAndInitialize); + return; + } + + cssVariablesElement = element; const cssVariablesElementObserver = new MutationObserver(() => { update(); @@ -76,7 +98,7 @@ const waitAndInitialize = () => { findAndPatchFromLinkElements(); }; -(() => { +((): void => { if (window.CSS && window.CSS.supports && window.CSS.supports('(--foo: red)')) { return; } diff --git a/client/polyfills/customEventPolyfill.js b/client/polyfills/customEventPolyfill.js deleted file mode 100644 index c09ca6b01a59e..0000000000000 --- a/client/polyfills/customEventPolyfill.js +++ /dev/null @@ -1,16 +0,0 @@ -(() => { - if (typeof window.CustomEvent === 'function') { - return; - } - - function CustomEvent(event, params) { - params = params || { bubbles: false, cancelable: false, detail: null }; - const evt = document.createEvent('CustomEvent'); - evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); - return evt; - } - - CustomEvent.prototype = window.CustomEvent?.prototype || window.Event.prototype; - - window.CustomEvent = CustomEvent; -})(); diff --git a/client/polyfills/customEventPolyfill.ts b/client/polyfills/customEventPolyfill.ts new file mode 100644 index 0000000000000..22d1a5171f460 --- /dev/null +++ b/client/polyfills/customEventPolyfill.ts @@ -0,0 +1,28 @@ +((): void => { + if (typeof window.CustomEvent === 'function') { + return; + } + + const CustomEvent = function ( + type: string, + { + bubbles = false, + cancelable = false, + detail = null as unknown as T, + }: CustomEventInit = {}, + ): CustomEvent { + const evt = document.createEvent('CustomEvent') as CustomEvent; + evt.initCustomEvent( + type, + bubbles, + cancelable, + detail, + ); + return evt; + } as unknown as { + prototype: CustomEvent; + new(typeArg: string, eventInitDict?: CustomEventInit): CustomEvent; + }; + + window.CustomEvent = CustomEvent; +})(); diff --git a/client/polyfills/index.ts b/client/polyfills/index.ts new file mode 100644 index 0000000000000..6c9fbc80ceff7 --- /dev/null +++ b/client/polyfills/index.ts @@ -0,0 +1,6 @@ +import '@rocket.chat/fuselage-polyfills'; +import 'url-polyfill'; +import './childNodeRemove'; +import './cssVars'; +import './customEventPolyfill'; +import './objectFromEntries'; diff --git a/client/polyfills/objectFromEntries.ts b/client/polyfills/objectFromEntries.ts new file mode 100644 index 0000000000000..f80b50eb25990 --- /dev/null +++ b/client/polyfills/objectFromEntries.ts @@ -0,0 +1,3 @@ +Object.fromEntries = Object.fromEntries || function fromEntries(entries: Iterable): { [k: string]: T } { + return [...entries].reduce((obj, { 0: key, 1: val }) => Object.assign(obj, { [key]: val }), {}); +}; From ce7a507f17d1d8dfd0785d07196b8257d8589fcd Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Wed, 24 Mar 2021 12:53:38 -0300 Subject: [PATCH 003/124] Migrate some startup scripts --- client/startup/{index.js => index.ts} | 0 client/startup/{loginViaQuery.js => loginViaQuery.ts} | 0 client/startup/{roomObserve.js => roomObserve.ts} | 2 +- .../startup/{userSetUtcOffset.js => userSetUtcOffset.ts} | 6 +++--- client/startup/{usersObserve.js => usersObserve.ts} | 9 +++++---- client/types/kadira-flow-router.d.ts | 2 +- 6 files changed, 10 insertions(+), 9 deletions(-) rename client/startup/{index.js => index.ts} (100%) rename client/startup/{loginViaQuery.js => loginViaQuery.ts} (100%) rename client/startup/{roomObserve.js => roomObserve.ts} (87%) rename client/startup/{userSetUtcOffset.js => userSetUtcOffset.ts} (80%) rename client/startup/{usersObserve.js => usersObserve.ts} (83%) diff --git a/client/startup/index.js b/client/startup/index.ts similarity index 100% rename from client/startup/index.js rename to client/startup/index.ts diff --git a/client/startup/loginViaQuery.js b/client/startup/loginViaQuery.ts similarity index 100% rename from client/startup/loginViaQuery.js rename to client/startup/loginViaQuery.ts diff --git a/client/startup/roomObserve.js b/client/startup/roomObserve.ts similarity index 87% rename from client/startup/roomObserve.js rename to client/startup/roomObserve.ts index 4dd658b856570..5114c8778a0ea 100644 --- a/client/startup/roomObserve.js +++ b/client/startup/roomObserve.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Session } from 'meteor/session'; -import { ChatRoom } from '../../app/models'; +import { ChatRoom } from '../../app/models/client'; Meteor.startup(function() { ChatRoom.find().observe({ diff --git a/client/startup/userSetUtcOffset.js b/client/startup/userSetUtcOffset.ts similarity index 80% rename from client/startup/userSetUtcOffset.js rename to client/startup/userSetUtcOffset.ts index 710ae9710897a..cac8c471ecfd4 100644 --- a/client/startup/userSetUtcOffset.js +++ b/client/startup/userSetUtcOffset.ts @@ -2,10 +2,10 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import moment from 'moment'; -import { Users } from '../../app/models'; +import { Users } from '../../app/models/client'; -Meteor.startup(function() { - Tracker.autorun(function() { +Meteor.startup(() => { + Tracker.autorun(() => { const user = Users.findOne({ _id: Meteor.userId() }, { fields: { statusConnection: 1, utcOffset: 1 } }); if (user && user.statusConnection === 'online') { const utcOffset = moment().utcOffset() / 60; diff --git a/client/startup/usersObserve.js b/client/startup/usersObserve.ts similarity index 83% rename from client/startup/usersObserve.js rename to client/startup/usersObserve.ts index 713c3ce7ae25b..06a33bc5381f0 100644 --- a/client/startup/usersObserve.js +++ b/client/startup/usersObserve.ts @@ -1,16 +1,17 @@ import { Meteor } from 'meteor/meteor'; import { Session } from 'meteor/session'; -import { RoomManager } from '../../app/ui-utils'; +import { RoomManager } from '../../app/ui-utils/client'; +import { IUser } from '../../definition/IUser'; -Meteor.startup(function() { +Meteor.startup(() => { Meteor.users.find({}, { fields: { name: 1, username: 1, status: 1, utcOffset: 1, statusText: 1 } }).observe({ - added(user) { + added(user: IUser) { Session.set(`user_${ user.username }_status`, user.status); Session.set(`user_${ user.username }_status_text`, user.statusText); RoomManager.updateUserStatus(user, user.status, user.utcOffset); }, - changed(user) { + changed(user: IUser) { Session.set(`user_${ user.username }_status`, user.status); if (user.statusText !== undefined) { Session.set(`user_${ user.username }_status_text`, user.statusText); diff --git a/client/types/kadira-flow-router.d.ts b/client/types/kadira-flow-router.d.ts index 934e0ba5d36d5..e31766f5b227d 100644 --- a/client/types/kadira-flow-router.d.ts +++ b/client/types/kadira-flow-router.d.ts @@ -110,7 +110,7 @@ declare module 'meteor/kadira:flow-router' { setParams(newParams: Record): boolean; - setQueryParams(newParams: Record): boolean; + setQueryParams(newParams: Record): boolean; current(): Current; From 9afe3304c2055cb34d3e54e5f670534349f65813 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Wed, 24 Mar 2021 13:27:00 -0300 Subject: [PATCH 004/124] Migrate some startup modules to TypeScript --- client/startup/contextualBar/{index.js => index.ts} | 0 client/startup/e2e.js | 2 +- client/startup/emailVerification.js | 2 +- client/startup/i18n.js | 6 +++--- .../renderMessage/{autolinker.js => autolinker.ts} | 4 ++-- .../{autotranslate.js => autotranslate.ts} | 4 ++-- client/startup/renderMessage/{emoji.js => emoji.ts} | 4 ++-- .../renderMessage/{googlevision.js => googlevision.ts} | 4 ++-- .../startup/renderMessage/{hexcolor.js => hexcolor.ts} | 4 ++-- .../{highlightWords.js => highlightWords.ts} | 10 +++++----- client/startup/renderMessage/{index.js => index.ts} | 0 .../renderMessage/{issuelink.js => issuelink.ts} | 4 ++-- client/startup/renderMessage/{katex.js => katex.ts} | 4 ++-- .../startup/renderMessage/{markdown.js => markdown.ts} | 4 ++-- .../{mentionsMessage.js => mentionsMessage.ts} | 4 ++-- .../startup/renderNotification/{index.js => index.ts} | 0 .../renderNotification/{markdown.js => markdown.ts} | 4 ++-- client/startup/startup.js | 4 ++-- .../{autotranslate.js => autotranslate.ts} | 6 +++--- .../streamMessage/{googlevision.js => googlevision.ts} | 4 ++-- client/startup/streamMessage/{index.js => index.ts} | 0 client/startup/unread.js | 8 ++++---- 22 files changed, 41 insertions(+), 41 deletions(-) rename client/startup/contextualBar/{index.js => index.ts} (100%) rename client/startup/renderMessage/{autolinker.js => autolinker.ts} (89%) rename client/startup/renderMessage/{autotranslate.js => autotranslate.ts} (85%) rename client/startup/renderMessage/{emoji.js => emoji.ts} (82%) rename client/startup/renderMessage/{googlevision.js => googlevision.ts} (83%) rename client/startup/renderMessage/{hexcolor.js => hexcolor.ts} (83%) rename client/startup/renderMessage/{highlightWords.js => highlightWords.ts} (60%) rename client/startup/renderMessage/{index.js => index.ts} (100%) rename client/startup/renderMessage/{issuelink.js => issuelink.ts} (85%) rename client/startup/renderMessage/{katex.js => katex.ts} (85%) rename client/startup/renderMessage/{markdown.js => markdown.ts} (89%) rename client/startup/renderMessage/{mentionsMessage.js => mentionsMessage.ts} (86%) rename client/startup/renderNotification/{index.js => index.ts} (100%) rename client/startup/renderNotification/{markdown.js => markdown.ts} (81%) rename client/startup/streamMessage/{autotranslate.js => autotranslate.ts} (78%) rename client/startup/streamMessage/{googlevision.js => googlevision.ts} (84%) rename client/startup/streamMessage/{index.js => index.ts} (100%) diff --git a/client/startup/contextualBar/index.js b/client/startup/contextualBar/index.ts similarity index 100% rename from client/startup/contextualBar/index.js rename to client/startup/contextualBar/index.ts diff --git a/client/startup/e2e.js b/client/startup/e2e.js index 4f00d7ff09c8f..a7e07a3355d38 100644 --- a/client/startup/e2e.js +++ b/client/startup/e2e.js @@ -7,7 +7,7 @@ import { settings } from '../../app/settings/client'; import { promises } from '../../app/promises/client'; import { Notifications } from '../../app/notifications/client'; import { e2e } from '../../app/e2e/client/rocketchat.e2e'; -import { Subscriptions } from '../../app/models'; +import { Subscriptions } from '../../app/models/client'; import { waitUntilFind } from '../../app/e2e/client/waitUntilFind'; const handle = async (roomId, keyId) => { diff --git a/client/startup/emailVerification.js b/client/startup/emailVerification.js index d70e6fd709ff9..e6fca3d8dad1b 100644 --- a/client/startup/emailVerification.js +++ b/client/startup/emailVerification.js @@ -4,7 +4,7 @@ import { Session } from 'meteor/session'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import toastr from 'toastr'; -import { settings } from '../../app/settings'; +import { settings } from '../../app/settings/client'; Meteor.startup(function() { Tracker.autorun(function() { diff --git a/client/startup/i18n.js b/client/startup/i18n.js index f5a27c247d094..dc3d4116bb120 100644 --- a/client/startup/i18n.js +++ b/client/startup/i18n.js @@ -4,9 +4,9 @@ import { Tracker } from 'meteor/tracker'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import moment from 'moment'; -import { isRtl } from '../../app/utils'; -import { settings } from '../../app/settings'; -import { Users } from '../../app/models'; +import { isRtl } from '../../app/utils/client'; +import { settings } from '../../app/settings/client'; +import { Users } from '../../app/models/client'; const currentLanguage = new ReactiveVar(); diff --git a/client/startup/renderMessage/autolinker.js b/client/startup/renderMessage/autolinker.ts similarity index 89% rename from client/startup/renderMessage/autolinker.js rename to client/startup/renderMessage/autolinker.ts index cd2e25e19dcd6..82d6fc4fba777 100644 --- a/client/startup/renderMessage/autolinker.js +++ b/client/startup/renderMessage/autolinker.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../app/settings'; -import { callbacks } from '../../../app/callbacks'; +import { settings } from '../../../app/settings/client'; +import { callbacks } from '../../../app/callbacks/client'; Meteor.startup(() => { Tracker.autorun(() => { diff --git a/client/startup/renderMessage/autotranslate.js b/client/startup/renderMessage/autotranslate.ts similarity index 85% rename from client/startup/renderMessage/autotranslate.js rename to client/startup/renderMessage/autotranslate.ts index 96dd3a307394a..88d66349c665f 100644 --- a/client/startup/renderMessage/autotranslate.js +++ b/client/startup/renderMessage/autotranslate.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../app/settings'; -import { callbacks } from '../../../app/callbacks'; +import { settings } from '../../../app/settings/client'; +import { callbacks } from '../../../app/callbacks/client'; import { hasPermission } from '../../../app/authorization/client'; Meteor.startup(() => { diff --git a/client/startup/renderMessage/emoji.js b/client/startup/renderMessage/emoji.ts similarity index 82% rename from client/startup/renderMessage/emoji.js rename to client/startup/renderMessage/emoji.ts index 938ff76e98eca..4ddbdfbcf2a59 100644 --- a/client/startup/renderMessage/emoji.js +++ b/client/startup/renderMessage/emoji.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { getUserPreference } from '../../../app/utils'; -import { callbacks } from '../../../app/callbacks'; +import { getUserPreference } from '../../../app/utils/client'; +import { callbacks } from '../../../app/callbacks/client'; Meteor.startup(() => { Tracker.autorun(() => { diff --git a/client/startup/renderMessage/googlevision.js b/client/startup/renderMessage/googlevision.ts similarity index 83% rename from client/startup/renderMessage/googlevision.js rename to client/startup/renderMessage/googlevision.ts index 2c6e335faea3f..989a6117f3ea1 100644 --- a/client/startup/renderMessage/googlevision.js +++ b/client/startup/renderMessage/googlevision.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../app/settings'; -import { callbacks } from '../../../app/callbacks'; +import { settings } from '../../../app/settings/client'; +import { callbacks } from '../../../app/callbacks/client'; Meteor.startup(() => { Tracker.autorun(() => { diff --git a/client/startup/renderMessage/hexcolor.js b/client/startup/renderMessage/hexcolor.ts similarity index 83% rename from client/startup/renderMessage/hexcolor.js rename to client/startup/renderMessage/hexcolor.ts index 99807323ea137..a4586c509d7fe 100644 --- a/client/startup/renderMessage/hexcolor.js +++ b/client/startup/renderMessage/hexcolor.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../app/settings'; -import { callbacks } from '../../../app/callbacks'; +import { settings } from '../../../app/settings/client'; +import { callbacks } from '../../../app/callbacks/client'; Meteor.startup(() => { Tracker.autorun(() => { diff --git a/client/startup/renderMessage/highlightWords.js b/client/startup/renderMessage/highlightWords.ts similarity index 60% rename from client/startup/renderMessage/highlightWords.js rename to client/startup/renderMessage/highlightWords.ts index b1fbd0b28218b..9239069a1596f 100644 --- a/client/startup/renderMessage/highlightWords.js +++ b/client/startup/renderMessage/highlightWords.ts @@ -1,12 +1,13 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { callbacks } from '../../../app/callbacks'; -import { getUserPreference } from '../../../app/utils'; +import { callbacks } from '../../../app/callbacks/client'; +import { getUserPreference } from '../../../app/utils/client'; Meteor.startup(() => { Tracker.autorun(() => { - const isEnabled = getUserPreference(Meteor.userId(), 'highlights')?.some((highlight) => highlight?.trim()) ?? false; + const highlights: (string | undefined)[] | undefined = getUserPreference(Meteor.userId(), 'highlights'); + const isEnabled = highlights?.some((highlight) => highlight?.trim()) ?? false; if (!isEnabled) { callbacks.remove('renderMessage', 'highlight-words'); @@ -14,8 +15,7 @@ Meteor.startup(() => { } const options = { - wordsToHighlight: getUserPreference(Meteor.userId(), 'highlights') - .filter((highlight) => highlight?.trim()), + wordsToHighlight: highlights?.filter((highlight) => highlight?.trim()), }; import('../../../app/highlight-words').then(({ createHighlightWordsMessageRenderer }) => { diff --git a/client/startup/renderMessage/index.js b/client/startup/renderMessage/index.ts similarity index 100% rename from client/startup/renderMessage/index.js rename to client/startup/renderMessage/index.ts diff --git a/client/startup/renderMessage/issuelink.js b/client/startup/renderMessage/issuelink.ts similarity index 85% rename from client/startup/renderMessage/issuelink.js rename to client/startup/renderMessage/issuelink.ts index d7536f39576ad..b8ef641720d18 100644 --- a/client/startup/renderMessage/issuelink.js +++ b/client/startup/renderMessage/issuelink.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../app/settings'; -import { callbacks } from '../../../app/callbacks'; +import { settings } from '../../../app/settings/client'; +import { callbacks } from '../../../app/callbacks/client'; Meteor.startup(() => { Tracker.autorun(() => { diff --git a/client/startup/renderMessage/katex.js b/client/startup/renderMessage/katex.ts similarity index 85% rename from client/startup/renderMessage/katex.js rename to client/startup/renderMessage/katex.ts index b2233b4471cc1..4f5d042d1b3a5 100644 --- a/client/startup/renderMessage/katex.js +++ b/client/startup/renderMessage/katex.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { callbacks } from '../../../app/callbacks'; -import { settings } from '../../../app/settings'; +import { callbacks } from '../../../app/callbacks/client'; +import { settings } from '../../../app/settings/client'; Meteor.startup(() => { Tracker.autorun(() => { diff --git a/client/startup/renderMessage/markdown.js b/client/startup/renderMessage/markdown.ts similarity index 89% rename from client/startup/renderMessage/markdown.js rename to client/startup/renderMessage/markdown.ts index 8d10ed44fe3f9..e38f62bb89bc5 100644 --- a/client/startup/renderMessage/markdown.js +++ b/client/startup/renderMessage/markdown.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { callbacks } from '../../../app/callbacks'; -import { settings } from '../../../app/settings'; +import { callbacks } from '../../../app/callbacks/client'; +import { settings } from '../../../app/settings/client'; Meteor.startup(() => { Tracker.autorun(() => { diff --git a/client/startup/renderMessage/mentionsMessage.js b/client/startup/renderMessage/mentionsMessage.ts similarity index 86% rename from client/startup/renderMessage/mentionsMessage.js rename to client/startup/renderMessage/mentionsMessage.ts index 5bcb80f6ef0f4..ec2936bc699b2 100644 --- a/client/startup/renderMessage/mentionsMessage.js +++ b/client/startup/renderMessage/mentionsMessage.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { callbacks } from '../../../app/callbacks'; -import { settings } from '../../../app/settings'; +import { callbacks } from '../../../app/callbacks/client'; +import { settings } from '../../../app/settings/client'; import { Users } from '../../../app/models/client'; Meteor.startup(() => { diff --git a/client/startup/renderNotification/index.js b/client/startup/renderNotification/index.ts similarity index 100% rename from client/startup/renderNotification/index.js rename to client/startup/renderNotification/index.ts diff --git a/client/startup/renderNotification/markdown.js b/client/startup/renderNotification/markdown.ts similarity index 81% rename from client/startup/renderNotification/markdown.js rename to client/startup/renderNotification/markdown.ts index ea11d0194377a..a76f420164d71 100644 --- a/client/startup/renderNotification/markdown.js +++ b/client/startup/renderNotification/markdown.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; -import { callbacks } from '../../../app/callbacks'; -import { settings } from '../../../app/settings'; +import { callbacks } from '../../../app/callbacks/client'; +import { settings } from '../../../app/settings/client'; Meteor.startup(() => { const options = { diff --git a/client/startup/startup.js b/client/startup/startup.js index 802b9af9bd634..e1148c04a2225 100644 --- a/client/startup/startup.js +++ b/client/startup/startup.js @@ -8,8 +8,8 @@ import toastr from 'toastr'; import hljs from '../../app/markdown/lib/hljs'; -import { fireGlobalEvent } from '../../app/ui-utils'; -import { getUserPreference, t } from '../../app/utils'; +import { fireGlobalEvent } from '../../app/ui-utils/client'; +import { getUserPreference, t } from '../../app/utils/client'; import { hasPermission } from '../../app/authorization/client'; import 'highlight.js/styles/github.css'; import { synchronizeUserData } from '../lib/userData'; diff --git a/client/startup/streamMessage/autotranslate.js b/client/startup/streamMessage/autotranslate.ts similarity index 78% rename from client/startup/streamMessage/autotranslate.js rename to client/startup/streamMessage/autotranslate.ts index c94495e8e82b1..5566d9dd025b5 100644 --- a/client/startup/streamMessage/autotranslate.js +++ b/client/startup/streamMessage/autotranslate.ts @@ -1,9 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../app/settings'; -import { callbacks } from '../../../app/callbacks'; -import { hasPermission } from '../../../app/authorization'; +import { settings } from '../../../app/settings/client'; +import { callbacks } from '../../../app/callbacks/client'; +import { hasPermission } from '../../../app/authorization/client'; Meteor.startup(() => { Tracker.autorun(() => { diff --git a/client/startup/streamMessage/googlevision.js b/client/startup/streamMessage/googlevision.ts similarity index 84% rename from client/startup/streamMessage/googlevision.js rename to client/startup/streamMessage/googlevision.ts index 5c931f039bdab..499d286fec77f 100644 --- a/client/startup/streamMessage/googlevision.js +++ b/client/startup/streamMessage/googlevision.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../app/settings'; -import { callbacks } from '../../../app/callbacks'; +import { settings } from '../../../app/settings/client'; +import { callbacks } from '../../../app/callbacks/client'; Meteor.startup(() => { Tracker.autorun(() => { diff --git a/client/startup/streamMessage/index.js b/client/startup/streamMessage/index.ts similarity index 100% rename from client/startup/streamMessage/index.js rename to client/startup/streamMessage/index.ts diff --git a/client/startup/unread.js b/client/startup/unread.js index 6072b23561ae8..10211a8b52ae9 100644 --- a/client/startup/unread.js +++ b/client/startup/unread.js @@ -2,11 +2,11 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import { Session } from 'meteor/session'; -import { Favico } from '../../app/favico'; +import { Favico } from '../../app/favico/client'; import { ChatSubscription, ChatRoom } from '../../app/models/client'; -import { menu, fireGlobalEvent } from '../../app/ui-utils'; -import { getUserPreference } from '../../app/utils'; -import { settings } from '../../app/settings'; +import { menu, fireGlobalEvent } from '../../app/ui-utils/client'; +import { getUserPreference } from '../../app/utils/client'; +import { settings } from '../../app/settings/client'; const fetchSubscriptions = () => ChatSubscription.find({ open: true, From 81a1f9a55ec0ef5b955cb61edd180c12816cdb65 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Wed, 24 Mar 2021 22:26:54 -0300 Subject: [PATCH 005/124] Convert reactAdapters module to TypeScript --- client/lib/createRouteGroup.ts | 6 +- client/reactAdapters.js | 209 ------------------ client/reactAdapters/createEphemeralPortal.ts | 21 ++ client/reactAdapters/createLazyElement.ts | 32 +++ client/reactAdapters/createLazyPortal.ts | 12 + .../createTemplateForComponent.ts | 46 ++++ client/reactAdapters/index.ts | 5 + client/reactAdapters/mountRoot.ts | 31 +++ client/reactAdapters/portalsSubscription.ts | 50 +++++ client/reactAdapters/renderRouteComponent.ts | 101 +++++++++ client/types/fuselage-tokens-colors.d.ts | 6 + client/types/fuselage.d.ts | 10 - client/types/kadira-blaze-layout.d.ts | 6 + client/types/meteor-htmljs.d.ts | 5 + client/types/meteor-mongo.d.ts | 12 + client/types/meteor-tracker.d.ts | 5 + client/types/meteor.d.ts | 25 +-- 17 files changed, 338 insertions(+), 244 deletions(-) delete mode 100644 client/reactAdapters.js create mode 100644 client/reactAdapters/createEphemeralPortal.ts create mode 100644 client/reactAdapters/createLazyElement.ts create mode 100644 client/reactAdapters/createLazyPortal.ts create mode 100644 client/reactAdapters/createTemplateForComponent.ts create mode 100644 client/reactAdapters/index.ts create mode 100644 client/reactAdapters/mountRoot.ts create mode 100644 client/reactAdapters/portalsSubscription.ts create mode 100644 client/reactAdapters/renderRouteComponent.ts create mode 100644 client/types/fuselage-tokens-colors.d.ts create mode 100644 client/types/kadira-blaze-layout.d.ts create mode 100644 client/types/meteor-htmljs.d.ts create mode 100644 client/types/meteor-mongo.d.ts create mode 100644 client/types/meteor-tracker.d.ts diff --git a/client/lib/createRouteGroup.ts b/client/lib/createRouteGroup.ts index c25fc1abe81a5..c3daff18d740c 100644 --- a/client/lib/createRouteGroup.ts +++ b/client/lib/createRouteGroup.ts @@ -1,18 +1,18 @@ import { FlowRouter } from 'meteor/kadira:flow-router'; -import type { ElementType } from 'react'; +import type { ComponentType } from 'react'; import { renderRouteComponent } from '../reactAdapters'; type RouteRegister = { (path: string, params: { name: string; - lazyRouteComponent: () => Promise; + lazyRouteComponent: () => Promise; props: Record; action: (params?: Record, queryParams?: Record) => void; }): void; }; -export const createRouteGroup = (name: string, prefix: string, importRouter: () => Promise): RouteRegister => { +export const createRouteGroup = (name: string, prefix: string, importRouter: () => Promise<{ default: ComponentType }>): RouteRegister => { const routeGroup = FlowRouter.group({ name, prefix, diff --git a/client/reactAdapters.js b/client/reactAdapters.js deleted file mode 100644 index 7913b08768121..0000000000000 --- a/client/reactAdapters.js +++ /dev/null @@ -1,209 +0,0 @@ -import { Emitter } from '@rocket.chat/emitter'; -import { Blaze } from 'meteor/blaze'; -import { HTML } from 'meteor/htmljs'; -import { Random } from 'meteor/random'; -import { BlazeLayout } from 'meteor/kadira:blaze-layout'; -import { FlowRouter } from 'meteor/kadira:flow-router'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Template } from 'meteor/templating'; -import { Tracker } from 'meteor/tracker'; - -const createPortalsSubscription = () => { - const portalsMap = new Map(); - const emitter = new Emitter(); - - return { - getCurrentValue: () => Array.from(portalsMap.values()), - subscribe: (callback) => emitter.on('update', callback), - delete: (key) => { - portalsMap.delete(key); - emitter.emit('update'); - }, - set: (key, portal) => { - portalsMap.set(key, { portal, key: Random.id() }); - emitter.emit('update'); - }, - has: (key) => portalsMap.has(key), - }; -}; - -export const portalsSubscription = createPortalsSubscription(); - -let rootNode; - -export const mountRoot = async () => { - if (rootNode) { - return; - } - - rootNode = document.getElementById('react-root'); - - if (!rootNode) { - rootNode = document.createElement('div'); - rootNode.id = 'react-root'; - document.body.insertBefore(rootNode, document.body.firstChild); - } - - const [ - { Suspense, createElement, lazy }, - { render }, - ] = await Promise.all([ - import('react'), - import('react-dom'), - ]); - - const LazyAppRoot = lazy(() => import('./components/AppRoot')); - - render(createElement(Suspense, { fallback: null }, createElement(LazyAppRoot)), rootNode); -}; - -const unregisterPortal = (key) => { - portalsSubscription.delete(key); -}; - -export const registerPortal = (key, portal) => { - mountRoot(); - portalsSubscription.set(key, portal); - return () => unregisterPortal(key); -}; - -const createLazyElement = async (importFn, propsFn) => { - const { createElement, lazy, useEffect, useState, memo, Suspense } = await import('react'); - const LazyComponent = lazy(importFn); - - if (!propsFn) { - return createElement(LazyComponent); - } - - const WrappedComponent = memo(() => { - const [props, setProps] = useState(() => Tracker.nonreactive(propsFn)); - - useEffect(() => { - const computation = Tracker.autorun(() => { - setProps(propsFn()); - }); - - return () => { - computation.stop(); - }; - }, []); - - return createElement(Suspense, { fallback: null }, createElement(LazyComponent, props)); - }); - - return createElement(WrappedComponent); -}; - -const createLazyPortal = async (importFn, propsFn, node) => { - const { createPortal } = await import('react-dom'); - return createPortal(await createLazyElement(importFn, propsFn), node); -}; - -export const createEphemeralPortal = async (importFn, propsFn, node) => { - const portal = await createLazyPortal(importFn, propsFn, node); - return registerPortal(node, portal); -}; - -const unregister = Symbol('unregister'); - -export const createTemplateForComponent = ( - name, - importFn, - { - renderContainerView = () => HTML.DIV(), // eslint-disable-line new-cap - } = {}, -) => { - if (Template[name]) { - return name; - } - - const template = new Blaze.Template(name, renderContainerView); - template.onRendered(async function() { - const props = new ReactiveVar(this.data); - this.autorun(() => { - props.set(Template.currentData()); - }); - - const portal = await createLazyPortal(importFn, () => props.get(), this.firstNode); - - if (!this.firstNode) { - return; - } - - this[unregister] = await registerPortal(this, portal); - }); - - template.onDestroyed(function() { - this[unregister]?.(); - }); - - Template[name] = template; - - return name; -}; - -export const renderRouteComponent = (importFn, { - template, - region, - propsFn = () => ({}), -} = {}) => { - const routeName = FlowRouter.getRouteName(); - - if (portalsSubscription.has(routeName)) { - return; - } - - Tracker.autorun(async (computation) => { - if (routeName !== FlowRouter.getRouteName()) { - unregisterPortal(routeName); - computation.stop(); - return; - } - - if (!computation.firstRun) { - return; - } - - if (!template || !region) { - BlazeLayout.reset(); - - const element = await createLazyElement(importFn, propsFn); - - if (routeName !== FlowRouter.getRouteName()) { - return; - } - - registerPortal(routeName, element); - return; - } - - if (!Template[routeName]) { - const blazeTemplate = new Blaze.Template(routeName, () => HTML.DIV()); // eslint-disable-line new-cap - - blazeTemplate.onRendered(async function() { - const node = this.firstNode.parentElement; - this.firstNode.remove(); - const portal = await createLazyPortal(importFn, propsFn, node); - - if (routeName !== FlowRouter.getRouteName()) { - return; - } - - registerPortal(routeName, portal); - - const handleMainContentDestroyed = () => { - unregisterPortal(routeName); - document.removeEventListener('main-content-destroyed', handleMainContentDestroyed); - }; - - document.addEventListener('main-content-destroyed', handleMainContentDestroyed); - }); - - Template[routeName] = blazeTemplate; - } - - Tracker.afterFlush(() => { - BlazeLayout.render(template, { [region]: routeName }); - }); - }); -}; diff --git a/client/reactAdapters/createEphemeralPortal.ts b/client/reactAdapters/createEphemeralPortal.ts new file mode 100644 index 0000000000000..1e541128f0922 --- /dev/null +++ b/client/reactAdapters/createEphemeralPortal.ts @@ -0,0 +1,21 @@ +import type { ComponentType, PropsWithoutRef } from 'react'; + +import { portalsSubscription, registerPortal, unregisterPortal } from './portalsSubscription'; +import { mountRoot } from './mountRoot'; +import { createLazyPortal } from './createLazyPortal'; + +export { + portalsSubscription, + mountRoot, + registerPortal, + unregisterPortal, +}; + +export const createEphemeralPortal = async ( + factory: () => Promise<{ default: ComponentType }>, + propsFn: () => PropsWithoutRef, + container: Element, +): Promise<() => void> => { + const portal = await createLazyPortal(factory, propsFn, container); + return registerPortal(container, portal); +}; diff --git a/client/reactAdapters/createLazyElement.ts b/client/reactAdapters/createLazyElement.ts new file mode 100644 index 0000000000000..b50badcccf80b --- /dev/null +++ b/client/reactAdapters/createLazyElement.ts @@ -0,0 +1,32 @@ +import { Tracker } from 'meteor/tracker'; +import type { ComponentType, ReactElement, PropsWithoutRef } from 'react'; + +export const createLazyElement = async ( + factory: () => Promise<{ default: ComponentType }>, + getProps?: () => PropsWithoutRef | undefined, +): Promise => { + const { createElement, lazy, useEffect, useState, memo, Suspense } = await import('react'); + const LazyComponent = lazy(factory); + + if (!getProps) { + return createElement(LazyComponent); + } + + const WrappedComponent = memo(() => { + const [props, setProps] = useState(() => Tracker.nonreactive(getProps)); + + useEffect(() => { + const computation = Tracker.autorun(() => { + setProps(getProps()); + }); + + return (): void => { + computation.stop(); + }; + }, []); + + return createElement(Suspense, { fallback: null }, createElement(LazyComponent, props)); + }); + + return createElement(WrappedComponent); +}; diff --git a/client/reactAdapters/createLazyPortal.ts b/client/reactAdapters/createLazyPortal.ts new file mode 100644 index 0000000000000..8f90c7b1de4bb --- /dev/null +++ b/client/reactAdapters/createLazyPortal.ts @@ -0,0 +1,12 @@ +import type { ComponentType, PropsWithoutRef, ReactPortal } from 'react'; + +import { createLazyElement } from './createLazyElement'; + +export const createLazyPortal = async ( + factory: () => Promise<{ default: ComponentType }>, + getProps: () => PropsWithoutRef | undefined, + container: Element, +): Promise => { + const { createPortal } = await import('react-dom'); + return createPortal(await createLazyElement(factory, getProps), container); +}; diff --git a/client/reactAdapters/createTemplateForComponent.ts b/client/reactAdapters/createTemplateForComponent.ts new file mode 100644 index 0000000000000..a947f26bf743e --- /dev/null +++ b/client/reactAdapters/createTemplateForComponent.ts @@ -0,0 +1,46 @@ +import { Blaze } from 'meteor/blaze'; +import { HTML } from 'meteor/htmljs'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Template } from 'meteor/templating'; +import type { ComponentType, PropsWithoutRef } from 'react'; + +import { registerPortal } from './portalsSubscription'; +import { createLazyPortal } from './createLazyPortal'; + +const unregister = Symbol('unregister'); + +export const createTemplateForComponent = ( + name: string, + factory: () => Promise<{ default: ComponentType }>, + { + renderContainerView = (): unknown => HTML.DIV(), // eslint-disable-line new-cap + } = {}, +): string => { + if (Template[name]) { + return name; + } + + const template = new Blaze.Template(name, renderContainerView); + template.onRendered(async function(this: Blaze.TemplateInstance & Record) { + const props = new ReactiveVar(this.data as PropsWithoutRef); + this.autorun(() => { + props.set(Template.currentData()); + }); + + const portal = await createLazyPortal(factory, () => props.get(), this.firstNode as Element); + + if (!this.firstNode) { + return; + } + + this[unregister] = registerPortal(this, portal); + }); + + template.onDestroyed(function(this: Blaze.TemplateInstance & Record void | undefined>) { + this[unregister]?.(); + }); + + Template[name] = template; + + return name; +}; diff --git a/client/reactAdapters/index.ts b/client/reactAdapters/index.ts new file mode 100644 index 0000000000000..08c4c295a92bb --- /dev/null +++ b/client/reactAdapters/index.ts @@ -0,0 +1,5 @@ +export { portalsSubscription, registerPortal, unregisterPortal } from './portalsSubscription'; +export { mountRoot } from './mountRoot'; +export { createEphemeralPortal } from './createEphemeralPortal'; +export { createTemplateForComponent } from './createTemplateForComponent'; +export { renderRouteComponent } from './renderRouteComponent'; diff --git a/client/reactAdapters/mountRoot.ts b/client/reactAdapters/mountRoot.ts new file mode 100644 index 0000000000000..b5c93e9fd4eb4 --- /dev/null +++ b/client/reactAdapters/mountRoot.ts @@ -0,0 +1,31 @@ +import { portalsSubscription } from './portalsSubscription'; + +export { portalsSubscription }; + +let rootNode: HTMLElement | null; + +export const mountRoot = async (): Promise => { + if (rootNode) { + return; + } + + rootNode = document.getElementById('react-root'); + + if (!rootNode) { + rootNode = document.createElement('div'); + rootNode.id = 'react-root'; + document.body.insertBefore(rootNode, document.body.firstChild); + } + + const [ + { Suspense, createElement, lazy }, + { render }, + ] = await Promise.all([ + import('react'), + import('react-dom'), + ]); + + const LazyAppRoot = lazy(() => import('../components/AppRoot')); + + render(createElement(Suspense, { fallback: null }, createElement(LazyAppRoot)), rootNode); +}; diff --git a/client/reactAdapters/portalsSubscription.ts b/client/reactAdapters/portalsSubscription.ts new file mode 100644 index 0000000000000..d21f99860d2e4 --- /dev/null +++ b/client/reactAdapters/portalsSubscription.ts @@ -0,0 +1,50 @@ +import { Emitter } from '@rocket.chat/emitter'; +import { Random } from 'meteor/random'; +import type { Subscription, Unsubscribe } from 'use-subscription'; +import type { ReactElement } from 'react'; + +import { mountRoot } from './mountRoot'; + +type SubscribedPortal = { + portal: ReactElement; + key: string; +}; + +type PortalsSubscription = Subscription & { + has: (key: unknown) => boolean; + set: (key: unknown, portal: ReactElement) => void; + delete: (key: unknown) => void; +}; + +const createPortalsSubscription = (): PortalsSubscription => { + const portalsMap = new Map(); + const emitter = new Emitter<{ 'update': void }>(); + + return { + getCurrentValue: (): SubscribedPortal[] => Array.from(portalsMap.values()), + subscribe: (callback): Unsubscribe => emitter.on('update', callback), + delete: (key): void => { + portalsMap.delete(key); + emitter.emit('update'); + }, + set: (key, portal): void => { + portalsMap.set(key, { portal, key: Random.id() }); + emitter.emit('update'); + }, + has: (key): boolean => portalsMap.has(key), + }; +}; + +export const portalsSubscription = createPortalsSubscription(); + +export const unregisterPortal = (key: unknown): void => { + portalsSubscription.delete(key); +}; + +export const registerPortal = (key: unknown, portal: ReactElement): () => void => { + mountRoot(); + portalsSubscription.set(key, portal); + return (): void => { + unregisterPortal(key); + }; +}; diff --git a/client/reactAdapters/renderRouteComponent.ts b/client/reactAdapters/renderRouteComponent.ts new file mode 100644 index 0000000000000..347b0463822fe --- /dev/null +++ b/client/reactAdapters/renderRouteComponent.ts @@ -0,0 +1,101 @@ +import { Blaze } from 'meteor/blaze'; +import { HTML } from 'meteor/htmljs'; +import { BlazeLayout } from 'meteor/kadira:blaze-layout'; +import { FlowRouter } from 'meteor/kadira:flow-router'; +import { Template } from 'meteor/templating'; +import { Tracker } from 'meteor/tracker'; +import type { ComponentType, PropsWithoutRef } from 'react'; + +import { portalsSubscription, registerPortal, unregisterPortal } from './portalsSubscription'; +import { mountRoot } from './mountRoot'; +import { createLazyElement } from './createLazyElement'; +import { createLazyPortal } from './createLazyPortal'; +import { createEphemeralPortal } from './createEphemeralPortal'; +import { createTemplateForComponent } from './createTemplateForComponent'; + +export { + portalsSubscription, + mountRoot, + registerPortal, + unregisterPortal, + createEphemeralPortal, + createTemplateForComponent, +}; + +export const renderRouteComponent = ( + factory: () => Promise<{ default: ComponentType }>, + { + template, + region, + propsFn: getProps, + }: { + template?: string; + region?: string; + propsFn?: () => PropsWithoutRef | undefined; + } = {}, +): void => { + const routeName = FlowRouter.getRouteName(); + + if (portalsSubscription.has(routeName)) { + return; + } + + Tracker.autorun(async (computation) => { + if (routeName !== FlowRouter.getRouteName()) { + unregisterPortal(routeName); + computation.stop(); + return; + } + + if (!computation.firstRun) { + return; + } + + if (!template || !region) { + BlazeLayout.reset(); + + const element = await createLazyElement(factory, getProps); + + if (routeName !== FlowRouter.getRouteName()) { + return; + } + + registerPortal(routeName, element); + return; + } + + if (!Template[routeName]) { + const blazeTemplate = new Blaze.Template(routeName, () => HTML.DIV()); // eslint-disable-line new-cap + + blazeTemplate.onRendered(async function(this: Blaze.TemplateInstance & { firstNode: Element }) { + const node = this.firstNode.parentElement; + + if (!node) { + throw new Error(); + } + + this.firstNode.remove(); + const portal = await createLazyPortal(factory, getProps ?? ((): undefined => undefined), node); + + if (routeName !== FlowRouter.getRouteName()) { + return; + } + + registerPortal(routeName, portal); + + const handleMainContentDestroyed = (): void => { + unregisterPortal(routeName); + document.removeEventListener('main-content-destroyed', handleMainContentDestroyed); + }; + + document.addEventListener('main-content-destroyed', handleMainContentDestroyed); + }); + + Template[routeName] = blazeTemplate; + } + + Tracker.afterFlush(() => { + BlazeLayout.render(template, { [region]: routeName }); + }); + }); +}; diff --git a/client/types/fuselage-tokens-colors.d.ts b/client/types/fuselage-tokens-colors.d.ts new file mode 100644 index 0000000000000..d9d4090972a92 --- /dev/null +++ b/client/types/fuselage-tokens-colors.d.ts @@ -0,0 +1,6 @@ +declare module '@rocket.chat/fuselage-tokens/colors' { + const Colors: { + [key: string]: string; + }; + export = Colors; +} diff --git a/client/types/fuselage.d.ts b/client/types/fuselage.d.ts index ca7c6f6a28b70..eb8063148d83f 100644 --- a/client/types/fuselage.d.ts +++ b/client/types/fuselage.d.ts @@ -1,12 +1,3 @@ -declare module '@rocket.chat/fuselage-tokens/colors' { - type ColorsType = { - [key: string]: string; - }; - - const Colors: ColorsType; - export default Colors; -} - declare module '@rocket.chat/fuselage' { import { css } from '@rocket.chat/css-in-js'; import { Placements } from '@rocket.chat/fuselage-hooks'; @@ -413,5 +404,4 @@ declare module '@rocket.chat/fuselage' { } export const Badge: ForwardRefExoticComponent; - } diff --git a/client/types/kadira-blaze-layout.d.ts b/client/types/kadira-blaze-layout.d.ts new file mode 100644 index 0000000000000..99d57115ddf64 --- /dev/null +++ b/client/types/kadira-blaze-layout.d.ts @@ -0,0 +1,6 @@ +declare module 'meteor/kadira:blaze-layout' { + namespace BlazeLayout { + function reset(): void; + function render(template: string, regions: { [region: string]: string }): void; + } +} diff --git a/client/types/meteor-htmljs.d.ts b/client/types/meteor-htmljs.d.ts new file mode 100644 index 0000000000000..a196507a60cc6 --- /dev/null +++ b/client/types/meteor-htmljs.d.ts @@ -0,0 +1,5 @@ +declare module 'meteor/htmljs' { + namespace HTML { + function DIV(): unknown; + } +} diff --git a/client/types/meteor-mongo.d.ts b/client/types/meteor-mongo.d.ts new file mode 100644 index 0000000000000..33e8f898a8918 --- /dev/null +++ b/client/types/meteor-mongo.d.ts @@ -0,0 +1,12 @@ +declare module 'meteor/mongo' { + namespace Mongo { + // eslint-disable-next-line @typescript-eslint/interface-name-prefix + interface CollectionStatic { + new (name: string | null, options?: { + connection?: object | null; + idGeneration?: string; + transform?: Function | null; + }): Collection; + } + } +} diff --git a/client/types/meteor-tracker.d.ts b/client/types/meteor-tracker.d.ts new file mode 100644 index 0000000000000..94d2162d6a8a6 --- /dev/null +++ b/client/types/meteor-tracker.d.ts @@ -0,0 +1,5 @@ +declare module 'meteor/tracker' { + namespace Tracker { + function nonreactive(func: () => T): T; + } +} diff --git a/client/types/meteor.d.ts b/client/types/meteor.d.ts index 82c8aea70c9a0..2a5a731fee5e0 100644 --- a/client/types/meteor.d.ts +++ b/client/types/meteor.d.ts @@ -1,7 +1,6 @@ -/* eslint-disable @typescript-eslint/camelcase */ -import { EJSON } from 'meteor/ejson'; - declare module 'meteor/meteor' { + import { EJSON } from 'meteor/ejson'; + namespace Meteor { interface IDDPMessage { msg: 'method'; @@ -18,6 +17,7 @@ declare module 'meteor/meteor' { interface IMeteorConnection { _send(message: IDDPMessage): void; + // eslint-disable-next-line @typescript-eslint/camelcase _livedata_data(message: IDDPUpdatedMessage): void; onMessage(message: string): void; @@ -26,22 +26,3 @@ declare module 'meteor/meteor' { const connection: IMeteorConnection; } } - -declare module 'meteor/tracker' { - namespace Tracker { - function nonreactive(func: () => T): T; - } -} - -declare module 'meteor/mongo' { - namespace Mongo { - // eslint-disable-next-line @typescript-eslint/interface-name-prefix - interface CollectionStatic { - new (name: string | null, options?: { - connection?: object | null; - idGeneration?: string; - transform?: Function | null; - }): Collection; - } - } -} From 885faa0584481f3fdbe3c18f057dd14dc56199e7 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Thu, 25 Mar 2021 01:18:10 -0300 Subject: [PATCH 006/124] Migrate routes module to TypeScript --- app/otr/client/rocketchat.otr.room.js | 4 +- app/search/client/provider/result.js | 3 +- app/ui-utils/client/lib/messageContext.js | 3 +- .../views/app/lib/getCommonRoomEvents.js | 3 +- app/webrtc/client/WebRTCClass.js | 6 +-- client/lib/goToRoomById.ts | 23 +++++++++ client/{routes.js => routes.ts} | 50 +++++++------------ client/sidebar/header/CreateChannel.js | 4 +- client/types/kadira-blaze-layout.d.ts | 3 +- client/types/kadira-flow-router.d.ts | 3 +- .../room/contextualBar/Discussions/index.js | 4 +- .../CreateTeamModal/CreateTeamModal.tsx | 4 +- definition/IUser.ts | 1 + package-lock.json | 16 ++++-- package.json | 1 + 15 files changed, 75 insertions(+), 53 deletions(-) create mode 100644 client/lib/goToRoomById.ts rename client/{routes.js => routes.ts} (75%) diff --git a/app/otr/client/rocketchat.otr.room.js b/app/otr/client/rocketchat.otr.room.js index 221d6a9d0de5c..b887082210e14 100644 --- a/app/otr/client/rocketchat.otr.room.js +++ b/app/otr/client/rocketchat.otr.room.js @@ -3,7 +3,6 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { Random } from 'meteor/random'; import { EJSON } from 'meteor/ejson'; import { Tracker } from 'meteor/tracker'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { TimeSync } from 'meteor/mizzao:timesync'; import _ from 'underscore'; @@ -14,6 +13,7 @@ import { Notifications } from '../../notifications'; import { modal } from '../../ui-utils'; import { getUidDirectMessage } from '../../ui-utils/client/lib/getUidDirectMessage'; import { Presence } from '../../../client/lib/presence'; +import { goToRoomById } from '../../../client/lib/goToRoomById'; OTR.Room = class { constructor(userId, roomId) { @@ -189,7 +189,7 @@ OTR.Room = class { this.generateKeyPair().then(() => { this.importPublicKey(data.publicKey).then(() => { this.firstPeer = false; - FlowRouter.goToRoomById(data.roomId); + goToRoomById(data.roomId); Meteor.defer(() => { this.established.set(true); this.acknowledge(); diff --git a/app/search/client/provider/result.js b/app/search/client/provider/result.js index 00a6eaee78a30..ff2cc972bf174 100644 --- a/app/search/client/provider/result.js +++ b/app/search/client/provider/result.js @@ -11,6 +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'; Meteor.startup(function() { MessageAction.addButton({ @@ -35,7 +36,7 @@ Meteor.startup(function() { return RoomHistoryManager.getSurroundingMessages(message, 50); } - FlowRouter.goToRoomById(message.rid); + goToRoomById(message.rid); // RocketChat.MessageAction.hideDropDown(); if (window.matchMedia('(max-width: 500px)').matches) { diff --git a/app/ui-utils/client/lib/messageContext.js b/app/ui-utils/client/lib/messageContext.js index 283cddf5bb9e0..62342c8d93b66 100644 --- a/app/ui-utils/client/lib/messageContext.js +++ b/app/ui-utils/client/lib/messageContext.js @@ -11,6 +11,7 @@ import { AutoTranslate } from '../../../autotranslate/client'; import { Layout } from './Layout'; import { fireGlobalEvent } from './fireGlobalEvent'; import { actionLinks } from '../../../action-links/client'; +import { goToRoomById } from '../../../../client/lib/goToRoomById'; const fields = { name: 1, username: 1, 'settings.preferences.showMessageInMainThread': 1, 'settings.preferences.autoImageLoad': 1, 'settings.preferences.saveMobileBandwidth': 1, 'settings.preferences.collapseMediaByDefault': 1, 'settings.preferences.hideRoles': 1 }; @@ -51,7 +52,7 @@ export function messageContext({ rid } = Template.instance()) { const openDiscussion = (e) => { e.preventDefault(); const { drid } = e.currentTarget.dataset; - FlowRouter.goToRoomById(drid); + goToRoomById(drid); }; const replyBroadcast = (e) => { diff --git a/app/ui/client/views/app/lib/getCommonRoomEvents.js b/app/ui/client/views/app/lib/getCommonRoomEvents.js index 3a28c14094aa4..c273067e36ee1 100644 --- a/app/ui/client/views/app/lib/getCommonRoomEvents.js +++ b/app/ui/client/views/app/lib/getCommonRoomEvents.js @@ -21,6 +21,7 @@ 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'; const mountPopover = (e, i, outerContext) => { let context = $(e.target).parents('.message').data('context'); @@ -313,7 +314,7 @@ export const getCommonRoomEvents = () => ({ if (Layout.isEmbedded()) { fireGlobalEvent('click-mention-link', { path: FlowRouter.path('channel', { name: channel }), channel }); } - FlowRouter.goToRoomById(channel); + goToRoomById(channel); return; } diff --git a/app/webrtc/client/WebRTCClass.js b/app/webrtc/client/WebRTCClass.js index 53eed4791b655..67610a6c0b655 100644 --- a/app/webrtc/client/WebRTCClass.js +++ b/app/webrtc/client/WebRTCClass.js @@ -2,7 +2,6 @@ import { Emitter } from '@rocket.chat/emitter'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import { ReactiveVar } from 'meteor/reactive-var'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { ChromeScreenShare } from './screenShare'; @@ -12,6 +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'; class WebRTCTransportClass extends Emitter { constructor(webrtcInstance) { @@ -663,7 +663,7 @@ class WebRTCClass { onRemoteCall(data) { if (this.autoAccept === true) { - FlowRouter.goToRoomById(data.room); + goToRoomById(data.room); Meteor.defer(() => { this.joinCall({ to: data.from, @@ -712,7 +712,7 @@ class WebRTCClass { cancelButtonText: t('No'), }, (isConfirm) => { if (isConfirm) { - FlowRouter.goToRoomById(data.room); + goToRoomById(data.room); return this.joinCall({ to: data.from, monitor: data.monitor, diff --git a/client/lib/goToRoomById.ts b/client/lib/goToRoomById.ts new file mode 100644 index 0000000000000..dbcd534aaf7c4 --- /dev/null +++ b/client/lib/goToRoomById.ts @@ -0,0 +1,23 @@ +import { memoize } from '@rocket.chat/memo'; +import { FlowRouter } from 'meteor/kadira:flow-router'; + +import { ChatSubscription } from '../../app/models/client'; +import { roomTypes } from '../../app/utils/client'; +import { call } from '../../app/ui-utils/client'; +import { IRoom } from '../../definition/IRoom'; + +const getRoomById = memoize((rid: IRoom['_id']) => call('getRoomById', rid)); + +export const goToRoomById = async (rid: IRoom['_id']): Promise => { + if (!rid) { + return; + } + const subscription = ChatSubscription.findOne({ rid }); + if (subscription) { + roomTypes.openRouteLink(subscription.t, subscription, FlowRouter.current().queryParams); + return; + } + + const room = await getRoomById(rid); + roomTypes.openRouteLink(room.t, room, FlowRouter.current().queryParams); +}; diff --git a/client/routes.js b/client/routes.ts similarity index 75% rename from client/routes.js rename to client/routes.ts index 820864f9edbe7..712df9497ee35 100644 --- a/client/routes.js +++ b/client/routes.ts @@ -1,4 +1,3 @@ -import mem from 'mem'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import { FlowRouter } from 'meteor/kadira:flow-router'; @@ -6,27 +5,10 @@ import { BlazeLayout } from 'meteor/kadira:blaze-layout'; import { Session } from 'meteor/session'; import toastr from 'toastr'; -import { KonchatNotification } from '../app/ui'; -import { ChatSubscription } from '../app/models'; -import { roomTypes, handleError } from '../app/utils'; -import { call } from '../app/ui-utils'; +import { KonchatNotification } from '../app/ui/client'; +import { handleError } from '../app/utils/client'; import { renderRouteComponent } from './reactAdapters'; - -const getRoomById = mem((rid) => call('getRoomById', rid)); - -FlowRouter.goToRoomById = async (rid) => { - if (!rid) { - return; - } - const subscription = ChatSubscription.findOne({ rid }); - if (subscription) { - return roomTypes.openRouteLink(subscription.t, subscription, FlowRouter.current().queryParams); - } - - const room = await getRoomById(rid); - return roomTypes.openRouteLink(room.t, room, FlowRouter.current().queryParams); -}; - +import { IUser } from '../definition/IUser'; BlazeLayout.setRoot('body'); @@ -40,11 +22,12 @@ FlowRouter.route('/', { return FlowRouter.go('home'); } - Tracker.autorun(function(c) { + Tracker.autorun((c) => { if (FlowRouter.subsReady() === true) { - Meteor.defer(function() { - if (Meteor.user() && Meteor.user().defaultRoom) { - const room = Meteor.user().defaultRoom.split('/'); + Meteor.defer(() => { + const user = Meteor.user() as IUser | null; + if (user?.defaultRoom) { + const room = user.defaultRoom.split('/'); FlowRouter.go(room[0], { name: room[1] }, FlowRouter.current().queryParams); } else { FlowRouter.go('home'); @@ -67,14 +50,15 @@ FlowRouter.route('/login', { FlowRouter.route('/home', { name: 'home', - action(params, queryParams) { + action(_params, queryParams) { KonchatNotification.getDesktopPermission(); - if (queryParams.saml_idp_credentialToken !== undefined) { + if (queryParams?.saml_idp_credentialToken !== undefined) { const token = queryParams.saml_idp_credentialToken; FlowRouter.setQueryParams({ + // eslint-disable-next-line @typescript-eslint/camelcase saml_idp_credentialToken: null, }); - Meteor.loginWithSamlToken(token, (error) => { + (Meteor as any).loginWithSamlToken(token, (error?: any) => { if (error) { if (error.reason) { toastr.error(error.reason); @@ -98,7 +82,7 @@ FlowRouter.route('/directory/:tab?', { action: () => { renderRouteComponent(() => import('./views/directory/DirectoryPage'), { template: 'main', region: 'center' }); }, - triggersExit: [function() { + triggersExit: [(): void => { $('.main-content').addClass('rc-old'); }], }); @@ -108,7 +92,7 @@ FlowRouter.route('/omnichannel-directory/:tab?/:context?/:id?', { action: () => { renderRouteComponent(() => import('./omnichannel/directory/OmnichannelDirectoryPage'), { template: 'main', region: 'center' }); }, - triggersExit: [function() { + triggersExit: [(): void => { $('.main-content').addClass('rc-old'); }], }); @@ -118,7 +102,7 @@ FlowRouter.route('/account/:group?', { action: () => { renderRouteComponent(() => import('./views/account/AccountRoute'), { template: 'main', region: 'center' }); }, - triggersExit: [function() { + triggersExit: [(): void => { $('.main-content').addClass('rc-old'); }], }); @@ -149,7 +133,7 @@ FlowRouter.route('/legal-notice', { FlowRouter.route('/room-not-found/:type/:name', { name: 'room-not-found', - action: ({ type, name }) => { + action: ({ type, name } = {}) => { Session.set('roomNotFound', { type, name }); BlazeLayout.render('main', { center: 'roomNotFound' }); }, @@ -177,7 +161,7 @@ FlowRouter.route('/setup-wizard/:step?', { }); FlowRouter.notFound = { - action: () => { + action: (): void => { renderRouteComponent(() => import('./views/notFound/NotFoundPage')); }, }; diff --git a/client/sidebar/header/CreateChannel.js b/client/sidebar/header/CreateChannel.js index 97d8bcac1f096..819a0d20d554b 100644 --- a/client/sidebar/header/CreateChannel.js +++ b/client/sidebar/header/CreateChannel.js @@ -1,4 +1,3 @@ -import { FlowRouter } from 'meteor/kadira:flow-router'; import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { useMutableCallback, useDebouncedCallback } from '@rocket.chat/fuselage-hooks'; import { Box, Modal, ButtonGroup, Button, TextInput, Icon, Field, ToggleSwitch } from '@rocket.chat/fuselage'; @@ -10,6 +9,7 @@ import UserAutoCompleteMultiple from '../../../ee/client/audit/UserAutoCompleteM import { useSetting } from '../../contexts/SettingsContext'; import { usePermission } from '../../contexts/AuthorizationContext'; import { useMethod } from '../../contexts/ServerContext'; +import { goToRoomById } from '../../lib/goToRoomById'; export const CreateChannel = ({ values, @@ -198,7 +198,7 @@ export default memo(({ const onCreate = useCallback(async () => { const goToRoom = (rid) => { - FlowRouter.goToRoomById(rid); + goToRoomById(rid); }; const params = { diff --git a/client/types/kadira-blaze-layout.d.ts b/client/types/kadira-blaze-layout.d.ts index 99d57115ddf64..04c0bda98b3e9 100644 --- a/client/types/kadira-blaze-layout.d.ts +++ b/client/types/kadira-blaze-layout.d.ts @@ -1,6 +1,7 @@ declare module 'meteor/kadira:blaze-layout' { namespace BlazeLayout { function reset(): void; - function render(template: string, regions: { [region: string]: string }): void; + function render(template: string, regions?: { [region: string]: string }): void; + function setRoot(selector: string): void; } } diff --git a/client/types/kadira-flow-router.d.ts b/client/types/kadira-flow-router.d.ts index 44baa5d3ee2f4..7bcd5abd811e5 100644 --- a/client/types/kadira-flow-router.d.ts +++ b/client/types/kadira-flow-router.d.ts @@ -124,7 +124,7 @@ declare module 'meteor/kadira:flow-router' { wait(): void; - notFound: Route | null; + notFound: Omit; getRouteName(): string; @@ -138,6 +138,5 @@ declare module 'meteor/kadira:flow-router' { export const FlowRouter: Router & { Route: typeof Route; Router: typeof Router; - goToRoomById: (rid: unknown) => void; }; } diff --git a/client/views/room/contextualBar/Discussions/index.js b/client/views/room/contextualBar/Discussions/index.js index 3475ab15bcce3..b3a1739c9e069 100644 --- a/client/views/room/contextualBar/Discussions/index.js +++ b/client/views/room/contextualBar/Discussions/index.js @@ -1,4 +1,3 @@ -import { FlowRouter } from 'meteor/kadira:flow-router'; import React, { useCallback, useMemo, useState, memo } from 'react'; import { Box, Icon, TextInput, Callout } from '@rocket.chat/fuselage'; import { Virtuoso } from 'react-virtuoso'; @@ -19,6 +18,7 @@ import { renderMessageBody } from '../../../../lib/renderMessageBody'; import ScrollableContentWrapper from '../../../../components/ScrollableContentWrapper'; import { useDiscussionsList } from './useDiscussionsList'; import { useRecordList } from '../../../../hooks/lists/useRecordList'; +import { goToRoomById } from '../../../../lib/goToRoomById'; function mapProps(WrappedComponent) { return ({ msg, username, tcount, ts, ...props }) => ; @@ -129,7 +129,7 @@ export function DiscussionList({ total = 10, discussions = [], loadMoreItems, lo const inputRef = useAutoFocus(true); const onClick = useCallback((e) => { const { drid } = e.currentTarget.dataset; - FlowRouter.goToRoomById(drid); + goToRoomById(drid); }, []); const { ref, diff --git a/client/views/teams/modals/CreateTeamModal/CreateTeamModal.tsx b/client/views/teams/modals/CreateTeamModal/CreateTeamModal.tsx index ea94462f0a827..ecec60ad01a9b 100644 --- a/client/views/teams/modals/CreateTeamModal/CreateTeamModal.tsx +++ b/client/views/teams/modals/CreateTeamModal/CreateTeamModal.tsx @@ -1,4 +1,3 @@ -import { FlowRouter } from 'meteor/kadira:flow-router'; import React, { FC, memo, Ref, useCallback, useEffect, useMemo, useState } from 'react'; import { useMutableCallback, useDebouncedCallback, useAutoFocus } from '@rocket.chat/fuselage-hooks'; import { Box, Modal, ButtonGroup, Button, TextInput, Field, ToggleSwitch } from '@rocket.chat/fuselage'; @@ -12,6 +11,7 @@ import { usePermission } from '../../../../contexts/AuthorizationContext'; import { useMethod } from '../../../../contexts/ServerContext'; import TeamNameInput from './TeamNameInput'; import UsersInput from './UsersInput'; +import { goToRoomById } from '../../../../lib/goToRoomById'; type CreateTeamModalState = { name: any; @@ -175,7 +175,7 @@ const useCreateTeamModalState = (onClose: () => void): CreateTeamModalState => { const data = await createTeam(params); - FlowRouter.goToRoomById(data.team.roomId); + goToRoomById(data.team.roomId); onClose(); }, [name, members, type, readOnly, description, broadcast, encrypted, createTeam, onClose]); diff --git a/definition/IUser.ts b/definition/IUser.ts index 73072e24de784..eb242b60970b4 100644 --- a/definition/IUser.ts +++ b/definition/IUser.ts @@ -118,6 +118,7 @@ export interface IUser { [key: string]: any; }; settings?: IUserSettings; + defaultRoom?: string; } export type IUserDataEvent = { diff --git a/package-lock.json b/package-lock.json index f5b59b6a42e40..c1fc4a253e960 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5936,6 +5936,11 @@ "stylis": "^4.0.6" }, "dependencies": { + "@rocket.chat/memo": { + "version": "0.6.3-dev.180", + "resolved": "https://registry.npmjs.org/@rocket.chat/memo/-/memo-0.6.3-dev.180.tgz", + "integrity": "sha512-3+zoOZW/f2/cwmjDAh8yXvGQvskopSR/QCAUmatqSAiMztFvkBljlVbSx1YUiq4y5t41dzM5m+MKYRZAEgAtLQ==" + }, "stylis": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.7.tgz", @@ -5991,6 +5996,11 @@ "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-tokens/-/fuselage-tokens-0.22.0.tgz", "integrity": "sha512-FhkZFE/HRjBTUG81TNPwILoJQZihyvhKT/XF2Zo8FxBQeDYXpMdyU/scesw8PpNZFTFEjMT7WEu5+QWVkVoq1A==" }, + "@rocket.chat/memo": { + "version": "0.6.3-dev.180", + "resolved": "https://registry.npmjs.org/@rocket.chat/memo/-/memo-0.6.3-dev.180.tgz", + "integrity": "sha512-3+zoOZW/f2/cwmjDAh8yXvGQvskopSR/QCAUmatqSAiMztFvkBljlVbSx1YUiq4y5t41dzM5m+MKYRZAEgAtLQ==" + }, "stylis": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.7.tgz", @@ -6113,9 +6123,9 @@ } }, "@rocket.chat/memo": { - "version": "0.6.3-dev.180", - "resolved": "https://registry.npmjs.org/@rocket.chat/memo/-/memo-0.6.3-dev.180.tgz", - "integrity": "sha512-3+zoOZW/f2/cwmjDAh8yXvGQvskopSR/QCAUmatqSAiMztFvkBljlVbSx1YUiq4y5t41dzM5m+MKYRZAEgAtLQ==" + "version": "0.6.3-dev.205", + "resolved": "https://registry.npmjs.org/@rocket.chat/memo/-/memo-0.6.3-dev.205.tgz", + "integrity": "sha512-xrwGKR2kn3VNMBw4BFHDeoU0bxF3EClxrKWKzsvmELx+qo2x4hk7HXZyZ2Z8IdmSWJS/yNCq0jv6T6A2k8KnqQ==" }, "@rocket.chat/mp3-encoder": { "version": "0.6.3-dev.178", diff --git a/package.json b/package.json index ac28a4ecfb1fc..aea525b7e6a16 100644 --- a/package.json +++ b/package.json @@ -145,6 +145,7 @@ "@rocket.chat/fuselage-polyfills": "^0.6.3-dev.181", "@rocket.chat/fuselage-tokens": "^0.21.0", "@rocket.chat/fuselage-ui-kit": "^0.6.3-dev.184", + "@rocket.chat/memo": "^0.6.3-dev.205", "@rocket.chat/mp3-encoder": "^0.6.3-dev.178", "@rocket.chat/ui-kit": "^0.6.3-dev.178", "@slack/client": "^4.12.0", From fce79b414c6db9746e508c2336748656eb47a26a Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Thu, 25 Mar 2021 02:24:26 -0300 Subject: [PATCH 007/124] Move more initialization modules --- app/authorization/client/index.js | 1 - app/authorization/client/usersNameChanged.js | 18 --- client/main.ts | 4 - client/notifications/UsersNameChanged.js | 41 ------- client/notifications/notification.js | 87 ------------- client/notifications/updateAvatar.js | 10 -- client/notifications/updateUserState.js | 11 -- client/startup/index.ts | 4 +- client/startup/notifications/index.ts | 3 + .../notifications/konchatNotifications.ts | 115 ++++++++++++++++++ client/startup/notifications/updateAvatar.ts | 16 +++ .../startup/notifications/usersNameChanged.ts | 54 ++++++++ client/startup/userStatusManuallySet.ts | 12 ++ 13 files changed, 203 insertions(+), 173 deletions(-) delete mode 100644 app/authorization/client/usersNameChanged.js delete mode 100644 client/notifications/UsersNameChanged.js delete mode 100644 client/notifications/notification.js delete mode 100644 client/notifications/updateAvatar.js delete mode 100644 client/notifications/updateUserState.js create mode 100644 client/startup/notifications/index.ts create mode 100644 client/startup/notifications/konchatNotifications.ts create mode 100644 client/startup/notifications/updateAvatar.ts create mode 100644 client/startup/notifications/usersNameChanged.ts create mode 100644 client/startup/userStatusManuallySet.ts diff --git a/app/authorization/client/index.js b/app/authorization/client/index.js index bf484b064551a..04692ed0749f2 100644 --- a/app/authorization/client/index.js +++ b/app/authorization/client/index.js @@ -1,7 +1,6 @@ import { hasAllPermission, hasAtLeastOnePermission, hasPermission, userHasAllPermission } from './hasPermission'; import { hasRole } from './hasRole'; import { AuthorizationUtils } from '../lib/AuthorizationUtils'; -import './usersNameChanged'; import './requiresPermission.html'; import './startup'; diff --git a/app/authorization/client/usersNameChanged.js b/app/authorization/client/usersNameChanged.js deleted file mode 100644 index c498ca472b3c1..0000000000000 --- a/app/authorization/client/usersNameChanged.js +++ /dev/null @@ -1,18 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { Notifications } from '../../notifications'; -import { RoomRoles } from '../../models'; - -Meteor.startup(function() { - Notifications.onLogged('Users:NameChanged', function({ _id, name }) { - RoomRoles.update({ - 'u._id': _id, - }, { - $set: { - 'u.name': name, - }, - }, { - multi: true, - }); - }); -}); diff --git a/client/main.ts b/client/main.ts index 9ee80d00c408e..97aa0fce2c521 100644 --- a/client/main.ts +++ b/client/main.ts @@ -12,10 +12,6 @@ import './methods/openRoom'; import './methods/setUserActiveStatus'; import './methods/toggleFavorite'; import './methods/updateMessage'; -import './notifications/notification'; -import './notifications/updateAvatar'; -import './notifications/updateUserState'; -import './notifications/UsersNameChanged'; import './routes'; import './startup'; import './views/admin'; diff --git a/client/notifications/UsersNameChanged.js b/client/notifications/UsersNameChanged.js deleted file mode 100644 index f7308efdfcc66..0000000000000 --- a/client/notifications/UsersNameChanged.js +++ /dev/null @@ -1,41 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { Notifications } from '../../app/notifications'; -import { Messages, Subscriptions } from '../../app/models'; - -Meteor.startup(function() { - Notifications.onLogged('Users:NameChanged', function({ _id, name, username }) { - Messages.update({ - 'u._id': _id, - }, { - $set: { - 'u.username': username, - 'u.name': name, - }, - }, { - multi: true, - }); - - Messages.update({ - mentions: { - $elemMatch: { _id }, - }, - }, { - $set: { - 'mentions.$.username': username, - 'mentions.$.name': name, - }, - }, { - multi: true, - }); - - Subscriptions.update({ - name: username, - t: 'd', - }, { - $set: { - fname: name, - }, - }); - }); -}); diff --git a/client/notifications/notification.js b/client/notifications/notification.js deleted file mode 100644 index ca374de481653..0000000000000 --- a/client/notifications/notification.js +++ /dev/null @@ -1,87 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; -import { FlowRouter } from 'meteor/kadira:flow-router'; -import { Session } from 'meteor/session'; - -import { KonchatNotification } from '../../app/ui'; -import { CachedChatSubscription } from '../../app/models'; -import { fireGlobalEvent, readMessage, Layout } from '../../app/ui-utils'; -import { getUserPreference } from '../../app/utils'; -import { Notifications } from '../../app/notifications'; - -// Show notifications and play a sound for new messages. -// We trust the server to only send notifications for interesting messages, e.g. direct messages or -// group messages in which the user is mentioned. - -function notifyNewRoom(sub) { - // Do not play new room sound if user is busy - if (Session.equals(`user_${ Meteor.userId() }_status`, 'busy')) { - return; - } - - if ((!FlowRouter.getParam('name') || FlowRouter.getParam('name') !== sub.name) && !sub.ls && sub.alert === true) { - return KonchatNotification.newRoom(sub.rid); - } -} - -Meteor.startup(function() { - Tracker.autorun(function() { - if (Meteor.userId()) { - Notifications.onUser('notification', function(notification) { - let openedRoomId = undefined; - if (['channel', 'group', 'direct'].includes(FlowRouter.getRouteName())) { - openedRoomId = Session.get('openedRoom'); - } - - // This logic is duplicated in /client/startup/unread.coffee. - const hasFocus = readMessage.isEnable(); - const messageIsInOpenedRoom = openedRoomId === notification.payload.rid; - - fireGlobalEvent('notification', { - notification, - fromOpenedRoom: messageIsInOpenedRoom, - hasFocus, - }); - - if (Layout.isEmbedded()) { - if (!hasFocus && messageIsInOpenedRoom) { - // Show a notification. - KonchatNotification.showDesktop(notification); - } - } else if (!hasFocus || !messageIsInOpenedRoom) { - // Show a notification. - KonchatNotification.showDesktop(notification); - } - }); - - Notifications.onUser('audioNotification', function(notification) { - const openedRoomId = Session.get('openedRoom'); - - // This logic is duplicated in /client/startup/unread.coffee. - const hasFocus = readMessage.isEnable(); - const messageIsInOpenedRoom = openedRoomId === notification.payload.rid; - const muteFocusedConversations = getUserPreference(Meteor.userId(), 'muteFocusedConversations'); - - if (Layout.isEmbedded()) { - if (!hasFocus && messageIsInOpenedRoom) { - // Play a notification sound - KonchatNotification.newMessage(notification.payload.rid); - } - } else if (!hasFocus || !messageIsInOpenedRoom || !muteFocusedConversations) { - // Play a notification sound - KonchatNotification.newMessage(notification.payload.rid); - } - }); - - CachedChatSubscription.onSyncData = function(action, sub) { - if (action !== 'removed') { - notifyNewRoom(sub); - } - }; - - Notifications.onUser('subscriptions-changed', (action, sub) => { - notifyNewRoom(sub); - }); - } - }); -}); diff --git a/client/notifications/updateAvatar.js b/client/notifications/updateAvatar.js deleted file mode 100644 index 3eb537117adf7..0000000000000 --- a/client/notifications/updateAvatar.js +++ /dev/null @@ -1,10 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { Notifications } from '../../app/notifications'; - -Meteor.startup(function() { - Notifications.onLogged('updateAvatar', function(data) { - const { username, etag } = data; - username && Meteor.users.update({ username }, { $set: { avatarETag: etag } }); - }); -}); diff --git a/client/notifications/updateUserState.js b/client/notifications/updateUserState.js deleted file mode 100644 index faaaa30fb7db0..0000000000000 --- a/client/notifications/updateUserState.js +++ /dev/null @@ -1,11 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { fireGlobalEvent } from '../../app/ui-utils'; -import { callbacks } from '../../app/callbacks'; - -/* fire user state change globally, to listen on desktop electron client */ -Meteor.startup(function() { - callbacks.add('userStatusManuallySet', (status) => { - fireGlobalEvent('user-status-manually-set', status); - }); -}); diff --git a/client/startup/index.ts b/client/startup/index.ts index 5b33c654ab615..b6fef65cbfa39 100644 --- a/client/startup/index.ts +++ b/client/startup/index.ts @@ -1,8 +1,10 @@ import './banners'; import './contextualBar'; +import './e2e'; import './emailVerification'; import './i18n'; import './loginViaQuery'; +import './notifications'; import './renderMessage'; import './renderNotification'; import './roomObserve'; @@ -12,4 +14,4 @@ import './theme'; import './unread'; import './userSetUtcOffset'; import './usersObserve'; -import './e2e'; +import './userStatusManuallySet'; diff --git a/client/startup/notifications/index.ts b/client/startup/notifications/index.ts new file mode 100644 index 0000000000000..07829fee7dde9 --- /dev/null +++ b/client/startup/notifications/index.ts @@ -0,0 +1,3 @@ +import './konchatNotifications'; +import './updateAvatar'; +import './usersNameChanged'; diff --git a/client/startup/notifications/konchatNotifications.ts b/client/startup/notifications/konchatNotifications.ts new file mode 100644 index 0000000000000..0bf4975a48f91 --- /dev/null +++ b/client/startup/notifications/konchatNotifications.ts @@ -0,0 +1,115 @@ +import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; +import { FlowRouter } from 'meteor/kadira:flow-router'; +import { Session } from 'meteor/session'; + +import { KonchatNotification } from '../../../app/ui/client'; +import { CachedChatSubscription } from '../../../app/models/client'; +import { fireGlobalEvent, readMessage, Layout } from '../../../app/ui-utils/client'; +import { getUserPreference } from '../../../app/utils/client'; +import { Notifications } from '../../../app/notifications/client'; +import { ISubscription } from '../../../definition/ISubscription'; +import { IMessage } from '../../../definition/IMessage'; +import { IRoom } from '../../../definition/IRoom'; + +const notifyNewRoom = (sub: ISubscription): void => { + if (Session.equals(`user_${ Meteor.userId() }_status`, 'busy')) { + return; + } + + if ((!FlowRouter.getParam('name') || FlowRouter.getParam('name') !== sub.name) && !sub.ls && sub.alert === true) { + KonchatNotification.newRoom(sub.rid); + } +}; + +type NotificationEvent = { + title: string; + text: string; + duration: number; + payload: { + _id: IMessage['_id']; + rid: IMessage['rid']; + tmid: IMessage['_id']; + sender: IMessage['u']; + type: IRoom['t']; + name: IRoom['name']; + message: { + msg: IMessage['msg']; + t: string; + }; + }; +}; + +type AudioNotificationEvent = { + payload: { + _id: IMessage['_id']; + rid: IMessage['rid']; + sender: IMessage['u']; + type: IRoom['t']; + name: IRoom['name']; + }; +}; + +Meteor.startup(() => { + Tracker.autorun(() => { + if (!Meteor.userId()) { + return; + } + + Notifications.onUser('notification', (notification: NotificationEvent) => { + let openedRoomId = undefined; + if (['channel', 'group', 'direct'].includes(FlowRouter.getRouteName())) { + openedRoomId = Session.get('openedRoom'); + } + + // This logic is duplicated in /client/startup/unread.coffee. + const hasFocus = readMessage.isEnable(); + const messageIsInOpenedRoom = openedRoomId === notification.payload.rid; + + fireGlobalEvent('notification', { + notification, + fromOpenedRoom: messageIsInOpenedRoom, + hasFocus, + }); + + if (Layout.isEmbedded()) { + if (!hasFocus && messageIsInOpenedRoom) { + // Show a notification. + KonchatNotification.showDesktop(notification); + } + } else if (!hasFocus || !messageIsInOpenedRoom) { + // Show a notification. + KonchatNotification.showDesktop(notification); + } + }); + + Notifications.onUser('audioNotification', (notification: AudioNotificationEvent) => { + const openedRoomId = Session.get('openedRoom'); + + // This logic is duplicated in /client/startup/unread.coffee. + const hasFocus = readMessage.isEnable(); + const messageIsInOpenedRoom = openedRoomId === notification.payload.rid; + const muteFocusedConversations = getUserPreference(Meteor.userId(), 'muteFocusedConversations'); + + if (Layout.isEmbedded()) { + if (!hasFocus && messageIsInOpenedRoom) { + // Play a notification sound + KonchatNotification.newMessage(notification.payload.rid); + } + } else if (!hasFocus || !messageIsInOpenedRoom || !muteFocusedConversations) { + // Play a notification sound + KonchatNotification.newMessage(notification.payload.rid); + } + }); + + CachedChatSubscription.onSyncData = ((action: 'changed' | 'removed', sub: ISubscription): void => { + if (action !== 'removed') { + notifyNewRoom(sub); + } + }) as () => void; + + Notifications.onUser('subscriptions-changed', (_action: 'changed' | 'removed', sub: ISubscription) => { + notifyNewRoom(sub); + }); + }); +}); diff --git a/client/startup/notifications/updateAvatar.ts b/client/startup/notifications/updateAvatar.ts new file mode 100644 index 0000000000000..ef587fc8c2513 --- /dev/null +++ b/client/startup/notifications/updateAvatar.ts @@ -0,0 +1,16 @@ +import { Meteor } from 'meteor/meteor'; + +import { Notifications } from '../../../app/notifications/client'; +import { IUser } from '../../../definition/IUser'; + +type UpdateAvatarEvent = { + username: IUser['username']; + etag: IUser['avatarETag']; +}; + +Meteor.startup(() => { + Notifications.onLogged('updateAvatar', (data: UpdateAvatarEvent) => { + const { username, etag } = data; + username && Meteor.users.update({ username }, { $set: { avatarETag: etag } }); + }); +}); diff --git a/client/startup/notifications/usersNameChanged.ts b/client/startup/notifications/usersNameChanged.ts new file mode 100644 index 0000000000000..c69442f313ad9 --- /dev/null +++ b/client/startup/notifications/usersNameChanged.ts @@ -0,0 +1,54 @@ +import { Meteor } from 'meteor/meteor'; + +import { Notifications } from '../../../app/notifications/client'; +import { Messages, RoomRoles, Subscriptions } from '../../../app/models/client'; +import { IUser } from '../../../definition/IUser'; + +type UsersNameChangedEvent = Partial; + +Meteor.startup(() => { + Notifications.onLogged('Users:NameChanged', ({ _id, name, username }: UsersNameChangedEvent) => { + Messages.update({ + 'u._id': _id, + }, { + $set: { + 'u.username': username, + 'u.name': name, + }, + }, { + multi: true, + }); + + Messages.update({ + mentions: { + $elemMatch: { _id }, + }, + }, { + $set: { + 'mentions.$.username': username, + 'mentions.$.name': name, + }, + }, { + multi: true, + }); + + Subscriptions.update({ + name: username, + t: 'd', + }, { + $set: { + fname: name, + }, + }); + + RoomRoles.update({ + 'u._id': _id, + }, { + $set: { + 'u.name': name, + }, + }, { + multi: true, + }); + }); +}); diff --git a/client/startup/userStatusManuallySet.ts b/client/startup/userStatusManuallySet.ts new file mode 100644 index 0000000000000..31b1e8c1aa0e4 --- /dev/null +++ b/client/startup/userStatusManuallySet.ts @@ -0,0 +1,12 @@ +import { Meteor } from 'meteor/meteor'; + +import { fireGlobalEvent } from '../../app/ui-utils/client'; +import { callbacks } from '../../app/callbacks/client'; +import { USER_STATUS } from '../../definition/UserStatus'; + +/* fire user state change globally, to listen on desktop electron client */ +Meteor.startup(() => { + callbacks.add('userStatusManuallySet', (status: USER_STATUS) => { + fireGlobalEvent('user-status-manually-set', status); + }); +}); From 5f8b0d4812ce54113009549797e7d45e12cb5adf Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Thu, 25 Mar 2021 19:20:45 -0300 Subject: [PATCH 008/124] Migrate some startup modules to TypeScript --- client/startup/{e2e.js => e2e.ts} | 44 ++++++++++--------- ...ilVerification.js => emailVerification.ts} | 4 +- definition/IMessage.ts | 2 + definition/ISubscription.ts | 2 + 4 files changed, 30 insertions(+), 22 deletions(-) rename client/startup/{e2e.js => e2e.ts} (73%) rename client/startup/{emailVerification.js => emailVerification.ts} (91%) diff --git a/client/startup/e2e.js b/client/startup/e2e.ts similarity index 73% rename from client/startup/e2e.js rename to client/startup/e2e.ts index 617d5980a1c53..6b506aac9caaa 100644 --- a/client/startup/e2e.js +++ b/client/startup/e2e.ts @@ -9,8 +9,11 @@ import { Notifications } from '../../app/notifications/client'; import { e2e } from '../../app/e2e/client/rocketchat.e2e'; import { Subscriptions, Rooms } from '../../app/models/client'; import { waitUntilFind } from '../../app/e2e/client/waitUntilFind'; +import { IMessage } from '../../definition/IMessage'; +import { ISubscription } from '../../definition/ISubscription'; +import { IRoom } from '../../definition/IRoom'; -const handle = async (roomId, keyId) => { +const handle = async (roomId: IRoom['_id'], keyId: string): Promise => { const e2eRoom = await e2e.getInstanceByRoomId(roomId); if (!e2eRoom) { return; @@ -19,22 +22,24 @@ const handle = async (roomId, keyId) => { e2eRoom.provideKeyToUser(keyId); }; -Meteor.startup(function() { - Tracker.autorun(function() { - if (Meteor.userId()) { - const adminEmbedded = Layout.isEmbedded() && FlowRouter.current().path.startsWith('/admin'); - - if (!adminEmbedded && settings.get('E2E_Enable') && window.crypto) { - e2e.startClient(); - e2e.enabled.set(true); - } else { - e2e.enabled.set(false); - e2e.closeAlert(); - } +Meteor.startup(() => { + Tracker.autorun(() => { + if (!Meteor.userId()) { + return; + } + + const adminEmbedded = Layout.isEmbedded() && FlowRouter.current().path.startsWith('/admin'); + + if (!adminEmbedded && settings.get('E2E_Enable') && window.crypto) { + e2e.startClient(); + e2e.enabled.set(true); + } else { + e2e.enabled.set(false); + e2e.closeAlert(); } }); - let observable = null; + let observable: Meteor.LiveQueryHandle | null = null; Tracker.autorun(() => { if (!e2e.isReady()) { promises.remove('onClientMessageReceived', 'e2e-decript-message'); @@ -43,11 +48,10 @@ Meteor.startup(function() { return promises.remove('onClientBeforeSendMessage', 'e2e'); } - Notifications.onUser('e2ekeyRequest', handle); observable = Subscriptions.find().observe({ - changed: async (doc) => { + changed: async (doc: ISubscription) => { if (!doc.encrypted && !doc.E2EKey) { e2e.removeInstanceByRoomId(doc.rid); return; @@ -78,18 +82,18 @@ Meteor.startup(function() { e2eRoom.decryptSubscription(); }, - added: async (doc) => { + added: async (doc: ISubscription) => { if (!doc.encrypted && !doc.E2EKey) { return; } return e2e.getInstanceByRoomId(doc.rid); }, - removed: (doc) => { + removed: (doc: ISubscription) => { e2e.removeInstanceByRoomId(doc.rid); }, }); - promises.add('onClientMessageReceived', async (msg) => { + promises.add('onClientMessageReceived', async (msg: IMessage) => { const e2eRoom = await e2e.getInstanceByRoomId(msg.rid); if (!e2eRoom || !e2eRoom.shouldConvertReceivedMessages()) { return msg; @@ -98,7 +102,7 @@ Meteor.startup(function() { }, promises.priority.HIGH, 'e2e-decript-message'); // Encrypt messages before sending - promises.add('onClientBeforeSendMessage', async function(message) { + promises.add('onClientBeforeSendMessage', async (message: IMessage) => { const e2eRoom = await e2e.getInstanceByRoomId(message.rid); if (!e2eRoom) { diff --git a/client/startup/emailVerification.js b/client/startup/emailVerification.ts similarity index 91% rename from client/startup/emailVerification.js rename to client/startup/emailVerification.ts index e6fca3d8dad1b..cb3a2d3addd6e 100644 --- a/client/startup/emailVerification.js +++ b/client/startup/emailVerification.ts @@ -6,8 +6,8 @@ import toastr from 'toastr'; import { settings } from '../../app/settings/client'; -Meteor.startup(function() { - Tracker.autorun(function() { +Meteor.startup(() => { + Tracker.autorun(() => { const user = Meteor.user(); if (user && user.emails && user.emails[0] && user.emails[0].verified !== true && settings.get('Accounts_EmailVerification') === true && !Session.get('Accounts_EmailVerification_Warning')) { toastr.warning(TAPi18n.__('You_have_not_verified_your_email')); diff --git a/definition/IMessage.ts b/definition/IMessage.ts index f67dfe12d3cef..36e6af8785354 100644 --- a/definition/IMessage.ts +++ b/definition/IMessage.ts @@ -27,4 +27,6 @@ export interface IMessage extends IRocketChatRecord { dcount?: number; tcount?: number; + t?: string; + e2e?: 'pending'; } diff --git a/definition/ISubscription.ts b/definition/ISubscription.ts index 6430eb5bd2214..122bea674aa39 100644 --- a/definition/ISubscription.ts +++ b/definition/ISubscription.ts @@ -35,6 +35,8 @@ export interface ISubscription extends IRocketChatRecord { roles?: string[]; onHold?: boolean; + encrypted?: boolean; + E2EKey?: string; } export interface ISubscriptionDirectMessage extends Omit { From 7e3efdf75092ef319eccf7d629ff23484faf2018 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Fri, 26 Mar 2021 01:28:02 -0300 Subject: [PATCH 009/124] Add custom eslint config to startup and Prettier --- .eslintignore | 1 + client/startup/.eslintrc.js | 120 ++++++++++++++++++ client/startup/.prettierrc | 12 ++ client/startup/banners.ts | 12 +- .../startup/contextualBar/exportMessages.ts | 30 +++-- client/startup/e2e.ts | 76 ++++++----- client/startup/emailVerification.ts | 13 +- client/startup/i18n.js | 48 ++++--- client/startup/loginViaQuery.ts | 2 +- .../notifications/konchatNotifications.ts | 75 +++++++---- client/startup/notifications/updateAvatar.ts | 3 +- .../startup/notifications/usersNameChanged.ts | 102 +++++++++------ client/startup/renderMessage/autolinker.ts | 19 ++- client/startup/renderMessage/autotranslate.ts | 24 ++-- client/startup/renderMessage/emoji.ts | 19 ++- client/startup/renderMessage/googlevision.ts | 19 ++- client/startup/renderMessage/hexcolor.ts | 19 ++- .../startup/renderMessage/highlightWords.ts | 25 +++- client/startup/renderMessage/issuelink.ts | 19 ++- client/startup/renderMessage/katex.ts | 17 ++- client/startup/renderMessage/markdown.ts | 17 ++- .../startup/renderMessage/mentionsMessage.ts | 22 +++- client/startup/renderNotification/markdown.ts | 17 ++- client/startup/roomObserve.ts | 8 +- client/startup/startup.js | 19 ++- client/startup/streamMessage/autotranslate.ts | 24 ++-- client/startup/streamMessage/googlevision.ts | 19 ++- client/startup/theme.js | 56 +++++--- client/startup/unread.js | 85 ++++++++----- client/startup/userSetUtcOffset.ts | 5 +- client/startup/userStatusManuallySet.ts | 2 +- client/startup/usersObserve.ts | 51 +++++--- package-lock.json | 48 ++++++- package.json | 3 + 34 files changed, 721 insertions(+), 310 deletions(-) create mode 100644 client/startup/.eslintrc.js create mode 100644 client/startup/.prettierrc diff --git a/.eslintignore b/.eslintignore index 5831f3fab824f..e17cbcb3ce4a1 100644 --- a/.eslintignore +++ b/.eslintignore @@ -18,3 +18,4 @@ imports/client/ !/.storybook/ ee/server/services/dist/** !/.mocharc.js +!/client/startup/.eslintrc.js diff --git a/client/startup/.eslintrc.js b/client/startup/.eslintrc.js new file mode 100644 index 0000000000000..3b8b64ca4499d --- /dev/null +++ b/client/startup/.eslintrc.js @@ -0,0 +1,120 @@ +module.exports = { + root: true, + extends: ['@rocket.chat/eslint-config', 'prettier'], + parser: 'babel-eslint', + plugins: ['react', 'react-hooks', 'prettier'], + rules: { + 'import/order': [ + 'error', + { + 'newlines-between': 'always', + 'groups': [ + 'builtin', + 'external', + 'internal', + ['parent', 'sibling', 'index'], + ], + 'alphabetize': { + order: 'asc', + }, + }, + ], + 'jsx-quotes': ['error', 'prefer-single'], + 'prefer-arrow-callback': ['error', { allowNamedFunctions: true }], + 'prettier/prettier': 2, + 'react/jsx-uses-react': 'error', + 'react/jsx-uses-vars': 'error', + 'react/jsx-no-undef': 'error', + 'react/jsx-fragments': ['error', 'syntax'], + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': [ + 'warn', + { + additionalHooks: '(useComponentDidUpdate)', + }, + ], + }, + settings: { + 'import/resolver': { + node: { + extensions: ['.js', '.ts', '.tsx'], + }, + }, + 'react': { + version: 'detect', + }, + }, + env: { + browser: true, + es6: true, + }, + overrides: [ + { + files: ['**/*.ts', '**/*.tsx'], + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/eslint-recommended', + '@rocket.chat/eslint-config', + 'prettier', + ], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint', 'react', 'react-hooks', 'prettier'], + rules: { + '@typescript-eslint/ban-ts-ignore': 'off', + '@typescript-eslint/indent': 'off', + '@typescript-eslint/interface-name-prefix': ['error', 'always'], + '@typescript-eslint/no-extra-parens': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + }, + ], + 'func-call-spacing': 'off', + 'indent': 'off', + 'import/order': [ + 'error', + { + 'newlines-between': 'always', + 'groups': [ + 'builtin', + 'external', + 'internal', + ['parent', 'sibling', 'index'], + ], + 'alphabetize': { + order: 'asc', + }, + }, + ], + 'jsx-quotes': ['error', 'prefer-single'], + 'no-extra-parens': 'off', + 'no-spaced-func': 'off', + 'no-unused-vars': 'off', + 'no-useless-constructor': 'off', + 'no-use-before-define': 'off', + 'prefer-arrow-callback': ['error', { allowNamedFunctions: true }], + 'prettier/prettier': 2, + 'react/jsx-uses-react': 'error', + 'react/jsx-uses-vars': 'error', + 'react/jsx-no-undef': 'error', + 'react/jsx-fragments': ['error', 'syntax'], + }, + env: { + browser: true, + es6: true, + }, + settings: { + 'import/resolver': { + node: { + extensions: ['.js', '.ts', '.tsx'], + }, + }, + 'react': { + version: 'detect', + }, + }, + }, + ], +}; diff --git a/client/startup/.prettierrc b/client/startup/.prettierrc new file mode 100644 index 0000000000000..7626cd71d5ad3 --- /dev/null +++ b/client/startup/.prettierrc @@ -0,0 +1,12 @@ +{ + "semi": true, + "bracketSpacing": true, + "arrowParens": "always", + "endOfLine": "lf", + "jsxSingleQuote": true, + "printWidth": 80, + "quoteProps": "consistent", + "singleQuote": true, + "trailingComma": "all", + "useTabs": true +} diff --git a/client/startup/banners.ts b/client/startup/banners.ts index 7cce0073b0e09..b5141c25f15e2 100644 --- a/client/startup/banners.ts +++ b/client/startup/banners.ts @@ -1,15 +1,15 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import * as banners from '../lib/banners'; +import { Notifications } from '../../app/notifications/client'; import { APIClient } from '../../app/utils/client'; import { IBanner, BannerPlatform } from '../../definition/IBanner'; -import { Notifications } from '../../app/notifications/client'; +import * as banners from '../lib/banners'; const fetchInitialBanners = async (): Promise => { - const response = await APIClient.get('v1/banners.getNew', { + const response = (await APIClient.get('v1/banners.getNew', { platform: BannerPlatform.Web, - }) as { + })) as { banners: IBanner[]; }; @@ -22,10 +22,10 @@ const fetchInitialBanners = async (): Promise => { }; const handleNewBanner = async (event: { bannerId: string }): Promise => { - const response = await APIClient.get('v1/banners.getNew', { + const response = (await APIClient.get('v1/banners.getNew', { platform: BannerPlatform.Web, bid: event.bannerId, - }) as { + })) as { banners: IBanner[]; }; diff --git a/client/startup/contextualBar/exportMessages.ts b/client/startup/contextualBar/exportMessages.ts index 4ddd34f341e9d..c740bba673866 100644 --- a/client/startup/contextualBar/exportMessages.ts +++ b/client/startup/contextualBar/exportMessages.ts @@ -1,18 +1,26 @@ import { useMemo, lazy, LazyExoticComponent, FC } from 'react'; -import { addAction } from '../../views/room/lib/Toolbox'; import { usePermission } from '../../contexts/AuthorizationContext'; +import { addAction } from '../../views/room/lib/Toolbox'; addAction('export-messages', ({ room }) => { const hasPermission = usePermission('mail-messages', room._id); - return useMemo(() => (hasPermission ? { - groups: ['channel', 'group', 'direct'], - id: 'export-messages', - anonymous: true, - title: 'Export_Messages', - icon: 'mail', - template: lazy(() => import('../../views/room/contextualBar/ExportMessages')) as LazyExoticComponent, - full: true, - order: 12, - } : null), [hasPermission]); + return useMemo( + () => + hasPermission + ? { + groups: ['channel', 'group', 'direct'], + id: 'export-messages', + anonymous: true, + title: 'Export_Messages', + icon: 'mail', + template: lazy( + () => import('../../views/room/contextualBar/ExportMessages'), + ) as LazyExoticComponent, + full: true, + order: 12, + } + : null, + [hasPermission], + ); }); diff --git a/client/startup/e2e.ts b/client/startup/e2e.ts index 6b506aac9caaa..cc1a27f1a19ce 100644 --- a/client/startup/e2e.ts +++ b/client/startup/e2e.ts @@ -2,16 +2,16 @@ import { FlowRouter } from 'meteor/kadira:flow-router'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { Layout } from '../../app/ui-utils/client'; -import { settings } from '../../app/settings/client'; -import { promises } from '../../app/promises/client'; -import { Notifications } from '../../app/notifications/client'; import { e2e } from '../../app/e2e/client/rocketchat.e2e'; -import { Subscriptions, Rooms } from '../../app/models/client'; import { waitUntilFind } from '../../app/e2e/client/waitUntilFind'; +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 { IMessage } from '../../definition/IMessage'; -import { ISubscription } from '../../definition/ISubscription'; import { IRoom } from '../../definition/IRoom'; +import { ISubscription } from '../../definition/ISubscription'; const handle = async (roomId: IRoom['_id'], keyId: string): Promise => { const e2eRoom = await e2e.getInstanceByRoomId(roomId); @@ -28,7 +28,8 @@ Meteor.startup(() => { return; } - const adminEmbedded = Layout.isEmbedded() && FlowRouter.current().path.startsWith('/admin'); + const adminEmbedded = + Layout.isEmbedded() && FlowRouter.current().path.startsWith('/admin'); if (!adminEmbedded && settings.get('E2E_Enable') && window.crypto) { e2e.startClient(); @@ -79,7 +80,6 @@ Meteor.startup(() => { return; } - e2eRoom.decryptSubscription(); }, added: async (doc: ISubscription) => { @@ -93,37 +93,49 @@ Meteor.startup(() => { }, }); - promises.add('onClientMessageReceived', async (msg: IMessage) => { - const e2eRoom = await e2e.getInstanceByRoomId(msg.rid); - if (!e2eRoom || !e2eRoom.shouldConvertReceivedMessages()) { - return msg; - } - return e2e.decryptMessage(msg); - }, promises.priority.HIGH, 'e2e-decript-message'); + promises.add( + 'onClientMessageReceived', + async (msg: IMessage) => { + const e2eRoom = await e2e.getInstanceByRoomId(msg.rid); + if (!e2eRoom || !e2eRoom.shouldConvertReceivedMessages()) { + return msg; + } + return e2e.decryptMessage(msg); + }, + promises.priority.HIGH, + 'e2e-decript-message', + ); // Encrypt messages before sending - promises.add('onClientBeforeSendMessage', async (message: IMessage) => { - const e2eRoom = await e2e.getInstanceByRoomId(message.rid); + promises.add( + 'onClientBeforeSendMessage', + async (message: IMessage) => { + const e2eRoom = await e2e.getInstanceByRoomId(message.rid); - if (!e2eRoom) { - return message; - } + if (!e2eRoom) { + return message; + } - const subscription = await waitUntilFind(() => Rooms.findOne({ _id: message.rid })); + const subscription = await waitUntilFind(() => + Rooms.findOne({ _id: message.rid }), + ); - subscription.encrypted ? e2eRoom.resume() : e2eRoom.pause(); + subscription.encrypted ? e2eRoom.resume() : e2eRoom.pause(); - if (!await e2eRoom.shouldConvertSentMessages()) { - return message; - } + if (!(await e2eRoom.shouldConvertSentMessages())) { + return message; + } - // Should encrypt this message. - const msg = await e2eRoom.encrypt(message); + // Should encrypt this message. + const msg = await e2eRoom.encrypt(message); - message.msg = msg; - message.t = 'e2e'; - message.e2e = 'pending'; - return message; - }, promises.priority.HIGH, 'e2e'); + message.msg = msg; + message.t = 'e2e'; + message.e2e = 'pending'; + return message; + }, + promises.priority.HIGH, + 'e2e', + ); }); }); diff --git a/client/startup/emailVerification.ts b/client/startup/emailVerification.ts index cb3a2d3addd6e..f29189da95702 100644 --- a/client/startup/emailVerification.ts +++ b/client/startup/emailVerification.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; -import { Session } from 'meteor/session'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; +import { Session } from 'meteor/session'; +import { Tracker } from 'meteor/tracker'; import toastr from 'toastr'; import { settings } from '../../app/settings/client'; @@ -9,7 +9,14 @@ import { settings } from '../../app/settings/client'; Meteor.startup(() => { Tracker.autorun(() => { const user = Meteor.user(); - if (user && user.emails && user.emails[0] && user.emails[0].verified !== true && settings.get('Accounts_EmailVerification') === true && !Session.get('Accounts_EmailVerification_Warning')) { + if ( + user && + user.emails && + user.emails[0] && + user.emails[0].verified !== true && + settings.get('Accounts_EmailVerification') === true && + !Session.get('Accounts_EmailVerification_Warning') + ) { toastr.warning(TAPi18n.__('You_have_not_verified_your_email')); Session.set('Accounts_EmailVerification_Warning', true); } diff --git a/client/startup/i18n.js b/client/startup/i18n.js index dc3d4116bb120..78b9a3f41e404 100644 --- a/client/startup/i18n.js +++ b/client/startup/i18n.js @@ -1,12 +1,12 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; +import { Tracker } from 'meteor/tracker'; import moment from 'moment'; -import { isRtl } from '../../app/utils/client'; -import { settings } from '../../app/settings/client'; import { Users } from '../../app/models/client'; +import { settings } from '../../app/settings/client'; +import { isRtl } from '../../app/utils/client'; const currentLanguage = new ReactiveVar(); @@ -21,30 +21,32 @@ Meteor.startup(() => { const regex = /([a-z]{2,3})-([a-z]{2,4})/; const matches = regex.exec(language); if (matches) { - return `${ matches[1] }-${ matches[2].toUpperCase() }`; + return `${matches[1]}-${matches[2].toUpperCase()}`; } return language; }; - const getBrowserLanguage = () => filterLanguage(window.navigator.userLanguage || window.navigator.language); + const getBrowserLanguage = () => + filterLanguage(window.navigator.userLanguage || window.navigator.language); - const loadMomentLocale = (language) => new Promise((resolve, reject) => { - if (moment.locales().includes(language.toLowerCase())) { - resolve(language); - return; - } - - Meteor.call('loadLocale', language, (error, localeSrc) => { - if (error) { - reject(error); + const loadMomentLocale = (language) => + new Promise((resolve, reject) => { + if (moment.locales().includes(language.toLowerCase())) { + resolve(language); return; } - Function(localeSrc).call({ moment }); - resolve(language); + Meteor.call('loadLocale', language, (error, localeSrc) => { + if (error) { + reject(error); + return; + } + + Function(localeSrc).call({ moment }); + resolve(language); + }); }); - }); const applyLanguage = (language = 'en') => { language = filterLanguage(language); @@ -56,8 +58,13 @@ Meteor.startup(() => { if (!language) { return; } - document.documentElement.classList[isRtl(language) ? 'add' : 'remove']('rtl'); - document.documentElement.setAttribute('dir', isRtl(language) ? 'rtl' : 'ltr'); + document.documentElement.classList[isRtl(language) ? 'add' : 'remove']( + 'rtl', + ); + document.documentElement.setAttribute( + 'dir', + isRtl(language) ? 'rtl' : 'ltr', + ); document.querySelector('html').lang = language; TAPi18n.setLanguage(language); @@ -76,7 +83,8 @@ Meteor.startup(() => { }; window.setLanguage = setLanguage; - const defaultUserLanguage = () => settings.get('Language') || getBrowserLanguage() || 'en'; + const defaultUserLanguage = () => + settings.get('Language') || getBrowserLanguage() || 'en'; window.defaultUserLanguage = defaultUserLanguage; Tracker.autorun(() => { diff --git a/client/startup/loginViaQuery.ts b/client/startup/loginViaQuery.ts index f4546bf9b5f7e..485dda8fb2a8a 100644 --- a/client/startup/loginViaQuery.ts +++ b/client/startup/loginViaQuery.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import { FlowRouter } from 'meteor/kadira:flow-router'; +import { Meteor } from 'meteor/meteor'; Meteor.startup(() => { const resumeToken = FlowRouter.getQueryParam('resumeToken'); diff --git a/client/startup/notifications/konchatNotifications.ts b/client/startup/notifications/konchatNotifications.ts index 0bf4975a48f91..19ebf423bdb52 100644 --- a/client/startup/notifications/konchatNotifications.ts +++ b/client/startup/notifications/konchatNotifications.ts @@ -1,23 +1,32 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; import { FlowRouter } from 'meteor/kadira:flow-router'; +import { Meteor } from 'meteor/meteor'; import { Session } from 'meteor/session'; +import { Tracker } from 'meteor/tracker'; -import { KonchatNotification } from '../../../app/ui/client'; import { CachedChatSubscription } from '../../../app/models/client'; -import { fireGlobalEvent, readMessage, Layout } from '../../../app/ui-utils/client'; -import { getUserPreference } from '../../../app/utils/client'; import { Notifications } from '../../../app/notifications/client'; -import { ISubscription } from '../../../definition/ISubscription'; +import { + fireGlobalEvent, + readMessage, + Layout, +} 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'; const notifyNewRoom = (sub: ISubscription): void => { - if (Session.equals(`user_${ Meteor.userId() }_status`, 'busy')) { + if (Session.equals(`user_${Meteor.userId()}_status`, 'busy')) { return; } - if ((!FlowRouter.getParam('name') || FlowRouter.getParam('name') !== sub.name) && !sub.ls && sub.alert === true) { + if ( + (!FlowRouter.getParam('name') || + FlowRouter.getParam('name') !== sub.name) && + !sub.ls && + sub.alert === true + ) { KonchatNotification.newRoom(sub.rid); } }; @@ -83,33 +92,49 @@ Meteor.startup(() => { } }); - Notifications.onUser('audioNotification', (notification: AudioNotificationEvent) => { - const openedRoomId = Session.get('openedRoom'); + Notifications.onUser( + 'audioNotification', + (notification: AudioNotificationEvent) => { + const openedRoomId = Session.get('openedRoom'); - // This logic is duplicated in /client/startup/unread.coffee. - const hasFocus = readMessage.isEnable(); - const messageIsInOpenedRoom = openedRoomId === notification.payload.rid; - const muteFocusedConversations = getUserPreference(Meteor.userId(), 'muteFocusedConversations'); + // This logic is duplicated in /client/startup/unread.coffee. + const hasFocus = readMessage.isEnable(); + const messageIsInOpenedRoom = openedRoomId === notification.payload.rid; + const muteFocusedConversations = getUserPreference( + Meteor.userId(), + 'muteFocusedConversations', + ); - if (Layout.isEmbedded()) { - if (!hasFocus && messageIsInOpenedRoom) { + if (Layout.isEmbedded()) { + if (!hasFocus && messageIsInOpenedRoom) { + // Play a notification sound + KonchatNotification.newMessage(notification.payload.rid); + } + } else if ( + !hasFocus || + !messageIsInOpenedRoom || + !muteFocusedConversations + ) { // Play a notification sound KonchatNotification.newMessage(notification.payload.rid); } - } else if (!hasFocus || !messageIsInOpenedRoom || !muteFocusedConversations) { - // Play a notification sound - KonchatNotification.newMessage(notification.payload.rid); - } - }); + }, + ); - CachedChatSubscription.onSyncData = ((action: 'changed' | 'removed', sub: ISubscription): void => { + CachedChatSubscription.onSyncData = (( + action: 'changed' | 'removed', + sub: ISubscription, + ): void => { if (action !== 'removed') { notifyNewRoom(sub); } }) as () => void; - Notifications.onUser('subscriptions-changed', (_action: 'changed' | 'removed', sub: ISubscription) => { - notifyNewRoom(sub); - }); + Notifications.onUser( + 'subscriptions-changed', + (_action: 'changed' | 'removed', sub: ISubscription) => { + notifyNewRoom(sub); + }, + ); }); }); diff --git a/client/startup/notifications/updateAvatar.ts b/client/startup/notifications/updateAvatar.ts index ef587fc8c2513..658959bf5d698 100644 --- a/client/startup/notifications/updateAvatar.ts +++ b/client/startup/notifications/updateAvatar.ts @@ -11,6 +11,7 @@ type UpdateAvatarEvent = { Meteor.startup(() => { Notifications.onLogged('updateAvatar', (data: UpdateAvatarEvent) => { const { username, etag } = data; - username && Meteor.users.update({ username }, { $set: { avatarETag: etag } }); + username && + Meteor.users.update({ username }, { $set: { avatarETag: etag } }); }); }); diff --git a/client/startup/notifications/usersNameChanged.ts b/client/startup/notifications/usersNameChanged.ts index c69442f313ad9..0998a94d3ba32 100644 --- a/client/startup/notifications/usersNameChanged.ts +++ b/client/startup/notifications/usersNameChanged.ts @@ -1,54 +1,72 @@ import { Meteor } from 'meteor/meteor'; -import { Notifications } from '../../../app/notifications/client'; import { Messages, RoomRoles, Subscriptions } from '../../../app/models/client'; +import { Notifications } from '../../../app/notifications/client'; import { IUser } from '../../../definition/IUser'; type UsersNameChangedEvent = Partial; Meteor.startup(() => { - Notifications.onLogged('Users:NameChanged', ({ _id, name, username }: UsersNameChangedEvent) => { - Messages.update({ - 'u._id': _id, - }, { - $set: { - 'u.username': username, - 'u.name': name, - }, - }, { - multi: true, - }); + Notifications.onLogged( + 'Users:NameChanged', + ({ _id, name, username }: UsersNameChangedEvent) => { + Messages.update( + { + 'u._id': _id, + }, + { + $set: { + 'u.username': username, + 'u.name': name, + }, + }, + { + multi: true, + }, + ); - Messages.update({ - mentions: { - $elemMatch: { _id }, - }, - }, { - $set: { - 'mentions.$.username': username, - 'mentions.$.name': name, - }, - }, { - multi: true, - }); + Messages.update( + { + mentions: { + $elemMatch: { _id }, + }, + }, + { + $set: { + 'mentions.$.username': username, + 'mentions.$.name': name, + }, + }, + { + multi: true, + }, + ); - Subscriptions.update({ - name: username, - t: 'd', - }, { - $set: { - fname: name, - }, - }); + Subscriptions.update( + { + name: username, + t: 'd', + }, + { + $set: { + fname: name, + }, + }, + ); - RoomRoles.update({ - 'u._id': _id, - }, { - $set: { - 'u.name': name, - }, - }, { - multi: true, - }); - }); + RoomRoles.update( + { + 'u._id': _id, + }, + { + $set: { + 'u.name': name, + }, + }, + { + multi: true, + }, + ); + }, + ); }); diff --git a/client/startup/renderMessage/autolinker.ts b/client/startup/renderMessage/autolinker.ts index 82d6fc4fba777..7da7a7063ed78 100644 --- a/client/startup/renderMessage/autolinker.ts +++ b/client/startup/renderMessage/autolinker.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../app/settings/client'; import { callbacks } from '../../../app/callbacks/client'; +import { settings } from '../../../app/settings/client'; Meteor.startup(() => { Tracker.autorun(() => { @@ -24,10 +24,17 @@ Meteor.startup(() => { phone: settings.get('AutoLinker_Phone'), }; - import('../../../app/autolinker/client').then(({ createAutolinkerMessageRenderer }) => { - const renderMessage = createAutolinkerMessageRenderer(options); - callbacks.remove('renderMessage', 'autolinker'); - callbacks.add('renderMessage', renderMessage, callbacks.priority.MEDIUM, 'autolinker'); - }); + import('../../../app/autolinker/client').then( + ({ createAutolinkerMessageRenderer }) => { + const renderMessage = createAutolinkerMessageRenderer(options); + callbacks.remove('renderMessage', 'autolinker'); + callbacks.add( + 'renderMessage', + renderMessage, + callbacks.priority.MEDIUM, + 'autolinker', + ); + }, + ); }); }); diff --git a/client/startup/renderMessage/autotranslate.ts b/client/startup/renderMessage/autotranslate.ts index 88d66349c665f..da7bacc0f5ead 100644 --- a/client/startup/renderMessage/autotranslate.ts +++ b/client/startup/renderMessage/autotranslate.ts @@ -1,23 +1,31 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../app/settings/client'; -import { callbacks } from '../../../app/callbacks/client'; import { hasPermission } from '../../../app/authorization/client'; +import { callbacks } from '../../../app/callbacks/client'; +import { settings } from '../../../app/settings/client'; Meteor.startup(() => { Tracker.autorun(() => { - const isEnabled = settings.get('AutoTranslate_Enabled') && hasPermission('auto-translate'); + const isEnabled = + settings.get('AutoTranslate_Enabled') && hasPermission('auto-translate'); if (!isEnabled) { callbacks.remove('renderMessage', 'autotranslate'); return; } - import('../../../app/autotranslate/client').then(({ createAutoTranslateMessageRenderer }) => { - const renderMessage = createAutoTranslateMessageRenderer(); - callbacks.remove('renderMessage', 'autotranslate'); - callbacks.add('renderMessage', renderMessage, callbacks.priority.HIGH - 3, 'autotranslate'); - }); + import('../../../app/autotranslate/client').then( + ({ createAutoTranslateMessageRenderer }) => { + const renderMessage = createAutoTranslateMessageRenderer(); + callbacks.remove('renderMessage', 'autotranslate'); + callbacks.add( + 'renderMessage', + renderMessage, + callbacks.priority.HIGH - 3, + 'autotranslate', + ); + }, + ); }); }); diff --git a/client/startup/renderMessage/emoji.ts b/client/startup/renderMessage/emoji.ts index 4ddbdfbcf2a59..22c6cd733172c 100644 --- a/client/startup/renderMessage/emoji.ts +++ b/client/startup/renderMessage/emoji.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { getUserPreference } from '../../../app/utils/client'; import { callbacks } from '../../../app/callbacks/client'; +import { getUserPreference } from '../../../app/utils/client'; Meteor.startup(() => { Tracker.autorun(() => { @@ -13,10 +13,17 @@ Meteor.startup(() => { return; } - import('../../../app/emoji/client').then(({ createEmojiMessageRenderer }) => { - const renderMessage = createEmojiMessageRenderer(); - callbacks.remove('renderMessage', 'emoji'); - callbacks.add('renderMessage', renderMessage, callbacks.priority.LOW, 'emoji'); - }); + import('../../../app/emoji/client').then( + ({ createEmojiMessageRenderer }) => { + const renderMessage = createEmojiMessageRenderer(); + callbacks.remove('renderMessage', 'emoji'); + callbacks.add( + 'renderMessage', + renderMessage, + callbacks.priority.LOW, + 'emoji', + ); + }, + ); }); }); diff --git a/client/startup/renderMessage/googlevision.ts b/client/startup/renderMessage/googlevision.ts index 989a6117f3ea1..ae350bc6f2835 100644 --- a/client/startup/renderMessage/googlevision.ts +++ b/client/startup/renderMessage/googlevision.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../app/settings/client'; import { callbacks } from '../../../app/callbacks/client'; +import { settings } from '../../../app/settings/client'; Meteor.startup(() => { Tracker.autorun(() => { @@ -13,10 +13,17 @@ Meteor.startup(() => { return; } - import('../../../app/google-vision/client').then(({ createGoogleVisionMessageRenderer }) => { - const renderMessage = createGoogleVisionMessageRenderer(); - callbacks.remove('renderMessage', 'googlevision'); - callbacks.add('renderMessage', renderMessage, callbacks.priority.HIGH - 3, 'googlevision'); - }); + import('../../../app/google-vision/client').then( + ({ createGoogleVisionMessageRenderer }) => { + const renderMessage = createGoogleVisionMessageRenderer(); + callbacks.remove('renderMessage', 'googlevision'); + callbacks.add( + 'renderMessage', + renderMessage, + callbacks.priority.HIGH - 3, + 'googlevision', + ); + }, + ); }); }); diff --git a/client/startup/renderMessage/hexcolor.ts b/client/startup/renderMessage/hexcolor.ts index a4586c509d7fe..302e45d5f96f6 100644 --- a/client/startup/renderMessage/hexcolor.ts +++ b/client/startup/renderMessage/hexcolor.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../app/settings/client'; import { callbacks } from '../../../app/callbacks/client'; +import { settings } from '../../../app/settings/client'; Meteor.startup(() => { Tracker.autorun(() => { @@ -13,10 +13,17 @@ Meteor.startup(() => { return; } - import('../../../app/colors/client').then(({ createHexColorPreviewMessageRenderer }) => { - const renderMessage = createHexColorPreviewMessageRenderer(); - callbacks.remove('renderMessage', 'hexcolor'); - callbacks.add('renderMessage', renderMessage, callbacks.priority.MEDIUM, 'hexcolor'); - }); + import('../../../app/colors/client').then( + ({ createHexColorPreviewMessageRenderer }) => { + const renderMessage = createHexColorPreviewMessageRenderer(); + callbacks.remove('renderMessage', 'hexcolor'); + callbacks.add( + 'renderMessage', + renderMessage, + callbacks.priority.MEDIUM, + 'hexcolor', + ); + }, + ); }); }); diff --git a/client/startup/renderMessage/highlightWords.ts b/client/startup/renderMessage/highlightWords.ts index 9239069a1596f..caa07ddcb941b 100644 --- a/client/startup/renderMessage/highlightWords.ts +++ b/client/startup/renderMessage/highlightWords.ts @@ -6,8 +6,12 @@ import { getUserPreference } from '../../../app/utils/client'; Meteor.startup(() => { Tracker.autorun(() => { - const highlights: (string | undefined)[] | undefined = getUserPreference(Meteor.userId(), 'highlights'); - const isEnabled = highlights?.some((highlight) => highlight?.trim()) ?? false; + const highlights: (string | undefined)[] | undefined = getUserPreference( + Meteor.userId(), + 'highlights', + ); + const isEnabled = + highlights?.some((highlight) => highlight?.trim()) ?? false; if (!isEnabled) { callbacks.remove('renderMessage', 'highlight-words'); @@ -18,10 +22,17 @@ Meteor.startup(() => { wordsToHighlight: highlights?.filter((highlight) => highlight?.trim()), }; - import('../../../app/highlight-words').then(({ createHighlightWordsMessageRenderer }) => { - const renderMessage = createHighlightWordsMessageRenderer(options); - callbacks.remove('renderMessage', 'highlight-words'); - callbacks.add('renderMessage', renderMessage, callbacks.priority.MEDIUM + 1, 'highlight-words'); - }); + import('../../../app/highlight-words').then( + ({ createHighlightWordsMessageRenderer }) => { + const renderMessage = createHighlightWordsMessageRenderer(options); + callbacks.remove('renderMessage', 'highlight-words'); + callbacks.add( + 'renderMessage', + renderMessage, + callbacks.priority.MEDIUM + 1, + 'highlight-words', + ); + }, + ); }); }); diff --git a/client/startup/renderMessage/issuelink.ts b/client/startup/renderMessage/issuelink.ts index b8ef641720d18..0be15dfe80c64 100644 --- a/client/startup/renderMessage/issuelink.ts +++ b/client/startup/renderMessage/issuelink.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../app/settings/client'; import { callbacks } from '../../../app/callbacks/client'; +import { settings } from '../../../app/settings/client'; Meteor.startup(() => { Tracker.autorun(() => { @@ -17,10 +17,17 @@ Meteor.startup(() => { template: settings.get('IssueLinks_Template'), }; - import('../../../app/issuelinks/client').then(({ createIssueLinksMessageRenderer }) => { - const renderMessage = createIssueLinksMessageRenderer(options); - callbacks.remove('renderMessage', 'issuelink'); - callbacks.add('renderMessage', renderMessage, callbacks.priority.MEDIUM, 'issuelink'); - }); + import('../../../app/issuelinks/client').then( + ({ createIssueLinksMessageRenderer }) => { + const renderMessage = createIssueLinksMessageRenderer(options); + callbacks.remove('renderMessage', 'issuelink'); + callbacks.add( + 'renderMessage', + renderMessage, + callbacks.priority.MEDIUM, + 'issuelink', + ); + }, + ); }); }); diff --git a/client/startup/renderMessage/katex.ts b/client/startup/renderMessage/katex.ts index 4f5d042d1b3a5..993c3c5dc5635 100644 --- a/client/startup/renderMessage/katex.ts +++ b/client/startup/renderMessage/katex.ts @@ -18,10 +18,17 @@ Meteor.startup(() => { parenthesisSyntax: settings.get('Katex_Parenthesis_Syntax'), }; - import('../../../app/katex/client').then(({ createKatexMessageRendering }) => { - const renderMessage = createKatexMessageRendering(options); - callbacks.remove('renderMessage', 'katex'); - callbacks.add('renderMessage', renderMessage, callbacks.priority.HIGH + 1, 'katex'); - }); + import('../../../app/katex/client').then( + ({ createKatexMessageRendering }) => { + const renderMessage = createKatexMessageRendering(options); + callbacks.remove('renderMessage', 'katex'); + callbacks.add( + 'renderMessage', + renderMessage, + callbacks.priority.HIGH + 1, + 'katex', + ); + }, + ); }); }); diff --git a/client/startup/renderMessage/markdown.ts b/client/startup/renderMessage/markdown.ts index e38f62bb89bc5..8a0d8e89984b8 100644 --- a/client/startup/renderMessage/markdown.ts +++ b/client/startup/renderMessage/markdown.ts @@ -21,10 +21,17 @@ Meteor.startup(() => { }, }; - import('../../../app/markdown/client').then(({ createMarkdownMessageRenderer }) => { - const renderMessage = createMarkdownMessageRenderer(options); - callbacks.remove('renderMessage', 'markdown'); - callbacks.add('renderMessage', renderMessage, callbacks.priority.HIGH, 'markdown'); - }); + import('../../../app/markdown/client').then( + ({ createMarkdownMessageRenderer }) => { + const renderMessage = createMarkdownMessageRenderer(options); + callbacks.remove('renderMessage', 'markdown'); + callbacks.add( + 'renderMessage', + renderMessage, + callbacks.priority.HIGH, + 'markdown', + ); + }, + ); }); }); diff --git a/client/startup/renderMessage/mentionsMessage.ts b/client/startup/renderMessage/mentionsMessage.ts index ec2936bc699b2..4a4741d471c39 100644 --- a/client/startup/renderMessage/mentionsMessage.ts +++ b/client/startup/renderMessage/mentionsMessage.ts @@ -2,22 +2,30 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import { callbacks } from '../../../app/callbacks/client'; -import { settings } from '../../../app/settings/client'; import { Users } from '../../../app/models/client'; +import { settings } from '../../../app/settings/client'; Meteor.startup(() => { Tracker.autorun(() => { const uid = Meteor.userId(); const options = { - me: uid && (Users.findOne(uid, { fields: { username: 1 } }) || {}).username, + me: + uid && (Users.findOne(uid, { fields: { username: 1 } }) || {}).username, pattern: settings.get('UTF8_Names_Validation'), useRealName: settings.get('UI_Use_Real_Name'), }; - import('../../../app/mentions/client').then(({ createMentionsMessageRenderer }) => { - const renderMessage = createMentionsMessageRenderer(options); - callbacks.remove('renderMessage', 'mentions-message'); - callbacks.add('renderMessage', renderMessage, callbacks.priority.MEDIUM, 'mentions-message'); - }); + import('../../../app/mentions/client').then( + ({ createMentionsMessageRenderer }) => { + const renderMessage = createMentionsMessageRenderer(options); + callbacks.remove('renderMessage', 'mentions-message'); + callbacks.add( + 'renderMessage', + renderMessage, + callbacks.priority.MEDIUM, + 'mentions-message', + ); + }, + ); }); }); diff --git a/client/startup/renderNotification/markdown.ts b/client/startup/renderNotification/markdown.ts index a76f420164d71..8a22279a616ff 100644 --- a/client/startup/renderNotification/markdown.ts +++ b/client/startup/renderNotification/markdown.ts @@ -8,9 +8,16 @@ Meteor.startup(() => { supportSchemesForLink: settings.get('Markdown_SupportSchemesForLink'), }; - import('../../../app/markdown/client').then(({ createMarkdownNotificationRenderer }) => { - const renderNotification = createMarkdownNotificationRenderer(options); - callbacks.remove('renderNotification', 'filter-markdown'); - callbacks.add('renderNotification', renderNotification, callbacks.priority.HIGH, 'filter-markdown'); - }); + import('../../../app/markdown/client').then( + ({ createMarkdownNotificationRenderer }) => { + const renderNotification = createMarkdownNotificationRenderer(options); + callbacks.remove('renderNotification', 'filter-markdown'); + callbacks.add( + 'renderNotification', + renderNotification, + callbacks.priority.HIGH, + 'filter-markdown', + ); + }, + ); }); diff --git a/client/startup/roomObserve.ts b/client/startup/roomObserve.ts index 5114c8778a0ea..0ad0218237d6a 100644 --- a/client/startup/roomObserve.ts +++ b/client/startup/roomObserve.ts @@ -3,16 +3,16 @@ import { Session } from 'meteor/session'; import { ChatRoom } from '../../app/models/client'; -Meteor.startup(function() { +Meteor.startup(() => { ChatRoom.find().observe({ added(data) { - Session.set(`roomData${ data._id }`, data); + Session.set(`roomData${data._id}`, data); }, changed(data) { - Session.set(`roomData${ data._id }`, data); + Session.set(`roomData${data._id}`, data); }, removed(data) { - Session.set(`roomData${ data._id }`, undefined); + Session.set(`roomData${data._id}`, undefined); }, }); }); diff --git a/client/startup/startup.js b/client/startup/startup.js index e1148c04a2225..df9f9c539cfeb 100644 --- a/client/startup/startup.js +++ b/client/startup/startup.js @@ -1,19 +1,18 @@ +import { Accounts } from 'meteor/accounts-base'; +import { UserPresence } from 'meteor/konecty:user-presence'; import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; -import { Session } from 'meteor/session'; import { TimeSync } from 'meteor/mizzao:timesync'; -import { UserPresence } from 'meteor/konecty:user-presence'; -import { Accounts } from 'meteor/accounts-base'; +import { Session } from 'meteor/session'; +import { Tracker } from 'meteor/tracker'; 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 { hasPermission } from '../../app/authorization/client'; import 'highlight.js/styles/github.css'; -import { synchronizeUserData } from '../lib/userData'; import * as banners from '../lib/banners'; +import { synchronizeUserData } from '../lib/userData'; hljs.initHighlightingOnLoad(); @@ -24,7 +23,7 @@ if (window.DISABLE_ANIMATION) { toastr.options.extendedTimeOut = 0; } -Meteor.startup(function() { +Meteor.startup(() => { Accounts.onLogout(() => Session.set('openedRoom', null)); TimeSync.loggingEnabled = false; @@ -35,7 +34,7 @@ Meteor.startup(function() { window.lastMessageWindowHistory = {}; let status = undefined; - Tracker.autorun(async function() { + Tracker.autorun(async () => { const uid = Meteor.userId(); if (!uid) { return; @@ -65,7 +64,7 @@ Meteor.startup(function() { } }); - const autoRunHandler = Tracker.autorun(async function() { + const autoRunHandler = Tracker.autorun(async () => { const uid = Meteor.userId(); if (!uid) { return; diff --git a/client/startup/streamMessage/autotranslate.ts b/client/startup/streamMessage/autotranslate.ts index 5566d9dd025b5..eb5151cf88e69 100644 --- a/client/startup/streamMessage/autotranslate.ts +++ b/client/startup/streamMessage/autotranslate.ts @@ -1,23 +1,31 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../app/settings/client'; -import { callbacks } from '../../../app/callbacks/client'; import { hasPermission } from '../../../app/authorization/client'; +import { callbacks } from '../../../app/callbacks/client'; +import { settings } from '../../../app/settings/client'; Meteor.startup(() => { Tracker.autorun(() => { - const isEnabled = settings.get('AutoTranslate_Enabled') && hasPermission('auto-translate'); + const isEnabled = + settings.get('AutoTranslate_Enabled') && hasPermission('auto-translate'); if (!isEnabled) { callbacks.remove('streamMessage', 'autotranslate-stream'); return; } - import('../../../app/autotranslate/client').then(({ createAutoTranslateMessageStreamHandler }) => { - const streamMessage = createAutoTranslateMessageStreamHandler(); - callbacks.remove('streamMessage', 'autotranslate-stream'); - callbacks.add('streamMessage', streamMessage, callbacks.priority.HIGH - 3, 'autotranslate-stream'); - }); + import('../../../app/autotranslate/client').then( + ({ createAutoTranslateMessageStreamHandler }) => { + const streamMessage = createAutoTranslateMessageStreamHandler(); + callbacks.remove('streamMessage', 'autotranslate-stream'); + callbacks.add( + 'streamMessage', + streamMessage, + callbacks.priority.HIGH - 3, + 'autotranslate-stream', + ); + }, + ); }); }); diff --git a/client/startup/streamMessage/googlevision.ts b/client/startup/streamMessage/googlevision.ts index 499d286fec77f..afbef49e460fd 100644 --- a/client/startup/streamMessage/googlevision.ts +++ b/client/startup/streamMessage/googlevision.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../app/settings/client'; import { callbacks } from '../../../app/callbacks/client'; +import { settings } from '../../../app/settings/client'; Meteor.startup(() => { Tracker.autorun(() => { @@ -13,10 +13,17 @@ Meteor.startup(() => { return; } - import('../../../app/google-vision/client').then(({ createGoogleVisionMessageStreamHandler }) => { - const streamMessage = createGoogleVisionMessageStreamHandler(); - callbacks.remove('streamMessage', 'googlevision'); - callbacks.add('streamMessage', streamMessage, callbacks.priority.HIGH - 3, 'googlevision'); - }); + import('../../../app/google-vision/client').then( + ({ createGoogleVisionMessageStreamHandler }) => { + const streamMessage = createGoogleVisionMessageStreamHandler(); + callbacks.remove('streamMessage', 'googlevision'); + callbacks.add( + 'streamMessage', + streamMessage, + callbacks.priority.HIGH - 3, + 'googlevision', + ); + }, + ); }); }); diff --git a/client/startup/theme.js b/client/startup/theme.js index 8a04c7c29b924..4d6712c1903b1 100644 --- a/client/startup/theme.js +++ b/client/startup/theme.js @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import createLess from 'less/browser'; +import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; import { settings } from '../../app/settings/client'; @@ -8,9 +8,15 @@ const variables = new Map(); const lessExpressions = new Map([ ['default-action-color', 'darken(@secondary-background-color, 15%)'], ['default-action-contrast', 'contrast(@default-action-color, #444444)'], - ['primary-background-contrast', 'contrast(@primary-background-color, #444444)'], + [ + 'primary-background-contrast', + 'contrast(@primary-background-color, #444444)', + ], ['primary-action-contrast', 'contrast(@primary-action-color, #444444)'], - ['secondary-background-contrast', 'contrast(@secondary-background-color, #444444)'], + [ + 'secondary-background-contrast', + 'contrast(@secondary-background-color, #444444)', + ], ['secondary-action-contrast', 'contrast(@secondary-action-color, #444444)'], ['selection-background', 'lighten(@selection-color, 30%)'], ['success-background', 'lighten(@success-color, 45%)'], @@ -36,10 +42,19 @@ const compileLess = async () => { } const lessCode = [ - ...Array.from(variables.entries(), ([name, value]) => `@${ name }: ${ value };`), - ...Array.from(lessExpressions.entries(), ([name, expression]) => `@${ name }: ${ expression };`), + ...Array.from( + variables.entries(), + ([name, value]) => `@${name}: ${value};`, + ), + ...Array.from( + lessExpressions.entries(), + ([name, expression]) => `@${name}: ${expression};`, + ), ':root {', - ...Array.from(lessExpressions.entries(), ([name]) => `--${ name }: @${ name };`), + ...Array.from( + lessExpressions.entries(), + ([name]) => `--${name}: @${name};`, + ), '}', ].join('\n'); @@ -61,7 +76,10 @@ const updateCssVariables = _.debounce(async () => { cssVariablesElement.innerHTML = [ ':root {', - ...Array.from(variables.entries(), ([name, value]) => `--${ name }: ${ value };`), + ...Array.from( + variables.entries(), + ([name, value]) => `--${name}: ${value};`, + ), '}', await compileLess(), ].join('\n'); @@ -82,7 +100,7 @@ const handleThemeColorChanged = ({ _id, value, editor }) => { return; } - variables.set(name, `var(--${ value })`); + variables.set(name, `var(--${value})`); } finally { updateCssVariables(); } @@ -98,13 +116,17 @@ const handleThemeFontChanged = ({ _id, value }) => { }; Meteor.startup(() => { - settings.collection.find({ _id: /^theme-color-/i }, { fields: { value: 1, editor: 1 } }).observe({ - added: handleThemeColorChanged, - changed: handleThemeColorChanged, - }); - - settings.collection.find({ _id: /^theme-font-/i }, { fields: { value: 1 } }).observe({ - added: handleThemeFontChanged, - changed: handleThemeFontChanged, - }); + settings.collection + .find({ _id: /^theme-color-/i }, { fields: { value: 1, editor: 1 } }) + .observe({ + added: handleThemeColorChanged, + changed: handleThemeColorChanged, + }); + + settings.collection + .find({ _id: /^theme-font-/i }, { fields: { value: 1 } }) + .observe({ + added: handleThemeFontChanged, + changed: handleThemeFontChanged, + }); }); diff --git a/client/startup/unread.js b/client/startup/unread.js index 10211a8b52ae9..6101031e0ff99 100644 --- a/client/startup/unread.js +++ b/client/startup/unread.js @@ -1,29 +1,33 @@ import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; import { Session } from 'meteor/session'; +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 { getUserPreference } from '../../app/utils/client'; -import { settings } from '../../app/settings/client'; -const fetchSubscriptions = () => ChatSubscription.find({ - open: true, - hideUnreadStatus: { $ne: true }, -}, { - fields: { - unread: 1, - alert: 1, - rid: 1, - t: 1, - name: 1, - ls: 1, - unreadAlert: 1, - fname: 1, - prid: 1, - }, -}).fetch(); +const fetchSubscriptions = () => + ChatSubscription.find( + { + open: true, + hideUnreadStatus: { $ne: true }, + }, + { + fields: { + unread: 1, + alert: 1, + rid: 1, + t: 1, + name: 1, + ls: 1, + unreadAlert: 1, + fname: 1, + prid: 1, + }, + }, + ).fetch(); Meteor.startup(() => { Tracker.autorun(() => { @@ -31,22 +35,37 @@ Meteor.startup(() => { let unreadAlert = false; - const unreadCount = fetchSubscriptions().reduce((ret, subscription) => - Tracker.nonreactive(() => { - const room = ChatRoom.findOne({ _id: subscription.rid }, { fields: { usersCount: 1 } }); - fireGlobalEvent('unread-changed-by-subscription', { ...subscription, usersCount: room && room.usersCount }); + const unreadCount = fetchSubscriptions().reduce( + (ret, subscription) => + Tracker.nonreactive(() => { + const room = ChatRoom.findOne( + { _id: subscription.rid }, + { fields: { usersCount: 1 } }, + ); + fireGlobalEvent('unread-changed-by-subscription', { + ...subscription, + usersCount: room && room.usersCount, + }); - if (subscription.alert || subscription.unread > 0) { - // Increment the total unread count. - if (subscription.alert === true && subscription.unreadAlert !== 'nothing') { - if (subscription.unreadAlert === 'all' || userUnreadAlert !== false) { - unreadAlert = '•'; + if (subscription.alert || subscription.unread > 0) { + // Increment the total unread count. + if ( + subscription.alert === true && + subscription.unreadAlert !== 'nothing' + ) { + if ( + subscription.unreadAlert === 'all' || + userUnreadAlert !== false + ) { + unreadAlert = '•'; + } } + return ret + subscription.unread; } - return ret + subscription.unread; - } - return ret; - }), 0); + return ret; + }), + 0, + ); menu.updateUnreadBars(); @@ -72,7 +91,7 @@ Meteor.startup(() => { window.favico = favicon; - Tracker.autorun(function() { + Tracker.autorun(() => { const siteName = settings.get('Site_Name') || ''; const unread = Session.get('unread'); @@ -84,6 +103,6 @@ Meteor.startup(() => { }); } - document.title = unread === '' ? siteName : `(${ unread }) ${ siteName }`; + document.title = unread === '' ? siteName : `(${unread}) ${siteName}`; }); }); diff --git a/client/startup/userSetUtcOffset.ts b/client/startup/userSetUtcOffset.ts index cac8c471ecfd4..0de22e7863741 100644 --- a/client/startup/userSetUtcOffset.ts +++ b/client/startup/userSetUtcOffset.ts @@ -6,7 +6,10 @@ import { Users } from '../../app/models/client'; Meteor.startup(() => { Tracker.autorun(() => { - const user = Users.findOne({ _id: Meteor.userId() }, { fields: { statusConnection: 1, utcOffset: 1 } }); + const user = Users.findOne( + { _id: Meteor.userId() }, + { fields: { statusConnection: 1, utcOffset: 1 } }, + ); if (user && user.statusConnection === 'online') { const utcOffset = moment().utcOffset() / 60; if (user.utcOffset !== utcOffset) { diff --git a/client/startup/userStatusManuallySet.ts b/client/startup/userStatusManuallySet.ts index 31b1e8c1aa0e4..e86bbb2b402d9 100644 --- a/client/startup/userStatusManuallySet.ts +++ b/client/startup/userStatusManuallySet.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; -import { fireGlobalEvent } from '../../app/ui-utils/client'; import { callbacks } from '../../app/callbacks/client'; +import { fireGlobalEvent } from '../../app/ui-utils/client'; import { USER_STATUS } from '../../definition/UserStatus'; /* fire user state change globally, to listen on desktop electron client */ diff --git a/client/startup/usersObserve.ts b/client/startup/usersObserve.ts index 06a33bc5381f0..1def913737c0a 100644 --- a/client/startup/usersObserve.ts +++ b/client/startup/usersObserve.ts @@ -5,23 +5,36 @@ import { RoomManager } from '../../app/ui-utils/client'; import { IUser } from '../../definition/IUser'; Meteor.startup(() => { - Meteor.users.find({}, { fields: { name: 1, username: 1, status: 1, utcOffset: 1, statusText: 1 } }).observe({ - added(user: IUser) { - Session.set(`user_${ user.username }_status`, user.status); - Session.set(`user_${ user.username }_status_text`, user.statusText); - RoomManager.updateUserStatus(user, user.status, user.utcOffset); - }, - changed(user: IUser) { - Session.set(`user_${ user.username }_status`, user.status); - if (user.statusText !== undefined) { - Session.set(`user_${ user.username }_status_text`, user.statusText); - } - RoomManager.updateUserStatus(user, user.status, user.utcOffset); - }, - removed(user) { - Session.set(`user_${ user.username }_status`, null); - Session.set(`user_${ user.username }_status_text`, null); - RoomManager.updateUserStatus(user, 'offline', null); - }, - }); + Meteor.users + .find( + {}, + { + fields: { + name: 1, + username: 1, + status: 1, + utcOffset: 1, + statusText: 1, + }, + }, + ) + .observe({ + added(user: IUser) { + Session.set(`user_${user.username}_status`, user.status); + Session.set(`user_${user.username}_status_text`, user.statusText); + RoomManager.updateUserStatus(user, user.status, user.utcOffset); + }, + changed(user: IUser) { + Session.set(`user_${user.username}_status`, user.status); + if (user.statusText !== undefined) { + Session.set(`user_${user.username}_status_text`, user.statusText); + } + RoomManager.updateUserStatus(user, user.status, user.utcOffset); + }, + removed(user) { + Session.set(`user_${user.username}_status`, null); + Session.set(`user_${user.username}_status_text`, null); + RoomManager.updateUserStatus(user, 'offline', null); + }, + }); }); diff --git a/package-lock.json b/package-lock.json index c1fc4a253e960..b5ba67c161f9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7351,6 +7351,12 @@ "integrity": "sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg==", "dev": true }, + "prettier": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", + "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "dev": true + }, "regenerator-runtime": { "version": "0.13.7", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", @@ -10326,6 +10332,12 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "prettier": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", + "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "dev": true + }, "regenerator-runtime": { "version": "0.13.7", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", @@ -18613,6 +18625,12 @@ } } }, + "eslint-config-prettier": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz", + "integrity": "sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==", + "dev": true + }, "eslint-import-resolver-node": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", @@ -18671,6 +18689,15 @@ } } }, + "eslint-plugin-prettier": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz", + "integrity": "sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, "eslint-plugin-react": { "version": "7.20.6", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.20.6.tgz", @@ -19193,6 +19220,12 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-glob": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", @@ -29716,11 +29749,20 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, "prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", "dev": true }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-bytes": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz", diff --git a/package.json b/package.json index aea525b7e6a16..32de08797aef3 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,9 @@ "cypress": "^4.12.1", "emojione-assets": "^4.5.0", "eslint": "^6.8.0", + "eslint-config-prettier": "^8.1.0", "eslint-plugin-import": "^2.22.0", + "eslint-plugin-prettier": "^3.3.1", "eslint-plugin-react": "^7.20.6", "eslint-plugin-react-hooks": "^4.1.0", "fast-glob": "^2.2.7", @@ -115,6 +117,7 @@ "postcss-nested": "^4.2.3", "postcss-selector-not": "^4.0.0", "postcss-url": "^8.0.0", + "prettier": "^2.2.1", "progress": "^2.0.3", "proxyquire": "^2.1.3", "rewire": "^5.0.0", From 2679910f7c3cd4c74153317b119a66ce1980b9d6 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Fri, 26 Mar 2021 10:44:23 -0300 Subject: [PATCH 010/124] Convert last startup modules to TypeScript --- client/lib/userData.ts | 4 +- client/startup/.eslintrc.js | 14 +-- client/startup/.prettierrc | 2 +- client/startup/e2e.ts | 7 +- client/startup/{i18n.js => i18n.ts} | 35 +++--- .../notifications/konchatNotifications.ts | 48 +++----- client/startup/notifications/updateAvatar.ts | 3 +- .../startup/notifications/usersNameChanged.ts | 105 +++++++++--------- client/startup/renderMessage/autolinker.ts | 17 +-- client/startup/renderMessage/autotranslate.ts | 20 +--- client/startup/renderMessage/emoji.ts | 17 +-- client/startup/renderMessage/googlevision.ts | 17 +-- client/startup/renderMessage/hexcolor.ts | 17 +-- .../startup/renderMessage/highlightWords.ts | 25 ++--- client/startup/renderMessage/issuelink.ts | 17 +-- client/startup/renderMessage/katex.ts | 17 +-- client/startup/renderMessage/markdown.ts | 17 +-- .../startup/renderMessage/mentionsMessage.ts | 20 +--- client/startup/renderNotification/markdown.ts | 22 ++-- client/startup/{startup.js => startup.ts} | 42 +++---- client/startup/streamMessage/autotranslate.ts | 3 +- client/startup/streamMessage/googlevision.ts | 7 +- client/startup/{theme.js => theme.ts} | 78 ++++++------- client/startup/{unread.js => unread.ts} | 24 ++-- client/types/global.d.ts | 15 +++ client/types/konecty-user-presence.d.ts | 12 ++ client/types/less-browser.d.ts | 5 + client/types/meteor.d.ts | 3 + client/types/mizzao-timesync.d.ts | 5 + definition/ISubscription.ts | 1 + package-lock.json | 6 + package.json | 1 + 32 files changed, 277 insertions(+), 349 deletions(-) rename client/startup/{i18n.js => i18n.ts} (71%) rename client/startup/{startup.js => startup.ts} (72%) rename client/startup/{theme.js => theme.ts} (61%) rename client/startup/{unread.js => unread.ts} (79%) create mode 100644 client/types/global.d.ts create mode 100644 client/types/konecty-user-presence.d.ts create mode 100644 client/types/less-browser.d.ts create mode 100644 client/types/mizzao-timesync.d.ts diff --git a/client/lib/userData.ts b/client/lib/userData.ts index c77634c1a0063..8cf3f1fbd0bd4 100644 --- a/client/lib/userData.ts +++ b/client/lib/userData.ts @@ -27,12 +27,12 @@ const updateUser = (userData: IUser & { _updatedAt: Date }): void => { Meteor.users.update({ _id: user._id }, { $set: userData }); }; -export const synchronizeUserData = async (uid: Meteor.User['_id']): Promise => { +export const synchronizeUserData = async (uid: Meteor.User['_id']): Promise => { if (!uid) { return; } - await Notifications.onUser('userData', (data: IUserDataEvent) => { + Notifications.onUser('userData', (data: IUserDataEvent) => { switch (data.type) { case 'inserted': // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/client/startup/.eslintrc.js b/client/startup/.eslintrc.js index 3b8b64ca4499d..26eb23275c671 100644 --- a/client/startup/.eslintrc.js +++ b/client/startup/.eslintrc.js @@ -8,12 +8,7 @@ module.exports = { 'error', { 'newlines-between': 'always', - 'groups': [ - 'builtin', - 'external', - 'internal', - ['parent', 'sibling', 'index'], - ], + 'groups': ['builtin', 'external', 'internal', ['parent', 'sibling', 'index']], 'alphabetize': { order: 'asc', }, @@ -77,12 +72,7 @@ module.exports = { 'error', { 'newlines-between': 'always', - 'groups': [ - 'builtin', - 'external', - 'internal', - ['parent', 'sibling', 'index'], - ], + 'groups': ['builtin', 'external', 'internal', ['parent', 'sibling', 'index']], 'alphabetize': { order: 'asc', }, diff --git a/client/startup/.prettierrc b/client/startup/.prettierrc index 7626cd71d5ad3..0244eac568448 100644 --- a/client/startup/.prettierrc +++ b/client/startup/.prettierrc @@ -4,7 +4,7 @@ "arrowParens": "always", "endOfLine": "lf", "jsxSingleQuote": true, - "printWidth": 80, + "printWidth": 100, "quoteProps": "consistent", "singleQuote": true, "trailingComma": "all", diff --git a/client/startup/e2e.ts b/client/startup/e2e.ts index cc1a27f1a19ce..bc5b74c668a93 100644 --- a/client/startup/e2e.ts +++ b/client/startup/e2e.ts @@ -28,8 +28,7 @@ Meteor.startup(() => { return; } - const adminEmbedded = - Layout.isEmbedded() && FlowRouter.current().path.startsWith('/admin'); + const adminEmbedded = Layout.isEmbedded() && FlowRouter.current().path.startsWith('/admin'); if (!adminEmbedded && settings.get('E2E_Enable') && window.crypto) { e2e.startClient(); @@ -116,9 +115,7 @@ Meteor.startup(() => { return message; } - const subscription = await waitUntilFind(() => - Rooms.findOne({ _id: message.rid }), - ); + const subscription = await waitUntilFind(() => Rooms.findOne({ _id: message.rid })); subscription.encrypted ? e2eRoom.resume() : e2eRoom.pause(); diff --git a/client/startup/i18n.js b/client/startup/i18n.ts similarity index 71% rename from client/startup/i18n.js rename to client/startup/i18n.ts index 78b9a3f41e404..49d3cebd982a3 100644 --- a/client/startup/i18n.js +++ b/client/startup/i18n.ts @@ -8,15 +8,16 @@ import { Users } from '../../app/models/client'; import { settings } from '../../app/settings/client'; import { isRtl } from '../../app/utils/client'; -const currentLanguage = new ReactiveVar(); +const currentLanguage = new ReactiveVar(null); Meteor.startup(() => { + // eslint-disable-next-line @typescript-eslint/camelcase TAPi18n.conf.i18n_files_route = Meteor._relativeToSiteRootUrl('/tap-i18n'); currentLanguage.set(Meteor._localStorage.getItem('userLanguage')); const availableLanguages = TAPi18n.getLanguages(); - const filterLanguage = (language) => { + const filterLanguage = (language: string): string => { // Fix browsers having all-lowercase language settings eg. pt-br, en-us const regex = /([a-z]{2,3})-([a-z]{2,4})/; const matches = regex.exec(language); @@ -27,17 +28,17 @@ Meteor.startup(() => { return language; }; - const getBrowserLanguage = () => - filterLanguage(window.navigator.userLanguage || window.navigator.language); + const getBrowserLanguage = (): string => + filterLanguage(window.navigator.userLanguage ?? window.navigator.language); - const loadMomentLocale = (language) => + const loadMomentLocale = (language: string): Promise => new Promise((resolve, reject) => { if (moment.locales().includes(language.toLowerCase())) { resolve(language); return; } - Meteor.call('loadLocale', language, (error, localeSrc) => { + Meteor.call('loadLocale', language, (error: unknown, localeSrc: string) => { if (error) { reject(error); return; @@ -48,7 +49,7 @@ Meteor.startup(() => { }); }); - const applyLanguage = (language = 'en') => { + const applyLanguage = (language: string | undefined = 'en'): void => { language = filterLanguage(language); if (!availableLanguages[language]) { @@ -58,14 +59,9 @@ Meteor.startup(() => { if (!language) { return; } - document.documentElement.classList[isRtl(language) ? 'add' : 'remove']( - 'rtl', - ); - document.documentElement.setAttribute( - 'dir', - isRtl(language) ? 'rtl' : 'ltr', - ); - document.querySelector('html').lang = language; + document.documentElement.classList[isRtl(language) ? 'add' : 'remove']('rtl'); + document.documentElement.setAttribute('dir', isRtl(language) ? 'rtl' : 'ltr'); + document.documentElement.lang = language; TAPi18n.setLanguage(language); loadMomentLocale(language) @@ -76,14 +72,14 @@ Meteor.startup(() => { }); }; - const setLanguage = (language) => { + const setLanguage = (language: string): void => { const lang = filterLanguage(language); currentLanguage.set(lang); Meteor._localStorage.setItem('userLanguage', lang); }; window.setLanguage = setLanguage; - const defaultUserLanguage = () => + const defaultUserLanguage = (): string => settings.get('Language') || getBrowserLanguage() || 'en'; window.defaultUserLanguage = defaultUserLanguage; @@ -94,8 +90,9 @@ Meteor.startup(() => { }); Tracker.autorun(() => { - if (currentLanguage.get()) { - applyLanguage(currentLanguage.get()); + const language = currentLanguage.get(); + if (language) { + applyLanguage(language); } }); }); diff --git a/client/startup/notifications/konchatNotifications.ts b/client/startup/notifications/konchatNotifications.ts index 19ebf423bdb52..3467a73bcd8e7 100644 --- a/client/startup/notifications/konchatNotifications.ts +++ b/client/startup/notifications/konchatNotifications.ts @@ -5,11 +5,7 @@ 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 { fireGlobalEvent, readMessage, Layout } from '../../../app/ui-utils/client'; import { KonchatNotification } from '../../../app/ui/client'; import { getUserPreference } from '../../../app/utils/client'; import { IMessage } from '../../../definition/IMessage'; @@ -22,8 +18,7 @@ const notifyNewRoom = (sub: ISubscription): void => { } if ( - (!FlowRouter.getParam('name') || - FlowRouter.getParam('name') !== sub.name) && + (!FlowRouter.getParam('name') || FlowRouter.getParam('name') !== sub.name) && !sub.ls && sub.alert === true ) { @@ -92,34 +87,27 @@ Meteor.startup(() => { } }); - Notifications.onUser( - 'audioNotification', - (notification: AudioNotificationEvent) => { - const openedRoomId = Session.get('openedRoom'); + Notifications.onUser('audioNotification', (notification: AudioNotificationEvent) => { + const openedRoomId = Session.get('openedRoom'); - // This logic is duplicated in /client/startup/unread.coffee. - const hasFocus = readMessage.isEnable(); - const messageIsInOpenedRoom = openedRoomId === notification.payload.rid; - const muteFocusedConversations = getUserPreference( - Meteor.userId(), - 'muteFocusedConversations', - ); + // This logic is duplicated in /client/startup/unread.coffee. + const hasFocus = readMessage.isEnable(); + const messageIsInOpenedRoom = openedRoomId === notification.payload.rid; + const muteFocusedConversations = getUserPreference( + Meteor.userId(), + 'muteFocusedConversations', + ); - if (Layout.isEmbedded()) { - if (!hasFocus && messageIsInOpenedRoom) { - // Play a notification sound - KonchatNotification.newMessage(notification.payload.rid); - } - } else if ( - !hasFocus || - !messageIsInOpenedRoom || - !muteFocusedConversations - ) { + if (Layout.isEmbedded()) { + if (!hasFocus && messageIsInOpenedRoom) { // Play a notification sound KonchatNotification.newMessage(notification.payload.rid); } - }, - ); + } else if (!hasFocus || !messageIsInOpenedRoom || !muteFocusedConversations) { + // Play a notification sound + KonchatNotification.newMessage(notification.payload.rid); + } + }); CachedChatSubscription.onSyncData = (( action: 'changed' | 'removed', diff --git a/client/startup/notifications/updateAvatar.ts b/client/startup/notifications/updateAvatar.ts index 658959bf5d698..ef587fc8c2513 100644 --- a/client/startup/notifications/updateAvatar.ts +++ b/client/startup/notifications/updateAvatar.ts @@ -11,7 +11,6 @@ type UpdateAvatarEvent = { Meteor.startup(() => { Notifications.onLogged('updateAvatar', (data: UpdateAvatarEvent) => { const { username, etag } = data; - username && - Meteor.users.update({ username }, { $set: { avatarETag: etag } }); + username && Meteor.users.update({ username }, { $set: { avatarETag: etag } }); }); }); diff --git a/client/startup/notifications/usersNameChanged.ts b/client/startup/notifications/usersNameChanged.ts index 0998a94d3ba32..3087fa1651fb3 100644 --- a/client/startup/notifications/usersNameChanged.ts +++ b/client/startup/notifications/usersNameChanged.ts @@ -7,66 +7,63 @@ import { IUser } from '../../../definition/IUser'; type UsersNameChangedEvent = Partial; Meteor.startup(() => { - Notifications.onLogged( - 'Users:NameChanged', - ({ _id, name, username }: UsersNameChangedEvent) => { - Messages.update( - { - 'u._id': _id, + Notifications.onLogged('Users:NameChanged', ({ _id, name, username }: UsersNameChangedEvent) => { + Messages.update( + { + 'u._id': _id, + }, + { + $set: { + 'u.username': username, + 'u.name': name, }, - { - $set: { - 'u.username': username, - 'u.name': name, - }, - }, - { - multi: true, - }, - ); + }, + { + multi: true, + }, + ); - Messages.update( - { - mentions: { - $elemMatch: { _id }, - }, - }, - { - $set: { - 'mentions.$.username': username, - 'mentions.$.name': name, - }, + Messages.update( + { + mentions: { + $elemMatch: { _id }, }, - { - multi: true, + }, + { + $set: { + 'mentions.$.username': username, + 'mentions.$.name': name, }, - ); + }, + { + multi: true, + }, + ); - Subscriptions.update( - { - name: username, - t: 'd', + Subscriptions.update( + { + name: username, + t: 'd', + }, + { + $set: { + fname: name, }, - { - $set: { - fname: name, - }, - }, - ); + }, + ); - RoomRoles.update( - { - 'u._id': _id, - }, - { - $set: { - 'u.name': name, - }, - }, - { - multi: true, + RoomRoles.update( + { + 'u._id': _id, + }, + { + $set: { + 'u.name': name, }, - ); - }, - ); + }, + { + multi: true, + }, + ); + }); }); diff --git a/client/startup/renderMessage/autolinker.ts b/client/startup/renderMessage/autolinker.ts index 7da7a7063ed78..bf240aab3ea76 100644 --- a/client/startup/renderMessage/autolinker.ts +++ b/client/startup/renderMessage/autolinker.ts @@ -24,17 +24,10 @@ Meteor.startup(() => { phone: settings.get('AutoLinker_Phone'), }; - import('../../../app/autolinker/client').then( - ({ createAutolinkerMessageRenderer }) => { - const renderMessage = createAutolinkerMessageRenderer(options); - callbacks.remove('renderMessage', 'autolinker'); - callbacks.add( - 'renderMessage', - renderMessage, - callbacks.priority.MEDIUM, - 'autolinker', - ); - }, - ); + import('../../../app/autolinker/client').then(({ createAutolinkerMessageRenderer }) => { + const renderMessage = createAutolinkerMessageRenderer(options); + callbacks.remove('renderMessage', 'autolinker'); + callbacks.add('renderMessage', renderMessage, callbacks.priority.MEDIUM, 'autolinker'); + }); }); }); diff --git a/client/startup/renderMessage/autotranslate.ts b/client/startup/renderMessage/autotranslate.ts index da7bacc0f5ead..9142d4670a364 100644 --- a/client/startup/renderMessage/autotranslate.ts +++ b/client/startup/renderMessage/autotranslate.ts @@ -7,25 +7,17 @@ import { settings } from '../../../app/settings/client'; Meteor.startup(() => { Tracker.autorun(() => { - const isEnabled = - settings.get('AutoTranslate_Enabled') && hasPermission('auto-translate'); + const isEnabled = settings.get('AutoTranslate_Enabled') && hasPermission('auto-translate'); if (!isEnabled) { callbacks.remove('renderMessage', 'autotranslate'); return; } - import('../../../app/autotranslate/client').then( - ({ createAutoTranslateMessageRenderer }) => { - const renderMessage = createAutoTranslateMessageRenderer(); - callbacks.remove('renderMessage', 'autotranslate'); - callbacks.add( - 'renderMessage', - renderMessage, - callbacks.priority.HIGH - 3, - 'autotranslate', - ); - }, - ); + import('../../../app/autotranslate/client').then(({ createAutoTranslateMessageRenderer }) => { + const renderMessage = createAutoTranslateMessageRenderer(); + callbacks.remove('renderMessage', 'autotranslate'); + callbacks.add('renderMessage', renderMessage, callbacks.priority.HIGH - 3, 'autotranslate'); + }); }); }); diff --git a/client/startup/renderMessage/emoji.ts b/client/startup/renderMessage/emoji.ts index 22c6cd733172c..a5abc9d827739 100644 --- a/client/startup/renderMessage/emoji.ts +++ b/client/startup/renderMessage/emoji.ts @@ -13,17 +13,10 @@ Meteor.startup(() => { return; } - import('../../../app/emoji/client').then( - ({ createEmojiMessageRenderer }) => { - const renderMessage = createEmojiMessageRenderer(); - callbacks.remove('renderMessage', 'emoji'); - callbacks.add( - 'renderMessage', - renderMessage, - callbacks.priority.LOW, - 'emoji', - ); - }, - ); + import('../../../app/emoji/client').then(({ createEmojiMessageRenderer }) => { + const renderMessage = createEmojiMessageRenderer(); + callbacks.remove('renderMessage', 'emoji'); + callbacks.add('renderMessage', renderMessage, callbacks.priority.LOW, 'emoji'); + }); }); }); diff --git a/client/startup/renderMessage/googlevision.ts b/client/startup/renderMessage/googlevision.ts index ae350bc6f2835..73abc30ea08bf 100644 --- a/client/startup/renderMessage/googlevision.ts +++ b/client/startup/renderMessage/googlevision.ts @@ -13,17 +13,10 @@ Meteor.startup(() => { return; } - import('../../../app/google-vision/client').then( - ({ createGoogleVisionMessageRenderer }) => { - const renderMessage = createGoogleVisionMessageRenderer(); - callbacks.remove('renderMessage', 'googlevision'); - callbacks.add( - 'renderMessage', - renderMessage, - callbacks.priority.HIGH - 3, - 'googlevision', - ); - }, - ); + import('../../../app/google-vision/client').then(({ createGoogleVisionMessageRenderer }) => { + const renderMessage = createGoogleVisionMessageRenderer(); + callbacks.remove('renderMessage', 'googlevision'); + callbacks.add('renderMessage', renderMessage, callbacks.priority.HIGH - 3, 'googlevision'); + }); }); }); diff --git a/client/startup/renderMessage/hexcolor.ts b/client/startup/renderMessage/hexcolor.ts index 302e45d5f96f6..c24b9dc559e96 100644 --- a/client/startup/renderMessage/hexcolor.ts +++ b/client/startup/renderMessage/hexcolor.ts @@ -13,17 +13,10 @@ Meteor.startup(() => { return; } - import('../../../app/colors/client').then( - ({ createHexColorPreviewMessageRenderer }) => { - const renderMessage = createHexColorPreviewMessageRenderer(); - callbacks.remove('renderMessage', 'hexcolor'); - callbacks.add( - 'renderMessage', - renderMessage, - callbacks.priority.MEDIUM, - 'hexcolor', - ); - }, - ); + import('../../../app/colors/client').then(({ createHexColorPreviewMessageRenderer }) => { + const renderMessage = createHexColorPreviewMessageRenderer(); + callbacks.remove('renderMessage', 'hexcolor'); + callbacks.add('renderMessage', renderMessage, callbacks.priority.MEDIUM, 'hexcolor'); + }); }); }); diff --git a/client/startup/renderMessage/highlightWords.ts b/client/startup/renderMessage/highlightWords.ts index caa07ddcb941b..928565d238c54 100644 --- a/client/startup/renderMessage/highlightWords.ts +++ b/client/startup/renderMessage/highlightWords.ts @@ -10,8 +10,7 @@ Meteor.startup(() => { Meteor.userId(), 'highlights', ); - const isEnabled = - highlights?.some((highlight) => highlight?.trim()) ?? false; + const isEnabled = highlights?.some((highlight) => highlight?.trim()) ?? false; if (!isEnabled) { callbacks.remove('renderMessage', 'highlight-words'); @@ -22,17 +21,15 @@ Meteor.startup(() => { wordsToHighlight: highlights?.filter((highlight) => highlight?.trim()), }; - import('../../../app/highlight-words').then( - ({ createHighlightWordsMessageRenderer }) => { - const renderMessage = createHighlightWordsMessageRenderer(options); - callbacks.remove('renderMessage', 'highlight-words'); - callbacks.add( - 'renderMessage', - renderMessage, - callbacks.priority.MEDIUM + 1, - 'highlight-words', - ); - }, - ); + import('../../../app/highlight-words').then(({ createHighlightWordsMessageRenderer }) => { + const renderMessage = createHighlightWordsMessageRenderer(options); + callbacks.remove('renderMessage', 'highlight-words'); + callbacks.add( + 'renderMessage', + renderMessage, + callbacks.priority.MEDIUM + 1, + 'highlight-words', + ); + }); }); }); diff --git a/client/startup/renderMessage/issuelink.ts b/client/startup/renderMessage/issuelink.ts index 0be15dfe80c64..7a465e15b19a8 100644 --- a/client/startup/renderMessage/issuelink.ts +++ b/client/startup/renderMessage/issuelink.ts @@ -17,17 +17,10 @@ Meteor.startup(() => { template: settings.get('IssueLinks_Template'), }; - import('../../../app/issuelinks/client').then( - ({ createIssueLinksMessageRenderer }) => { - const renderMessage = createIssueLinksMessageRenderer(options); - callbacks.remove('renderMessage', 'issuelink'); - callbacks.add( - 'renderMessage', - renderMessage, - callbacks.priority.MEDIUM, - 'issuelink', - ); - }, - ); + import('../../../app/issuelinks/client').then(({ createIssueLinksMessageRenderer }) => { + const renderMessage = createIssueLinksMessageRenderer(options); + callbacks.remove('renderMessage', 'issuelink'); + callbacks.add('renderMessage', renderMessage, callbacks.priority.MEDIUM, 'issuelink'); + }); }); }); diff --git a/client/startup/renderMessage/katex.ts b/client/startup/renderMessage/katex.ts index 993c3c5dc5635..4f5d042d1b3a5 100644 --- a/client/startup/renderMessage/katex.ts +++ b/client/startup/renderMessage/katex.ts @@ -18,17 +18,10 @@ Meteor.startup(() => { parenthesisSyntax: settings.get('Katex_Parenthesis_Syntax'), }; - import('../../../app/katex/client').then( - ({ createKatexMessageRendering }) => { - const renderMessage = createKatexMessageRendering(options); - callbacks.remove('renderMessage', 'katex'); - callbacks.add( - 'renderMessage', - renderMessage, - callbacks.priority.HIGH + 1, - 'katex', - ); - }, - ); + import('../../../app/katex/client').then(({ createKatexMessageRendering }) => { + const renderMessage = createKatexMessageRendering(options); + callbacks.remove('renderMessage', 'katex'); + callbacks.add('renderMessage', renderMessage, callbacks.priority.HIGH + 1, 'katex'); + }); }); }); diff --git a/client/startup/renderMessage/markdown.ts b/client/startup/renderMessage/markdown.ts index 8a0d8e89984b8..e38f62bb89bc5 100644 --- a/client/startup/renderMessage/markdown.ts +++ b/client/startup/renderMessage/markdown.ts @@ -21,17 +21,10 @@ Meteor.startup(() => { }, }; - import('../../../app/markdown/client').then( - ({ createMarkdownMessageRenderer }) => { - const renderMessage = createMarkdownMessageRenderer(options); - callbacks.remove('renderMessage', 'markdown'); - callbacks.add( - 'renderMessage', - renderMessage, - callbacks.priority.HIGH, - 'markdown', - ); - }, - ); + import('../../../app/markdown/client').then(({ createMarkdownMessageRenderer }) => { + const renderMessage = createMarkdownMessageRenderer(options); + callbacks.remove('renderMessage', 'markdown'); + callbacks.add('renderMessage', renderMessage, callbacks.priority.HIGH, 'markdown'); + }); }); }); diff --git a/client/startup/renderMessage/mentionsMessage.ts b/client/startup/renderMessage/mentionsMessage.ts index 4a4741d471c39..2f3e9007b09ed 100644 --- a/client/startup/renderMessage/mentionsMessage.ts +++ b/client/startup/renderMessage/mentionsMessage.ts @@ -9,23 +9,15 @@ Meteor.startup(() => { Tracker.autorun(() => { const uid = Meteor.userId(); const options = { - me: - uid && (Users.findOne(uid, { fields: { username: 1 } }) || {}).username, + me: uid && (Users.findOne(uid, { fields: { username: 1 } }) || {}).username, pattern: settings.get('UTF8_Names_Validation'), useRealName: settings.get('UI_Use_Real_Name'), }; - import('../../../app/mentions/client').then( - ({ createMentionsMessageRenderer }) => { - const renderMessage = createMentionsMessageRenderer(options); - callbacks.remove('renderMessage', 'mentions-message'); - callbacks.add( - 'renderMessage', - renderMessage, - callbacks.priority.MEDIUM, - 'mentions-message', - ); - }, - ); + import('../../../app/mentions/client').then(({ createMentionsMessageRenderer }) => { + const renderMessage = createMentionsMessageRenderer(options); + callbacks.remove('renderMessage', 'mentions-message'); + callbacks.add('renderMessage', renderMessage, callbacks.priority.MEDIUM, 'mentions-message'); + }); }); }); diff --git a/client/startup/renderNotification/markdown.ts b/client/startup/renderNotification/markdown.ts index 8a22279a616ff..80b28bed11cc4 100644 --- a/client/startup/renderNotification/markdown.ts +++ b/client/startup/renderNotification/markdown.ts @@ -8,16 +8,14 @@ Meteor.startup(() => { supportSchemesForLink: settings.get('Markdown_SupportSchemesForLink'), }; - import('../../../app/markdown/client').then( - ({ createMarkdownNotificationRenderer }) => { - const renderNotification = createMarkdownNotificationRenderer(options); - callbacks.remove('renderNotification', 'filter-markdown'); - callbacks.add( - 'renderNotification', - renderNotification, - callbacks.priority.HIGH, - 'filter-markdown', - ); - }, - ); + import('../../../app/markdown/client').then(({ createMarkdownNotificationRenderer }) => { + const renderNotification = createMarkdownNotificationRenderer(options); + callbacks.remove('renderNotification', 'filter-markdown'); + callbacks.add( + 'renderNotification', + renderNotification, + callbacks.priority.HIGH, + 'filter-markdown', + ); + }); }); diff --git a/client/startup/startup.js b/client/startup/startup.ts similarity index 72% rename from client/startup/startup.js rename to client/startup/startup.ts index df9f9c539cfeb..89b0a636c658b 100644 --- a/client/startup/startup.js +++ b/client/startup/startup.ts @@ -11,6 +11,7 @@ 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 { USER_STATUS } from '../../definition/UserStatus'; import * as banners from '../lib/banners'; import { synchronizeUserData } from '../lib/userData'; @@ -33,7 +34,7 @@ Meteor.startup(() => { window.lastMessageWindow = {}; window.lastMessageWindowHistory = {}; - let status = undefined; + let status: USER_STATUS | undefined = undefined; Tracker.autorun(async () => { const uid = Meteor.userId(); if (!uid) { @@ -64,7 +65,7 @@ Meteor.startup(() => { } }); - const autoRunHandler = Tracker.autorun(async () => { + Tracker.autorun(async (c) => { const uid = Meteor.userId(); if (!uid) { return; @@ -74,22 +75,25 @@ Meteor.startup(() => { return; } - Meteor.call('cloud:checkRegisterStatus', (err, data) => { - if (err) { - console.log(err); - return; - } - - autoRunHandler.stop(); - const { connectToCloud = false, workspaceRegistered = false } = data; - if (connectToCloud === true && workspaceRegistered !== true) { - banners.open({ - id: 'cloud-registration', - title: t('Cloud_registration_pending_title'), - html: t('Cloud_registration_pending_html'), - modifiers: ['large', 'danger'], - }); - } - }); + Meteor.call( + 'cloud:checkRegisterStatus', + (err: unknown, data: { connectToCloud?: boolean; workspaceRegistered?: boolean }) => { + if (err) { + console.log(err); + return; + } + + c.stop(); + const { connectToCloud = false, workspaceRegistered = false } = data; + if (connectToCloud === true && workspaceRegistered !== true) { + banners.open({ + id: 'cloud-registration', + title: t('Cloud_registration_pending_title'), + html: t('Cloud_registration_pending_html'), + modifiers: ['large', 'danger'], + }); + } + }, + ); }); }); diff --git a/client/startup/streamMessage/autotranslate.ts b/client/startup/streamMessage/autotranslate.ts index eb5151cf88e69..c543998999b98 100644 --- a/client/startup/streamMessage/autotranslate.ts +++ b/client/startup/streamMessage/autotranslate.ts @@ -7,8 +7,7 @@ import { settings } from '../../../app/settings/client'; Meteor.startup(() => { Tracker.autorun(() => { - const isEnabled = - settings.get('AutoTranslate_Enabled') && hasPermission('auto-translate'); + const isEnabled = settings.get('AutoTranslate_Enabled') && hasPermission('auto-translate'); if (!isEnabled) { callbacks.remove('streamMessage', 'autotranslate-stream'); diff --git a/client/startup/streamMessage/googlevision.ts b/client/startup/streamMessage/googlevision.ts index afbef49e460fd..4d2347952d6db 100644 --- a/client/startup/streamMessage/googlevision.ts +++ b/client/startup/streamMessage/googlevision.ts @@ -17,12 +17,7 @@ Meteor.startup(() => { ({ createGoogleVisionMessageStreamHandler }) => { const streamMessage = createGoogleVisionMessageStreamHandler(); callbacks.remove('streamMessage', 'googlevision'); - callbacks.add( - 'streamMessage', - streamMessage, - callbacks.priority.HIGH - 3, - 'googlevision', - ); + callbacks.add('streamMessage', streamMessage, callbacks.priority.HIGH - 3, 'googlevision'); }, ); }); diff --git a/client/startup/theme.js b/client/startup/theme.ts similarity index 61% rename from client/startup/theme.js rename to client/startup/theme.ts index 4d6712c1903b1..f1ea2e56cceb1 100644 --- a/client/startup/theme.js +++ b/client/startup/theme.ts @@ -3,20 +3,15 @@ import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; import { settings } from '../../app/settings/client'; +import { ISetting } from '../../definition/ISetting'; const variables = new Map(); const lessExpressions = new Map([ ['default-action-color', 'darken(@secondary-background-color, 15%)'], ['default-action-contrast', 'contrast(@default-action-color, #444444)'], - [ - 'primary-background-contrast', - 'contrast(@primary-background-color, #444444)', - ], + ['primary-background-contrast', 'contrast(@primary-background-color, #444444)'], ['primary-action-contrast', 'contrast(@primary-action-color, #444444)'], - [ - 'secondary-background-contrast', - 'contrast(@secondary-background-color, #444444)', - ], + ['secondary-background-contrast', 'contrast(@secondary-background-color, #444444)'], ['secondary-action-contrast', 'contrast(@secondary-action-color, #444444)'], ['selection-background', 'lighten(@selection-color, 30%)'], ['success-background', 'lighten(@success-color, 45%)'], @@ -36,25 +31,16 @@ const lessExpressions = new Map([ const less = createLess(window, {}); -const compileLess = async () => { +const compileLess = async (): Promise => { if (lessExpressions.size === 0) { return ''; } const lessCode = [ - ...Array.from( - variables.entries(), - ([name, value]) => `@${name}: ${value};`, - ), - ...Array.from( - lessExpressions.entries(), - ([name, expression]) => `@${name}: ${expression};`, - ), + ...Array.from(variables.entries(), ([name, value]) => `@${name}: ${value};`), + ...Array.from(lessExpressions.entries(), ([name, expression]) => `@${name}: ${expression};`), ':root {', - ...Array.from( - lessExpressions.entries(), - ([name]) => `--${name}: @${name};`, - ), + ...Array.from(lessExpressions.entries(), ([name]) => `--${name}: @${name};`), '}', ].join('\n'); @@ -67,27 +53,33 @@ const compileLess = async () => { } }; -let cssVariablesElement; +let cssVariablesElement: HTMLStyleElement | null = null; const updateCssVariables = _.debounce(async () => { if (!cssVariablesElement) { cssVariablesElement = document.querySelector('#css-variables'); } + if (!cssVariablesElement) { + return; + } + cssVariablesElement.innerHTML = [ ':root {', - ...Array.from( - variables.entries(), - ([name, value]) => `--${name}: ${value};`, - ), + ...Array.from(variables.entries(), ([name, value]) => `--${name}: ${value};`), '}', await compileLess(), ].join('\n'); }, 50); -const handleThemeColorChanged = ({ _id, value, editor }) => { +const handleThemeColorChanged = ({ _id, value, editor }: ISetting & { value: string }): void => { try { - const name = /^theme-color-(.*)$/.exec(_id)[1]; + const name = /^theme-color-(.*)$/.exec(_id)?.[1]; + + if (!name) { + return; + } + const legacy = name.slice(0, 3) !== 'rc-'; if (editor === 'color') { @@ -106,9 +98,13 @@ const handleThemeColorChanged = ({ _id, value, editor }) => { } }; -const handleThemeFontChanged = ({ _id, value }) => { +const handleThemeFontChanged = ({ _id, value }: ISetting & { value: string }): void => { try { - const name = /^theme-font-(.*)$/.exec(_id)[1]; + const name = /^theme-font-(.*)$/.exec(_id)?.[1]; + if (!name) { + return; + } + variables.set(name, value); } finally { updateCssVariables(); @@ -116,17 +112,13 @@ const handleThemeFontChanged = ({ _id, value }) => { }; Meteor.startup(() => { - settings.collection - .find({ _id: /^theme-color-/i }, { fields: { value: 1, editor: 1 } }) - .observe({ - added: handleThemeColorChanged, - changed: handleThemeColorChanged, - }); - - settings.collection - .find({ _id: /^theme-font-/i }, { fields: { value: 1 } }) - .observe({ - added: handleThemeFontChanged, - changed: handleThemeFontChanged, - }); + settings.collection.find({ _id: /^theme-color-/i }, { fields: { value: 1, editor: 1 } }).observe({ + added: handleThemeColorChanged, + changed: handleThemeColorChanged, + }); + + settings.collection.find({ _id: /^theme-font-/i }, { fields: { value: 1 } }).observe({ + added: handleThemeFontChanged, + changed: handleThemeFontChanged, + }); }); diff --git a/client/startup/unread.js b/client/startup/unread.ts similarity index 79% rename from client/startup/unread.js rename to client/startup/unread.ts index 6101031e0ff99..68197a3c47e42 100644 --- a/client/startup/unread.js +++ b/client/startup/unread.ts @@ -7,8 +7,9 @@ import { ChatSubscription, ChatRoom } from '../../app/models/client'; import { settings } from '../../app/settings/client'; import { menu, fireGlobalEvent } from '../../app/ui-utils/client'; import { getUserPreference } from '../../app/utils/client'; +import { ISubscription } from '../../definition/ISubscription'; -const fetchSubscriptions = () => +const fetchSubscriptions = (): ISubscription[] => ChatSubscription.find( { open: true, @@ -33,15 +34,12 @@ Meteor.startup(() => { Tracker.autorun(() => { const userUnreadAlert = getUserPreference(Meteor.userId(), 'unreadAlert'); - let unreadAlert = false; + let unreadAlert: false | '•' = false; const unreadCount = fetchSubscriptions().reduce( (ret, subscription) => Tracker.nonreactive(() => { - const room = ChatRoom.findOne( - { _id: subscription.rid }, - { fields: { usersCount: 1 } }, - ); + const room = ChatRoom.findOne({ _id: subscription.rid }, { fields: { usersCount: 1 } }); fireGlobalEvent('unread-changed-by-subscription', { ...subscription, usersCount: room && room.usersCount, @@ -49,14 +47,8 @@ Meteor.startup(() => { if (subscription.alert || subscription.unread > 0) { // Increment the total unread count. - if ( - subscription.alert === true && - subscription.unreadAlert !== 'nothing' - ) { - if ( - subscription.unreadAlert === 'all' || - userUnreadAlert !== false - ) { + if (subscription.alert === true && subscription.unreadAlert !== 'nothing') { + if (subscription.unreadAlert === 'all' || userUnreadAlert !== false) { unreadAlert = '•'; } } @@ -84,7 +76,7 @@ Meteor.startup(() => { }); Meteor.startup(() => { - const favicon = new Favico({ + const favicon = new (Favico as any)({ position: 'up', animation: 'none', }); @@ -92,7 +84,7 @@ Meteor.startup(() => { window.favico = favicon; Tracker.autorun(() => { - const siteName = settings.get('Site_Name') || ''; + const siteName = settings.get('Site_Name') ?? ''; const unread = Session.get('unread'); fireGlobalEvent('unread-changed', unread); diff --git a/client/types/global.d.ts b/client/types/global.d.ts new file mode 100644 index 0000000000000..5d288bbe2a98a --- /dev/null +++ b/client/types/global.d.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/interface-name-prefix */ + +interface Navigator { + /** @deprecated */ + readonly userLanguage?: string; +} + +interface Window { + setLanguage?: (language: string) => void; + defaultUserLanguage?: () => string; + DISABLE_ANIMATION?: boolean; + lastMessageWindow?: Record; + lastMessageWindowHistory?: Record; + favico?: any; +} diff --git a/client/types/konecty-user-presence.d.ts b/client/types/konecty-user-presence.d.ts new file mode 100644 index 0000000000000..5be541d465bad --- /dev/null +++ b/client/types/konecty-user-presence.d.ts @@ -0,0 +1,12 @@ +declare module 'meteor/konecty:user-presence' { + namespace UserPresenceMonitor { + function processUserSession(userSession: any, event: string): void; + } + + namespace UserPresence { + let awayTime: number | undefined; + function removeConnectionsByInstanceId(id: string): void; + function start(): void + function stopTimer(): void + } +} diff --git a/client/types/less-browser.d.ts b/client/types/less-browser.d.ts new file mode 100644 index 0000000000000..d9bc07eff65b1 --- /dev/null +++ b/client/types/less-browser.d.ts @@ -0,0 +1,5 @@ +declare function createLess(window: Window, options: Less.Options): LessStatic; + +declare module 'less/browser' { + export = createLess; +} diff --git a/client/types/meteor.d.ts b/client/types/meteor.d.ts index 2a5a731fee5e0..43723e81dd7cf 100644 --- a/client/types/meteor.d.ts +++ b/client/types/meteor.d.ts @@ -24,5 +24,8 @@ declare module 'meteor/meteor' { } const connection: IMeteorConnection; + + function _relativeToSiteRootUrl(path: string): void; + const _localStorage: Window['localStorage']; } } diff --git a/client/types/mizzao-timesync.d.ts b/client/types/mizzao-timesync.d.ts new file mode 100644 index 0000000000000..d4473c49db886 --- /dev/null +++ b/client/types/mizzao-timesync.d.ts @@ -0,0 +1,5 @@ +declare module 'meteor/mizzao:timesync' { + namespace TimeSync { + let loggingEnabled: boolean; + } +} diff --git a/definition/ISubscription.ts b/definition/ISubscription.ts index 122bea674aa39..019f22bcd433b 100644 --- a/definition/ISubscription.ts +++ b/definition/ISubscription.ts @@ -37,6 +37,7 @@ export interface ISubscription extends IRocketChatRecord { onHold?: boolean; encrypted?: boolean; E2EKey?: string; + unreadAlert?: 'default' | 'all' | 'mentions' | 'nothing'; } export interface ISubscriptionDirectMessage extends Omit { diff --git a/package-lock.json b/package-lock.json index b5ba67c161f9e..e472424ce2040 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10844,6 +10844,12 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=" }, + "@types/less": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/less/-/less-3.0.2.tgz", + "integrity": "sha512-62vfe65cMSzYaWmpmhqCMMNl0khen89w57mByPi1OseGfcV/LV03fO8YVrNj7rFQsRWNJo650WWyh6m7p8vZmA==", + "dev": true + }, "@types/lockfile": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/lockfile/-/lockfile-1.0.1.tgz", diff --git a/package.json b/package.json index bedd6415f4034..bc834dcdffc68 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "@types/clipboard": "^2.0.1", "@types/ejson": "^2.1.2", "@types/jsdom": "^16.2.6", + "@types/less": "^3.0.2", "@types/meteor": "^1.4.49", "@types/mocha": "^8.0.3", "@types/mock-require": "^2.0.0", From 67cd663a120d60ba52d2048634b07fb0905e5d7e Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Fri, 26 Mar 2021 11:14:55 -0300 Subject: [PATCH 011/124] Promote eslint configuration to the entire client/ directory --- .eslintignore | 2 +- client/{startup => }/.eslintrc.js | 0 client/{startup => }/.prettierrc | 0 client/UIKit/hooks/useUIKitHandleAction.tsx | 25 +- client/UIKit/hooks/useUIKitHandleClose.tsx | 39 +- client/UIKit/hooks/useUIKitStateManager.tsx | 5 +- client/adapters.ts | 10 +- client/components/AppRoot.js | 12 +- client/components/AutoComplete.js | 42 +- client/components/AutoComplete.stories.js | 2 +- client/components/AutoCompleteAgent.js | 39 +- client/components/AutoCompleteDepartment.js | 37 +- client/components/Backdrop.js | 2 +- .../Breadcrumbs/Breadcrumbs.stories.js | 23 +- client/components/Breadcrumbs/index.js | 94 ++- client/components/Card/Card.js | 68 ++- client/components/Card/Card.stories.js | 160 ++--- .../ConfirmOwnerChangeWarningModal.js | 90 ++- client/components/CustomFieldsForm.js | 163 +++-- client/components/DeleteChannelWarning.js | 36 +- client/components/DeleteFileWarning.js | 6 +- client/components/DeleteSuccessModal.tsx | 40 +- client/components/DeleteWarningModal.tsx | 46 +- client/components/DotLeader.stories.js | 14 +- client/components/DotLeader.tsx | 25 +- client/components/Emoji.js | 4 +- client/components/ExternalLink.tsx | 5 +- client/components/FilterByText.tsx | 25 +- client/components/GenericModal.stories.js | 5 +- client/components/GenericModal.tsx | 50 +- client/components/GenericTable.stories.js | 22 +- client/components/GenericTable/HeaderCell.tsx | 14 +- client/components/GenericTable/LoadingRow.tsx | 13 +- client/components/GenericTable/SortIcon.tsx | 30 +- client/components/GenericTable/index.js | 121 ++-- client/components/Header/Header.stories.js | 134 ++-- client/components/Header/Header.tsx | 177 ++++-- client/components/Logo.js | 70 ++- client/components/MarkdownText.stories.js | 5 +- client/components/MarkdownText.tsx | 19 +- client/components/Message/Actions/index.tsx | 24 +- .../Message/Attachments/ActionAttachtment.tsx | 52 +- .../Message/Attachments/Attachment.tsx | 138 ++++- .../Attachments/Attachments.stories.js | 74 ++- .../Message/Attachments/DefaultAttachment.tsx | 128 ++-- .../Message/Attachments/FieldsAttachment.tsx | 28 +- .../Attachments/Files/AudioAttachment.tsx | 36 +- .../Files/GenericFileAttachment.tsx | 30 +- .../Attachments/Files/ImageAttachment.tsx | 40 +- .../Attachments/Files/PDFAttachment.tsx | 36 +- .../Attachments/Files/VideoAttachment.tsx | 41 +- .../Message/Attachments/Files/index.tsx | 29 +- .../Message/Attachments/QuoteAttachment.tsx | 73 ++- .../Message/Attachments/components/Image.tsx | 120 ++-- .../Attachments/context/AttachmentContext.tsx | 9 +- .../Message/Attachments/hooks/useCollapse.tsx | 7 +- .../components/Message/Attachments/index.tsx | 32 +- .../providers/AttachmentProvider.tsx | 28 +- client/components/Message/Message.stories.js | 63 +- .../components/Message/Metrics/Broadcast.tsx | 15 +- .../components/Message/Metrics/Discussion.tsx | 27 +- client/components/Message/Metrics/Thread.tsx | 88 ++- .../components/Message/NotificationStatus.js | 2 +- .../components/Message/helpers/followSyle.js | 15 +- client/components/Message/index.tsx | 39 +- .../ModalSeparator/ModalSeparator.tsx | 6 +- client/components/NotAuthorizedPage.js | 14 +- .../components/NotAuthorizedPage.stories.js | 3 +- .../Omnichannel/modals/CloseChatModal.tsx | 65 +- .../Omnichannel/modals/ForwardChatModal.js | 109 ++-- .../modals/ReturnChatQueueModal.tsx | 44 +- .../Omnichannel/modals/TranscriptModal.tsx | 124 ++-- client/components/Page.stories.js | 42 +- client/components/Page.tsx | 139 +++-- client/components/PageSkeleton.js | 36 +- client/components/PlanTag.js | 13 +- client/components/PortalWrapper.js | 8 +- client/components/RoomAutoComplete.js | 60 +- client/components/RoomForeword.js | 57 +- .../components/ScrollableContentWrapper.tsx | 75 +-- client/components/Sidebar.js | 157 +++-- client/components/Skeleton.js | 20 +- client/components/SortList.js | 277 ++++++--- client/components/Subtitle.js | 18 +- client/components/TextCopy.js | 62 +- client/components/UTCClock.js | 4 +- client/components/UserCard.js | 186 ++++-- client/components/UserCard.stories.js | 12 +- client/components/UserStatus.js | 23 +- client/components/UserStatus.stories.js | 1 - client/components/UserStatusMenu.js | 63 +- client/components/VerticalBar.js | 169 ++--- client/components/avatar/AppAvatar.js | 8 +- client/components/avatar/BaseAvatar.tsx | 4 +- client/components/avatar/RoomAvatar.js | 4 +- client/components/avatar/RoomAvatarEditor.js | 66 +- client/components/avatar/UserAvatar.tsx | 9 +- client/components/avatar/UserAvatarEditor.js | 135 ++-- client/components/burger/BurgerBadge.js | 22 +- .../components/burger/BurgerBadge.stories.js | 15 +- client/components/burger/BurgerIcon.js | 70 ++- .../components/burger/BurgerIcon.stories.js | 13 +- client/components/burger/BurgerMenuButton.js | 28 +- .../burger/BurgerMenuButton.stories.js | 13 +- .../connectionStatus/ConnectionStatusAlert.js | 46 +- .../ConnectionStatusAlert.stories.js | 46 +- client/components/data/Counter.js | 32 +- client/components/data/Counter.stories.js | 12 +- client/components/data/CounterSet.js | 18 +- client/components/data/CounterSet.stories.js | 16 +- client/components/data/Growth.js | 10 +- client/components/data/Growth.stories.js | 16 +- .../components/data/NegativeGrowthSymbol.js | 12 +- .../data/NegativeGrowthSymbol.stories.js | 8 +- .../components/data/PositiveGrowthSymbol.js | 12 +- .../data/PositiveGrowthSymbol.stories.js | 14 +- client/components/helpers.js | 3 +- client/components/withDoNotAskAgain.tsx | 42 +- client/contexts/AuthorizationContext.ts | 49 +- client/contexts/AvatarUrlContext.ts | 11 +- client/contexts/CustomSoundContext.ts | 3 +- client/contexts/EditableSettingsContext.ts | 16 +- client/contexts/LayoutContext.ts | 7 +- client/contexts/OmnichannelContext.ts | 3 +- client/contexts/RouterContext.ts | 99 ++- .../contexts/ServerContext/ServerContext.ts | 78 ++- client/contexts/ServerContext/endpoints.ts | 32 +- .../endpoints/v1/chat/followMessage.ts | 6 +- .../endpoints/v1/chat/getDiscussions.ts | 2 +- .../endpoints/v1/chat/getMessage.ts | 4 +- .../endpoints/v1/chat/getThreadsList.ts | 2 +- .../endpoints/v1/chat/unfollowMessage.ts | 6 +- .../endpoints/v1/custom-user-status/list.ts | 4 +- .../endpoints/v1/emoji-custom/list.ts | 4 +- .../endpoints/v1/livechat/appearance.ts | 4 +- .../endpoints/v1/livechat/visitorInfo.ts | 4 +- .../endpoints/v1/teams/addRooms.ts | 5 +- client/contexts/ServerContext/methods.ts | 12 +- .../ServerContext/methods/saveRoomSettings.ts | 54 +- client/contexts/SettingsContext.ts | 21 +- client/contexts/SidebarContext.ts | 3 +- client/contexts/TranslationContext.ts | 21 +- client/contexts/UserContext.ts | 96 ++- client/hooks/lists/useRecordList.ts | 4 +- .../hooks/lists/useScrollableMessageList.ts | 28 +- client/hooks/lists/useScrollableRecordList.ts | 2 +- .../lists/useStreamUpdatesForMessageList.ts | 29 +- client/hooks/useAsyncState.ts | 55 +- client/hooks/useClipboardWithToast.js | 6 +- client/hooks/useComponentDidUpdate.js | 7 +- client/hooks/useDir.js | 4 +- client/hooks/useEndpointAction.js | 58 +- client/hooks/useEndpointData.ts | 23 +- client/hooks/useEndpointUpload.js | 33 +- client/hooks/useForm.ts | 137 +++-- client/hooks/useFormatDate.js | 2 +- client/hooks/useFormatDateAndTime.js | 27 +- client/hooks/useFormatDuration.js | 43 +- client/hooks/useFormatMemorySize.js | 2 +- client/hooks/useFormatTime.js | 25 +- client/hooks/usePolledMethodData.ts | 6 +- client/hooks/usePresence.ts | 4 +- client/hooks/usePreventProgation.ts | 4 +- client/hooks/useResizeInlineBreakpoint.js | 7 +- client/hooks/useRoomIcon.tsx | 19 +- client/hooks/useTimeAgo.js | 35 +- client/hooks/useTimezoneNameList.js | 2 +- client/hooks/useTimezoneTime.js | 2 +- client/hooks/useUpdateAvatar.js | 49 +- client/hooks/useUserData.ts | 23 +- client/lib/asyncState/AsyncState.ts | 15 +- client/lib/asyncState/AsyncStatePhase.ts | 2 +- client/lib/banners.ts | 8 +- client/lib/capitalize.spec.ts | 12 +- client/lib/clickableItem.js | 8 +- client/lib/createRouteGroup.ts | 26 +- client/lib/createSidebarItems.ts | 6 +- client/lib/download.spec.ts | 10 +- client/lib/download.ts | 38 +- client/lib/getUserEmailAddress.ts | 2 +- client/lib/getUserEmailVerified.ts | 2 +- client/lib/goToRoomById.ts | 2 +- client/lib/lists/DiscussionsList.ts | 14 +- client/lib/lists/FilesList.ts | 2 +- client/lib/lists/RecordList.ts | 11 +- client/lib/lists/ThreadsList.ts | 31 +- client/lib/meteorCallWrapper.ts | 14 +- client/lib/minimongo/bson.spec.ts | 2 +- client/lib/minimongo/comparisons.spec.ts | 8 +- client/lib/minimongo/comparisons.ts | 4 +- client/lib/minimongo/lookups.spec.ts | 4 +- client/lib/minimongo/lookups.ts | 3 +- client/lib/minimongo/query.ts | 148 +++-- client/lib/minimongo/sort.ts | 40 +- client/lib/minimongo/types.ts | 17 +- client/lib/presence.ts | 49 +- client/lib/renderMessageBody.ts | 12 +- .../PrivateSettingsCachedCollection.ts | 15 +- client/lib/userData.ts | 6 +- client/methods/deleteMessage.js | 14 +- client/methods/hideRoom.js | 19 +- client/methods/openRoom.js | 17 +- client/methods/toggleFavorite.js | 17 +- client/methods/updateMessage.js | 29 +- .../directory/OmnichannelDirectoryPage.js | 138 +++-- client/omnichannel/directory/Skeleton.js | 20 +- client/omnichannel/directory/chats/ChatTab.js | 197 ++++-- .../directory/chats/contextualBar/ChatInfo.js | 236 ++++--- .../chats/contextualBar/ChatRoomEdit.js | 214 ++++--- .../directory/chats/contextualBar/index.js | 22 +- .../directory/contacts/ContactTab.js | 176 ++++-- .../contacts/contextualBar/ContactForm.js | 201 +++--- .../contacts/contextualBar/ContactInfo.js | 175 +++--- .../directory/contacts/contextualBar/index.js | 24 +- client/omnichannel/routes.js | 4 +- client/polyfills/childNodeRemove.ts | 6 +- client/polyfills/cssVars.ts | 45 +- client/polyfills/customEventPolyfill.ts | 15 +- client/polyfills/objectFromEntries.ts | 8 +- client/providers/AuthorizationProvider.tsx | 40 +- client/providers/AvatarUrlProvider.tsx | 35 +- client/providers/ConnectionStatusProvider.tsx | 5 +- client/providers/CustomSoundProvider.tsx | 5 +- client/providers/EditableSettingsProvider.tsx | 134 ++-- client/providers/LayoutProvider.tsx | 55 +- client/providers/MeteorProvider.js | 69 ++- client/providers/ModalProvider.js | 28 +- client/providers/OmniChannelProvider.tsx | 114 ++-- client/providers/RouterProvider.tsx | 11 +- client/providers/ServerProvider.js | 26 +- client/providers/SessionProvider.tsx | 7 +- client/providers/SettingsProvider.tsx | 121 ++-- client/providers/SidebarProvider.tsx | 5 +- client/providers/ToastMessagesProvider.tsx | 9 +- client/providers/TranslationProvider.js | 24 +- client/providers/UserProvider.tsx | 57 +- .../createReactiveSubscriptionFactory.ts | 43 +- client/reactAdapters/createEphemeralPortal.ts | 11 +- .../createTemplateForComponent.ts | 10 +- client/reactAdapters/mountRoot.ts | 5 +- client/reactAdapters/portalsSubscription.ts | 6 +- client/reactAdapters/renderRouteComponent.ts | 16 +- client/routes.ts | 47 +- client/sidebar/Item/Condensed.js | 91 +-- client/sidebar/Item/Condensed.stories.js | 125 ++-- client/sidebar/Item/Extended.js | 127 ++-- client/sidebar/Item/Extended.stories.js | 172 +++--- client/sidebar/Item/ExtendedSkeleton.js | 18 +- client/sidebar/Item/Medium.js | 91 +-- client/sidebar/Item/Medium.stories.js | 123 ++-- .../Item/skeletons/CondensedSkeleton.js | 16 +- .../sidebar/Item/skeletons/MediumSkeleton.js | 16 +- .../Item/skeletons/Skeleton.stories.js | 2 +- client/sidebar/RoomList.js | 331 ++++++---- client/sidebar/RoomMenu.js | 289 +++++---- client/sidebar/Sidebar.stories.js | 32 +- client/sidebar/header/CreateChannel.js | 276 +++++---- client/sidebar/header/UserAvatarButton.js | 57 +- client/sidebar/header/UserDropdown.js | 171 +++--- client/sidebar/header/actions/CreateRoom.js | 24 +- .../sidebar/header/actions/CreateRoomList.js | 83 ++- .../header/actions/CreateRoomListItem.js | 38 +- client/sidebar/header/actions/Directory.js | 4 +- client/sidebar/header/actions/Home.js | 4 +- client/sidebar/header/actions/Login.js | 13 +- client/sidebar/header/actions/Search.js | 13 +- client/sidebar/header/actions/Sort.js | 12 +- client/sidebar/header/index.js | 50 +- client/sidebar/hooks/useAvatarTemplate.js | 4 +- client/sidebar/hooks/useQueryOptions.js | 19 +- client/sidebar/hooks/useRoomList.ts | 37 +- .../sidebar/hooks/useSidebarPaletteColor.js | 234 ++++--- client/sidebar/search/SearchList.js | 258 +++++--- client/sidebar/sections/Omnichannel.js | 33 +- client/types/fuselage-ui-kit.d.ts | 23 +- client/types/fuselage.d.ts | 32 +- client/types/kadira-flow-router.d.ts | 44 +- client/types/konecty-user-presence.d.ts | 4 +- client/types/meteor-mongo.d.ts | 13 +- client/views/InfoPanel/InfoPanel.stories.js | 63 +- client/views/InfoPanel/InfoPanel.tsx | 85 ++- .../InfoPanel/RetentionPolicyCallout.tsx | 32 +- .../views/account/AccountIntegrationsPage.js | 55 +- client/views/account/AccountProfileForm.js | 414 +++++++++---- client/views/account/AccountProfilePage.js | 193 +++--- client/views/account/AccountRoute.js | 10 +- client/views/account/AccountSidebar.js | 22 +- client/views/account/ActionConfirmModal.tsx | 57 +- .../preferences/AccountPreferencesPage.js | 111 ++-- .../views/account/preferences/MyDataModal.tsx | 36 +- .../preferences/PreferencesGlobalSection.js | 41 +- .../PreferencesHighlightsSection.js | 30 +- .../PreferencesLocalizationSection.js | 29 +- .../preferences/PreferencesMessagesSection.js | 314 ++++++---- .../preferences/PreferencesMyDataSection.js | 146 +++-- .../PreferencesNotificationsSection.js | 219 ++++--- .../preferences/PreferencesSoundSection.js | 130 ++-- .../PreferencesUserPresenceSection.js | 66 +- .../account/security/AccountSecurityPage.js | 50 +- .../account/security/BackupCodesModal.tsx | 46 +- client/views/account/security/EndToEnd.js | 77 ++- .../views/account/security/TwoFactorEmail.js | 57 +- .../views/account/security/TwoFactorTOTP.js | 92 +-- .../account/security/VerifyCodeModal.tsx | 65 +- client/views/account/sidebarItems.js | 17 +- .../views/account/tokens/AccountTokensPage.js | 20 +- .../views/account/tokens/AccountTokensRow.tsx | 32 +- .../account/tokens/AccountTokensTable.js | 201 +++--- client/views/account/tokens/AddToken.js | 90 ++- client/views/account/tokens/InfoModal.tsx | 19 +- client/views/admin/AdministrationLayout.tsx | 4 +- client/views/admin/AdministrationRouter.js | 18 +- client/views/admin/apps/APIsDisplay.tsx | 49 +- client/views/admin/apps/AppDetailsPage.js | 75 ++- .../admin/apps/AppDetailsPageContent.tsx | 186 ++++-- client/views/admin/apps/AppInstallPage.js | 108 ++-- client/views/admin/apps/AppLogsPage.js | 107 ++-- client/views/admin/apps/AppMenu.js | 170 +++--- .../admin/apps/AppPermissionsReviewModal.js | 72 +-- client/views/admin/apps/AppRow.tsx | 102 ++-- client/views/admin/apps/AppSettings.js | 99 +-- client/views/admin/apps/AppStatus.js | 92 ++- client/views/admin/apps/AppsContext.tsx | 2 +- client/views/admin/apps/AppsPage.js | 34 +- client/views/admin/apps/AppsProvider.tsx | 92 +-- client/views/admin/apps/AppsRoute.js | 30 +- client/views/admin/apps/AppsTable.js | 67 +- client/views/admin/apps/AppsWhatIsIt.js | 46 +- client/views/admin/apps/CloudLoginModal.js | 38 +- client/views/admin/apps/IframeModal.js | 12 +- client/views/admin/apps/LoadingDetails.tsx | 15 +- client/views/admin/apps/LogEntry.tsx | 26 +- client/views/admin/apps/LogItem.tsx | 22 +- client/views/admin/apps/LogsLoading.tsx | 5 +- client/views/admin/apps/MarketplacePage.js | 38 +- client/views/admin/apps/MarketplaceRow.tsx | 110 ++-- client/views/admin/apps/MarketplaceTable.js | 88 ++- client/views/admin/apps/PriceDisplay.js | 23 +- client/views/admin/apps/SettingsDisplay.tsx | 28 +- client/views/admin/apps/WarningModal.js | 36 +- client/views/admin/apps/helpers.js | 40 +- client/views/admin/apps/hooks/useAppInfo.ts | 14 +- .../views/admin/apps/hooks/useFilteredApps.ts | 4 +- client/views/admin/cloud/CloudPage.js | 96 +-- client/views/admin/cloud/CloudRoute.js | 2 +- .../admin/cloud/ConnectToCloudSection.js | 50 +- client/views/admin/cloud/CopyStep.tsx | 82 +-- .../cloud/ManualWorkspaceRegistrationModal.js | 20 +- client/views/admin/cloud/PasteStep.tsx | 95 +-- .../admin/cloud/TroubleshootingSection.js | 45 +- client/views/admin/cloud/WhatIsItSection.js | 34 +- .../admin/cloud/WorkspaceLoginSection.js | 53 +- .../cloud/WorkspaceRegistrationSection.js | 51 +- .../views/admin/customEmoji/AddCustomEmoji.js | 133 ++-- client/views/admin/customEmoji/CustomEmoji.js | 75 ++- .../admin/customEmoji/CustomEmojiRoute.js | 74 ++- .../admin/customEmoji/EditCustomEmoji.tsx | 160 +++-- .../customEmoji/EditCustomEmojiWithData.tsx | 48 +- .../admin/customSounds/AddCustomSound.js | 137 +++-- .../views/admin/customSounds/AdminSounds.js | 93 ++- .../admin/customSounds/AdminSoundsRoute.js | 91 +-- .../admin/customSounds/EditCustomSound.js | 257 +++++--- client/views/admin/customSounds/lib.js | 6 +- .../customUserStatus/AddCustomUserStatus.js | 70 ++- .../customUserStatus/CustomUserStatus.js | 81 ++- .../customUserStatus/CustomUserStatusRoute.js | 82 ++- .../customUserStatus/EditCustomUserStatus.js | 132 ++-- .../EditCustomUserStatusWithData.tsx | 59 +- .../views/admin/emailInbox/EmailInboxForm.js | 374 ++++++------ .../views/admin/emailInbox/EmailInboxPage.js | 46 +- .../views/admin/emailInbox/EmailInboxRoute.js | 2 +- .../views/admin/emailInbox/EmailInboxTable.js | 122 ++-- client/views/admin/emailInbox/Skeleton.js | 20 +- .../FederationDashboardPage.js | 20 +- .../FederationDashboardRoute.tsx | 2 +- .../federationDashboard/OverviewSection.js | 63 +- .../federationDashboard/ServersSection.js | 22 +- .../views/admin/import/ImportHistoryPage.js | 156 +++-- .../admin/import/ImportOperationSummary.js | 125 ++-- .../import/ImportOperationSummary.stories.js | 12 +- .../views/admin/import/ImportProgressPage.js | 86 ++- client/views/admin/import/ImportRoute.js | 4 +- client/views/admin/import/NewImportPage.js | 272 +++++---- client/views/admin/import/PrepareChannels.tsx | 134 ++-- .../views/admin/import/PrepareImportPage.js | 171 ++++-- client/views/admin/import/PrepareUsers.tsx | 138 +++-- client/views/admin/info/DeploymentCard.js | 125 ++-- client/views/admin/info/DescriptionList.js | 44 +- .../admin/info/DescriptionList.stories.js | 20 +- client/views/admin/info/InformationRoute.js | 55 +- client/views/admin/info/InstancesCard.js | 48 +- client/views/admin/info/InstancesModal.js | 118 +++- client/views/admin/info/LicenseCard.js | 128 ++-- client/views/admin/info/NewInformationPage.js | 128 ++-- .../views/admin/info/OfflineLicenseModal.js | 135 ++-- client/views/admin/info/PushCard.js | 38 +- client/views/admin/info/UsageCard.js | 327 +++++----- client/views/admin/info/UsagePieGraph.js | 87 +-- .../admin/integrations/IncomingWebhookForm.js | 414 +++++++++---- .../admin/integrations/IntegrationsPage.js | 54 +- .../admin/integrations/IntegrationsRoute.js | 19 +- .../admin/integrations/IntegrationsTable.js | 196 ++++-- .../integrations/OutgoiongWebhookForm.js | 578 ++++++++++++------ .../integrations/edit/EditIncomingWebhook.js | 119 ++-- .../integrations/edit/EditIntegrationsPage.js | 44 +- .../integrations/edit/EditOutgoingWebhook.js | 143 +++-- .../edit/OutgoingWebhookHistoryPage.js | 434 +++++++------ .../admin/integrations/exampleIncomingData.js | 20 +- client/views/admin/integrations/new/NewBot.js | 11 +- .../integrations/new/NewIncomingWebhook.js | 51 +- .../integrations/new/NewIntegrationsPage.js | 64 +- .../integrations/new/NewOutgoingWebhook.js | 65 +- .../views/admin/integrations/new/NewZapier.js | 63 +- client/views/admin/invites/InvitesPage.js | 158 ++--- client/views/admin/invites/InvitesRoute.js | 2 +- client/views/admin/mailer/Mailer.js | 196 +++--- client/views/admin/mailer/Mailer.stories.js | 3 +- client/views/admin/mailer/MailerRoute.js | 7 +- client/views/admin/oauthApps/OAuthAddApp.js | 94 +-- client/views/admin/oauthApps/OAuthAppsPage.js | 46 +- .../views/admin/oauthApps/OAuthAppsRoute.js | 4 +- .../views/admin/oauthApps/OAuthAppsTable.js | 64 +- client/views/admin/oauthApps/OAuthEditApp.js | 241 ++++---- .../views/admin/permissions/EditRolePage.js | 62 +- client/views/admin/permissions/NewRolePage.js | 33 +- .../permissions/PermissionsContextBar.js | 33 +- .../admin/permissions/PermissionsRouter.js | 6 +- .../admin/permissions/PermissionsTable.js | 297 +++++---- client/views/admin/permissions/RoleForm.js | 100 +-- client/views/admin/permissions/UsersInRole.js | 89 +-- .../admin/permissions/UsersInRoleTable.js | 138 +++-- client/views/admin/rooms/EditRoom.js | 337 ++++++---- client/views/admin/rooms/RoomsPage.js | 36 +- client/views/admin/rooms/RoomsRoute.js | 2 +- client/views/admin/rooms/RoomsTable.js | 380 ++++++++---- client/views/admin/routes.js | 12 +- client/views/admin/settings/GroupPage.js | 159 ++--- .../views/admin/settings/GroupPage.stories.js | 12 +- client/views/admin/settings/GroupSelector.tsx | 4 +- .../admin/settings/ResetSettingButton.js | 24 +- client/views/admin/settings/Section.js | 96 +-- client/views/admin/settings/Setting.js | 231 ++++--- .../views/admin/settings/Setting.stories.js | 100 +-- client/views/admin/settings/SettingsRoute.js | 10 +- .../admin/settings/groups/AssetsGroupPage.js | 39 +- .../admin/settings/groups/GenericGroupPage.js | 17 +- .../admin/settings/groups/OAuthGroupPage.js | 151 +++-- .../settings/inputs/ActionSettingInput.js | 34 +- .../inputs/ActionSettingInput.stories.js | 33 +- .../settings/inputs/AssetSettingInput.js | 73 ++- .../inputs/AssetSettingInput.stories.js | 34 +- .../settings/inputs/BooleanSettingInput.js | 32 +- .../inputs/BooleanSettingInput.stories.js | 32 +- .../admin/settings/inputs/CodeSettingInput.js | 72 ++- .../inputs/CodeSettingInput.stories.js | 33 +- .../settings/inputs/ColorSettingInput.js | 131 ++-- .../inputs/ColorSettingInput.stories.js | 29 +- .../admin/settings/inputs/FontSettingInput.js | 46 +- .../inputs/FontSettingInput.stories.js | 34 +- .../settings/inputs/GenericSettingInput.js | 46 +- .../inputs/GenericSettingInput.stories.js | 34 +- .../admin/settings/inputs/IntSettingInput.js | 48 +- .../inputs/IntSettingInput.stories.js | 34 +- .../settings/inputs/LanguageSettingInput.js | 48 +- .../inputs/LanguageSettingInput.stories.js | 34 +- .../inputs/MultiSelectSettingInput.js | 10 +- .../inputs/MultiSelectSettingInput.stories.js | 24 +- .../settings/inputs/PasswordSettingInput.js | 46 +- .../inputs/PasswordSettingInput.stories.js | 29 +- .../inputs/RelativeUrlSettingInput.js | 42 +- .../inputs/RelativeUrlSettingInput.stories.js | 34 +- .../settings/inputs/RoomPickSettingInput.js | 93 +-- .../settings/inputs/SelectSettingInput.js | 58 +- .../inputs/SelectSettingInput.stories.js | 24 +- .../settings/inputs/StringSettingInput.js | 99 +-- .../inputs/StringSettingInput.stories.js | 39 +- client/views/admin/sidebar/AdminSidebar.js | 38 +- .../views/admin/sidebar/AdminSidebarPages.tsx | 8 +- .../admin/sidebar/AdminSidebarSettings.tsx | 83 +-- client/views/admin/sidebarItems.js | 35 +- client/views/admin/users/AddUser.js | 137 +++-- client/views/admin/users/EditUser.js | 189 ++++-- client/views/admin/users/InviteUsers.js | 38 +- client/views/admin/users/UserForm.js | 396 ++++++++---- client/views/admin/users/UserInfo.js | 70 ++- client/views/admin/users/UserInfoActions.js | 382 +++++++----- client/views/admin/users/UsersPage.js | 71 +-- client/views/admin/users/UsersRoute.js | 2 +- client/views/admin/users/UsersTable.js | 301 ++++++--- client/views/admin/viewLogs/ViewLogs.js | 125 ++-- .../views/admin/viewLogs/ViewLogs.stories.js | 11 +- client/views/banners/LegacyBanner.tsx | 37 +- client/views/banners/UiKitBanner.tsx | 53 +- client/views/blocks/MessageBlock.js | 9 +- client/views/blocks/ModalBlock.js | 172 +++--- client/views/directory/ChannelsTab.js | 221 +++++-- client/views/directory/DirectoryPage.js | 54 +- client/views/directory/TeamsTab.js | 180 ++++-- client/views/directory/UserTab.js | 200 ++++-- client/views/directory/hooks.js | 28 +- client/views/hooks/useActionSpread.ts | 24 +- client/views/location/MapView.tsx | 8 +- client/views/location/MapViewFallback.tsx | 14 +- client/views/location/MapViewImage.tsx | 10 +- .../login/ResetPassword/ResetPassword.js | 92 +-- .../ResetPassword/ResetPassword.stories.js | 4 +- client/views/login/index.js | 12 +- client/views/notFound/NotFoundPage.js | 79 ++- .../omnichannel/DepartmentAutoComplete.js | 42 +- .../views/omnichannel/OmnichannelRouter.tsx | 10 +- client/views/omnichannel/agents/AgentEdit.js | 218 ++++--- client/views/omnichannel/agents/AgentInfo.js | 86 ++- client/views/omnichannel/agents/AgentsPage.js | 75 ++- .../views/omnichannel/agents/AgentsRoute.js | 285 ++++++--- .../omnichannel/analytics/AgentOverview.js | 49 +- .../omnichannel/analytics/AnalyticsPage.js | 118 ++-- .../omnichannel/analytics/DateRangePicker.js | 134 ++-- .../analytics/InterchangeableChart.js | 30 +- .../views/omnichannel/analytics/Overview.js | 47 +- .../omnichannel/appearance/AppearanceForm.tsx | 352 +++++++---- .../omnichannel/appearance/AppearancePage.tsx | 78 +-- .../businessHours/BusinessHoursForm.js | 59 +- .../BusinessHoursForm.stories.js | 12 +- .../BusinessHoursFormContainer.js | 64 +- .../businessHours/BusinessHoursPage.js | 33 +- .../businessHours/BusinessHoursRouter.js | 18 +- .../businessHours/EditBusinessHoursPage.js | 99 +-- .../businessHours/NewBusinessHoursPage.js | 65 +- .../businessHours/TimeRangeFieldsAssembler.js | 38 +- .../businessHours/TimeRangeInput.js | 32 +- .../businessHours/mapBusinessHoursForm.js | 6 +- .../currentChats/CurrentChatsPage.js | 237 ++++--- .../currentChats/CurrentChatsRoute.js | 267 +++++--- .../customFields/CustomFieldsForm.js | 96 ++- .../customFields/CustomFieldsForm.stories.js | 12 +- .../customFields/CustomFieldsPage.js | 60 +- .../customFields/CustomFieldsRoute.js | 170 ++++-- .../customFields/EditCustomFieldsPage.js | 90 +-- .../customFields/NewCustomFieldsPage.js | 58 +- .../omnichannel/departments/DepartmentEdit.js | 425 +++++++++---- .../departments/DepartmentsAgentsTable.js | 206 ++++--- .../departments/DepartmentsPage.js | 66 +- .../departments/DepartmentsRoute.js | 195 ++++-- .../omnichannel/facebook/FacebookPage.tsx | 210 ++++--- .../omnichannel/installation/Installation.js | 70 ++- .../omnichannel/managers/ManagersPage.js | 75 ++- .../omnichannel/managers/ManagersRoute.js | 171 ++++-- .../RealTimeMonitoringPage.js | 217 ++++--- .../charts/AgentStatusChart.js | 26 +- .../realTimeMonitoring/charts/Chart.js | 14 +- .../charts/ChatDurationChart.js | 34 +- .../realTimeMonitoring/charts/ChatsChart.js | 31 +- .../charts/ChatsPerAgentChart.js | 28 +- .../charts/ChatsPerDepartmentChart.js | 28 +- .../charts/ResponseTimesChart.js | 44 +- .../charts/getMomentChartLabelsAndData.js | 6 +- .../charts/getMomentCurrentLabel.js | 5 +- .../charts/useUpdateChartData.js | 13 +- .../counter/CounterContainer.js | 20 +- .../realTimeMonitoring/counter/CounterItem.js | 30 +- .../realTimeMonitoring/counter/CounterRow.js | 44 +- .../counter/CounterRow.stories.js | 16 +- .../overviews/AgentsOverview.js | 8 +- .../overviews/ChatsOverview.js | 2 +- .../overviews/ConversationOverview.js | 2 +- .../overviews/ProductivityOverview.js | 10 +- client/views/omnichannel/routes.js | 6 +- .../sidebar/OmnichannelSidebar.tsx | 36 +- client/views/omnichannel/sidebarItems.js | 36 +- .../omnichannel/triggers/EditTriggerPage.js | 86 +-- .../omnichannel/triggers/NewTriggerPage.js | 66 +- .../triggers/TriggersForm.stories.js | 16 +- .../omnichannel/triggers/TriggersForm.tsx | 212 ++++--- .../omnichannel/triggers/TriggersPage.js | 62 +- .../omnichannel/triggers/TriggersTable.js | 140 ++--- .../omnichannel/webhooks/WebhooksPage.js | 212 ++++--- .../room/Announcement/Announcement.stories.js | 3 +- .../views/room/Announcement/Announcement.tsx | 66 +- .../room/Announcement/AnnouncementModal.tsx | 12 +- client/views/room/Header/Burger.js | 4 +- client/views/room/Header/Header.js | 139 +++-- .../Omnichannel/QuickActions/QuickActions.tsx | 230 ++++--- client/views/room/Header/ToolBox/ToolBox.tsx | 114 ++-- client/views/room/Header/icons/Encrypted.js | 17 +- client/views/room/Header/icons/Favorite.js | 23 +- client/views/room/Header/icons/Translate.js | 9 +- client/views/room/MemberListRouter.js | 22 +- client/views/room/Room.stories.js | 16 +- client/views/room/UserCard/index.js | 80 +-- client/views/room/adapters.js | 20 +- client/views/room/components/BlazeTemplate.js | 52 +- client/views/room/components/Message.js | 104 +++- client/views/room/components/RoomTemplate.js | 23 +- .../room/components/VerticalBarOldActions.js | 23 +- client/views/room/contexts/RoomContext.ts | 3 +- .../AutoTranslate/AutoTranslate.js | 93 +-- .../AutoTranslate/AutoTranslate.stories.js | 8 +- .../room/contextualBar/Call/BBB/CallBBB.tsx | 97 +-- .../contextualBar/Call/Jitsi/CallJitsi.js | 126 ++-- .../Call/Jitsi/CallJitsi.stories.js | 22 +- .../Call/Jitsi/components/CallModal.js | 43 +- .../contextualBar/Call/Jitsi/lib/Jitsi.js | 90 ++- .../Call/Jitsi/lib/JitsiBridge.js | 32 +- .../Discussions/components/Message.js | 100 ++- .../Discussions/components/Message.stories.js | 3 +- .../room/contextualBar/Discussions/index.js | 236 ++++--- .../Discussions/useDiscussionsList.ts | 17 +- .../contextualBar/ExportMessages/index.js | 174 +++--- .../Info/EditRoomInfo/EditRoomInfo.js | 535 ++++++++++------ .../contextualBar/Info/RoomInfo/RoomInfo.js | 301 +++++---- .../Info/RoomInfo/RoomInfo.stories.js | 87 +-- client/views/room/contextualBar/Info/index.js | 13 +- .../KeyboardShortcuts/KeyboardShortcuts.js | 22 +- .../KeyboardShortcuts.stories.js | 16 +- .../NotificationPreferences.js | 180 ++++-- .../NotificationsPreferences.stories.js | 41 +- .../components/NotificationByDevice.js | 20 +- .../components/NotificationToogle.js | 20 +- .../components/Preferences.js | 6 +- client/views/room/contextualBar/OTR/OTR.js | 65 +- .../room/contextualBar/OTR/OTR.stories.js | 84 +-- .../views/room/contextualBar/OTR/OTRModal.js | 19 +- .../PruneMessages/PruneMessages.js | 221 ++++--- .../PruneMessages/PruneMessages.stories.js | 24 +- .../room/contextualBar/RoomFiles/RoomFiles.js | 123 ++-- .../RoomFiles/RoomFiles.stories.js | 46 +- .../RoomFiles/components/FileItem.js | 100 ++- .../RoomFiles/components/FileItem.stories.js | 10 +- .../RoomFiles/components/FileItemIcon.js | 143 ++++- .../RoomFiles/hooks/useFileList.js | 7 +- .../RoomFiles/hooks/useFilesList.ts | 19 +- .../hooks/useMessageDeletionIsAllowed.js | 25 +- .../RoomMembers/AddUsers/AddUsers.js | 37 +- .../RoomMembers/AddUsers/AddUsers.stories.js | 36 +- .../RoomMembers/EditInvite/EditInvite.js | 56 +- .../EditInvite/EditInvite.stories.js | 26 +- .../RoomMembers/InviteUsers/InviteUsers.js | 51 +- .../InviteUsers/InviteUsers.stories.js | 22 +- .../RoomMembers/List/RoomMembers.js | 200 ++++-- .../RoomMembers/List/components/MemberItem.js | 59 +- .../RoomMembers/RoomMembers.stories.js | 24 +- .../Threads/components/Message.js | 131 +++- .../Threads/components/Message.stories.js | 4 +- .../views/room/contextualBar/Threads/index.js | 221 +++---- .../contextualBar/Threads/useThreadsList.ts | 17 +- .../UserInfo/UserInfo.stories.js | 17 +- .../UserInfo/actions/UserActions.js | 53 +- .../room/contextualBar/UserInfo/index.js | 279 +++++---- .../hooks/useDataWithLoadMore.ts | 39 +- client/views/room/hooks/useUserInfoActions.js | 434 ++++++++----- client/views/room/hooks/useUserRoom.js | 5 +- client/views/room/index.js | 82 ++- .../lib/QuickActions/QuickActionsContext.tsx | 5 +- .../room/lib/QuickActions/defaultActions.ts | 1 - client/views/room/lib/QuickActions/index.tsx | 18 +- .../views/room/lib/Toolbox/ToolboxContext.tsx | 5 +- .../views/room/lib/Toolbox/defaultActions.ts | 26 +- client/views/room/lib/Toolbox/generator.tsx | 11 +- client/views/room/lib/Toolbox/index.tsx | 11 +- client/views/room/providers/RoomProvider.tsx | 18 +- .../views/room/providers/ToolboxProvider.tsx | 171 ++++-- client/views/setupWizard/Pager.js | 20 +- client/views/setupWizard/Pager.stories.js | 17 +- client/views/setupWizard/SetupWizardPage.js | 131 ++-- client/views/setupWizard/SetupWizardState.js | 61 +- client/views/setupWizard/SideBar.js | 165 +++-- client/views/setupWizard/SideBar.stories.js | 10 +- client/views/setupWizard/Step.js | 9 +- client/views/setupWizard/StepHeader.js | 14 +- .../views/setupWizard/StepHeader.stories.js | 6 +- .../steps/AdminUserInformationStep.js | 155 +++-- .../steps/AdminUserInformationStep.stories.js | 9 +- client/views/setupWizard/steps/FinalStep.js | 32 +- .../setupWizard/steps/FinalStep.stories.js | 3 +- .../setupWizard/steps/RegisterServerStep.js | 278 ++++----- .../steps/RegisterServerStep.stories.js | 9 +- .../setupWizard/steps/SettingsBasedStep.js | 171 +++--- .../steps/SettingsBasedStep.stories.js | 9 +- client/views/teams/ChannelDesertionTable.js | 102 ++-- client/views/teams/TeamAutocomplete.js | 55 +- .../teams/contextualBar/TeamChannelItem.js | 152 +++-- .../views/teams/contextualBar/TeamChannels.js | 139 +++-- .../teams/info/Delete/ChannelDeletionTable.js | 92 +-- .../teams/info/Delete/DeleteTeamModal.js | 45 +- .../info/Delete/DeleteTeamModal.stories.js | 29 +- client/views/teams/info/Delete/StepOne.js | 22 +- client/views/teams/info/Delete/StepThree.js | 59 +- client/views/teams/info/Delete/StepTwo.js | 40 +- client/views/teams/info/Delete/index.js | 11 +- .../views/teams/info/Leave/LeaveTeamModal.js | 53 +- .../info/Leave/LeaveTeamModal.stories.js | 25 +- client/views/teams/info/Leave/StepOne.js | 44 +- client/views/teams/info/Leave/StepTwo.js | 62 +- client/views/teams/info/Leave/index.js | 19 +- client/views/teams/info/RoomLinkList.js | 12 +- client/views/teams/info/TeamsInfo.js | 250 ++++---- client/views/teams/info/TeamsInfo.stories.js | 85 +-- client/views/teams/info/index.js | 38 +- .../RemoveUsersModal/RemoveUsersModal.js | 153 +++-- .../RemoveUsersModal.stories.js | 12 +- .../members/RemoveUsersModal/RoomLinkList.js | 12 +- .../teams/members/RemoveUsersModal/index.js | 23 +- client/views/teams/members/TeamsMembers.js | 66 +- .../AddExistingModal/AddExistingModal.tsx | 82 +-- .../modals/AddExistingModal/RoomsInput.tsx | 106 ++-- .../ChannelToTeamModal/ChannelToTeamModal.js | 27 +- .../modals/ChannelToTeamModal/StepOne.js | 43 +- .../modals/ChannelToTeamModal/StepTwo.js | 35 +- .../ConfirmationModal/ConfirmationModal.tsx | 48 +- .../views/teams/modals/ConvertToTeamModal.js | 25 +- .../CreateTeamModal/CreateTeamModal.tsx | 292 +++++---- .../modals/CreateTeamModal/TeamNameInput.tsx | 15 +- .../modals/CreateTeamModal/UsersInput.tsx | 86 ++- 713 files changed, 28619 insertions(+), 18760 deletions(-) rename client/{startup => }/.eslintrc.js (100%) rename client/{startup => }/.prettierrc (100%) diff --git a/.eslintignore b/.eslintignore index e17cbcb3ce4a1..efd43547278d1 100644 --- a/.eslintignore +++ b/.eslintignore @@ -18,4 +18,4 @@ imports/client/ !/.storybook/ ee/server/services/dist/** !/.mocharc.js -!/client/startup/.eslintrc.js +!/client/.eslintrc.js diff --git a/client/startup/.eslintrc.js b/client/.eslintrc.js similarity index 100% rename from client/startup/.eslintrc.js rename to client/.eslintrc.js diff --git a/client/startup/.prettierrc b/client/.prettierrc similarity index 100% rename from client/startup/.prettierrc rename to client/.prettierrc diff --git a/client/UIKit/hooks/useUIKitHandleAction.tsx b/client/UIKit/hooks/useUIKitHandleAction.tsx index be642ff89c135..276d8655d301c 100644 --- a/client/UIKit/hooks/useUIKitHandleAction.tsx +++ b/client/UIKit/hooks/useUIKitHandleAction.tsx @@ -10,15 +10,20 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import * as ActionManager from '../../../app/ui-message/client/ActionManager'; import { UiKitPayload, UIKitActionEvent } from '../../../definition/UIKit'; -const useUIKitHandleAction = (state: S): (event: UIKitActionEvent) => Promise => useMutableCallback(async ({ blockId, value, appId, actionId }) => ActionManager.triggerBlockAction({ - container: { - type: UIKitIncomingInteractionContainerType.VIEW, - id: state.viewId || state.appId, - }, - actionId, - appId, - value, - blockId, -})); +const useUIKitHandleAction = ( + state: S, +): ((event: UIKitActionEvent) => Promise) => + useMutableCallback(async ({ blockId, value, appId, actionId }) => + ActionManager.triggerBlockAction({ + container: { + type: UIKitIncomingInteractionContainerType.VIEW, + id: state.viewId || state.appId, + }, + actionId, + appId, + value, + blockId, + }), + ); export { useUIKitHandleAction }; diff --git a/client/UIKit/hooks/useUIKitHandleClose.tsx b/client/UIKit/hooks/useUIKitHandleClose.tsx index 975f895f15b2d..cd0577c2306cf 100644 --- a/client/UIKit/hooks/useUIKitHandleClose.tsx +++ b/client/UIKit/hooks/useUIKitHandleClose.tsx @@ -7,29 +7,36 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; // import { UIKitIncomingInteractionContainerType } from '@rocket.chat/apps-engine/definition/uikit/UIKitIncomingInteractionContainer'; // import { useEndpoint } from '../../contexts/ServerContext'; -import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext'; import * as ActionManager from '../../../app/ui-message/client/ActionManager'; import { UiKitPayload } from '../../../definition/UIKit'; +import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext'; // eslint-disable-next-line @typescript-eslint/no-unused-vars const emptyFn = (_error: any, _result: UIKitInteractionType | void): void => undefined; -const useUIKitHandleClose = (state: S, fn = emptyFn): () => Promise => { +const useUIKitHandleClose = ( + state: S, + fn = emptyFn, +): (() => Promise) => { const dispatchToastMessage = useToastMessageDispatch(); - return useMutableCallback(() => ActionManager.triggerCancel({ - appId: state.appId, - viewId: state.viewId, - view: { - ...state, - id: state.viewId, - // state: groupStateByBlockId(values), - }, - isCleared: true, - }).then((result) => fn(undefined, result)).catch((error) => { - dispatchToastMessage({ type: 'error', message: error }); - fn(error, undefined); - return Promise.reject(error); - })); + return useMutableCallback(() => + ActionManager.triggerCancel({ + appId: state.appId, + viewId: state.viewId, + view: { + ...state, + id: state.viewId, + // state: groupStateByBlockId(values), + }, + isCleared: true, + }) + .then((result) => fn(undefined, result)) + .catch((error) => { + dispatchToastMessage({ type: 'error', message: error }); + fn(error, undefined); + return Promise.reject(error); + }), + ); }; export { useUIKitHandleClose }; diff --git a/client/UIKit/hooks/useUIKitStateManager.tsx b/client/UIKit/hooks/useUIKitStateManager.tsx index 1017af4b61d52..c773f18d012f2 100644 --- a/client/UIKit/hooks/useUIKitStateManager.tsx +++ b/client/UIKit/hooks/useUIKitStateManager.tsx @@ -1,9 +1,8 @@ -import { useEffect, useState } from 'react'; import { useSafely } from '@rocket.chat/fuselage-hooks'; +import { useEffect, useState } from 'react'; -import { isErrorType, UIKitUserInteractionResult, UiKitPayload } from '../../../definition/UIKit'; import * as ActionManager from '../../../app/ui-message/client/ActionManager'; - +import { isErrorType, UIKitUserInteractionResult, UiKitPayload } from '../../../definition/UIKit'; const useUIKitStateManager = (initialState: S): S => { const [state, setState] = useSafely(useState(initialState)); diff --git a/client/adapters.ts b/client/adapters.ts index 31980a2227fa6..22616d8971eb9 100644 --- a/client/adapters.ts +++ b/client/adapters.ts @@ -3,5 +3,11 @@ import { createTemplateForComponent } from './reactAdapters'; createTemplateForComponent('MessageActions', () => import('./components/Message/Actions')); createTemplateForComponent('reactAttachments', () => import('./components/Message/Attachments')); createTemplateForComponent('ThreadMetric', () => import('./components/Message/Metrics/Thread')); -createTemplateForComponent('DiscussionMetric', () => import('./components/Message/Metrics/Discussion')); -createTemplateForComponent('BroadCastMetric', () => import('./components/Message/Metrics/Broadcast')); +createTemplateForComponent( + 'DiscussionMetric', + () => import('./components/Message/Metrics/Discussion'), +); +createTemplateForComponent( + 'BroadCastMetric', + () => import('./components/Message/Metrics/Broadcast'), +); diff --git a/client/components/AppRoot.js b/client/components/AppRoot.js index eaceafdd8f07b..474cda05dd540 100644 --- a/client/components/AppRoot.js +++ b/client/components/AppRoot.js @@ -9,10 +9,14 @@ import PortalWrapper from './PortalWrapper'; const AppRoot = () => { const portals = useSubscription(portalsSubscription); - return - - {portals.map(({ key, portal }) => )} - ; + return ( + + + {portals.map(({ key, portal }) => ( + + ))} + + ); }; export default AppRoot; diff --git a/client/components/AutoComplete.js b/client/components/AutoComplete.js index a2d92cbf3e700..0284ff4db770e 100644 --- a/client/components/AutoComplete.js +++ b/client/components/AutoComplete.js @@ -1,23 +1,39 @@ -import React, { useMemo, useState } from 'react'; import { AutoComplete, Option, Options } from '@rocket.chat/fuselage'; +import React, { useMemo, useState } from 'react'; -import UserAvatar from './avatar/UserAvatar'; import { useEndpointData } from '../hooks/useEndpointData'; +import UserAvatar from './avatar/UserAvatar'; const query = (term = '') => ({ selector: JSON.stringify({ term }) }); -const Avatar = ({ value, ...props }) => ; +const Avatar = ({ value, ...props }) => ( + +); export const UserAutoComplete = React.memo((props) => { const [filter, setFilter] = useState(''); - const { value: data } = useEndpointData('users.autocomplete', useMemo(() => query(filter), [filter])); - const options = useMemo(() => (data && data.items.map((user) => ({ value: user.username, label: user.name }))) || [], [data]); - return <> {label}} - renderItem={({ value, ...props }) =>