Chore: Meteor 2.2 and bump dependencies (#22399)

Co-authored-by: Diego Sampaio <chinello@gmail.com>
pull/22529/head
Guilherme Gazzo 5 years ago committed by GitHub
parent 6236ec0702
commit a7932bfe9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .eslintignore
  2. 2
      .eslintrc
  3. 6
      .github/workflows/build_and_test.yml
  4. 14
      .meteor/packages
  5. 2
      .meteor/release
  6. 78
      .meteor/versions
  7. 1
      .postcssrc
  8. 1
      .storybook/main.js
  9. 2
      app/2fa/server/functions/resetTOTP.ts
  10. 2
      app/livechat/server/business-hour/AbstractBusinessHour.ts
  11. 6
      app/livechat/server/business-hour/BusinessHourManager.ts
  12. 2
      app/livechat/server/business-hour/Default.ts
  13. 4
      app/models/server/raw/Banners.ts
  14. 19
      app/models/server/raw/BannersDismiss.ts
  15. 110
      app/models/server/raw/BaseRaw.ts
  16. 10
      app/models/server/raw/LivechatBusinessHours.ts
  17. 37
      app/models/server/raw/LivechatDepartmentAgents.js
  18. 74
      app/models/server/raw/LivechatDepartmentAgents.ts
  19. 8
      app/models/server/raw/NpsVote.ts
  20. 4
      app/models/server/raw/Settings.ts
  21. 6
      app/models/server/raw/Subscriptions.ts
  22. 99
      app/models/server/raw/Team.ts
  23. 54
      app/models/server/raw/TeamMember.ts
  24. 4
      app/notification-queue/server/NotificationQueue.ts
  25. 3
      app/ui-cached-collection/client/models/CachedCollection.js
  26. 8
      client/components/AutoCompleteAgent.js
  27. 9
      client/components/AutoCompleteDepartment.js
  28. 8
      client/components/AutoCompleteDepartmentMultiple.js
  29. 7
      client/components/CustomFieldsForm.js
  30. 3
      client/components/Header/Header.stories.js
  31. 7
      client/components/ScrollableContentWrapper.tsx
  32. 6
      client/components/UserCard/UserCard.stories.js
  33. 27
      client/components/connectionStatus/ConnectionStatusBar.stories.js
  34. 27
      client/contexts/AuthorizationContext.ts
  35. 8
      client/contexts/EditableSettingsContext.ts
  36. 20
      client/contexts/RouterContext.ts
  37. 10
      client/contexts/ServerContext/ServerContext.ts
  38. 10
      client/contexts/ServerContext/endpoints.ts
  39. 4
      client/contexts/ServerContext/endpoints/v1/chat/followMessage.ts
  40. 7
      client/contexts/ServerContext/endpoints/v1/chat/getDiscussions.ts
  41. 4
      client/contexts/ServerContext/endpoints/v1/chat/getMessage.ts
  42. 4
      client/contexts/ServerContext/endpoints/v1/chat/unfollowMessage.ts
  43. 4
      client/contexts/ServerContext/endpoints/v1/custom-user-status/list.ts
  44. 4
      client/contexts/ServerContext/endpoints/v1/emoji-custom/list.ts
  45. 7
      client/contexts/ServerContext/endpoints/v1/groups/files.ts
  46. 7
      client/contexts/ServerContext/endpoints/v1/im/files.ts
  47. 4
      client/contexts/ServerContext/endpoints/v1/livechat/appearance.ts
  48. 6
      client/contexts/ServerContext/endpoints/v1/livechat/departmentsByUnit.ts
  49. 6
      client/contexts/ServerContext/endpoints/v1/livechat/monitorsList.ts
  50. 4
      client/contexts/ServerContext/endpoints/v1/livechat/onHold.ts
  51. 6
      client/contexts/ServerContext/endpoints/v1/livechat/tagsList.ts
  52. 7
      client/contexts/ServerContext/endpoints/v1/livechat/usersAgent.ts
  53. 4
      client/contexts/ServerContext/endpoints/v1/livechat/visitorInfo.ts
  54. 5
      client/contexts/ServerContext/endpoints/v1/teams/addRooms.ts
  55. 37
      client/contexts/UserContext.ts
  56. 15
      client/hooks/lists/useStreamUpdatesForMessageList.ts
  57. 7
      client/hooks/useEndpointData.ts
  58. 114
      client/hooks/useForm.ts
  59. 4
      client/hooks/useRoomIcon.tsx
  60. 4
      client/lib/RoomManager.ts
  61. 3
      client/lib/appLayout.ts
  62. 3
      client/lib/imperativeModal.ts
  63. 6
      client/lib/lists/RecordList.ts
  64. 158
      client/lib/minimongo/query.ts
  65. 3
      client/lib/portals/blazePortals.ts
  66. 10
      client/polyfills/customEventPolyfill.ts
  67. 60
      client/providers/createReactiveSubscriptionFactory.ts
  68. 50
      client/sidebar/header/CreateDiscussion.tsx
  69. 7
      client/sidebar/hooks/useSidebarPaletteColor.js
  70. 5
      client/sidebar/search/SearchList.js
  71. 2
      client/startup/listenActiveUsers.ts
  72. 7
      client/views/account/AccountProfileForm.js
  73. 8
      client/views/admin/apps/AppSetting.js
  74. 6
      client/views/admin/apps/MarketplaceTable.js
  75. 10
      client/views/admin/customSounds/EditSound.js
  76. 10
      client/views/admin/customUserStatus/EditCustomUserStatusWithData.tsx
  77. 8
      client/views/admin/import/ImportOperationSummary.js
  78. 7
      client/views/admin/import/PrepareImportPage.js
  79. 6
      client/views/admin/info/NewInformationPage.js
  80. 13
      client/views/admin/integrations/IncomingWebhookForm.js
  81. 8
      client/views/admin/integrations/IntegrationsTable.js
  82. 7
      client/views/admin/integrations/edit/EditIncomingWebhook.js
  83. 7
      client/views/admin/integrations/edit/EditOutgoingWebhook.js
  84. 6
      client/views/admin/oauthApps/EditOauthApp.js
  85. 6
      client/views/admin/oauthApps/OAuthAddApp.js
  86. 7
      client/views/admin/rooms/EditRoomWithData.js
  87. 7
      client/views/admin/rooms/FilterByTypeAndText.js
  88. 7
      client/views/admin/settings/Section.js
  89. 8
      client/views/admin/settings/Setting.js
  90. 6
      client/views/admin/users/EditUserWithData.js
  91. 15
      client/views/admin/users/UserInfo.js
  92. 52
      client/views/admin/users/UserInfoActions.js
  93. 7
      client/views/login/ResetPassword/ResetPassword.js
  94. 11
      client/views/omnichannel/agents/AgentsRoute.js
  95. 5
      client/views/omnichannel/appearance/AppearancePage.tsx
  96. 7
      client/views/omnichannel/businessHours/TimeRangeFieldsAssembler.js
  97. 11
      client/views/omnichannel/customFields/CustomFieldsRoute.js
  98. 11
      client/views/omnichannel/departments/DepartmentsRoute.js
  99. 8
      client/views/omnichannel/departments/EditDepartment.js
  100. 9
      client/views/omnichannel/directory/ChatsContextualBar.js
  101. Some files were not shown because too many files have changed in this diff Show More

@ -14,7 +14,7 @@ public/livechat/
!.scripts
public/pdf.worker.min.js
public/workers/**/*
imports/client/
imports/client/**/*
!/.storybook/
ee/server/services/dist/**
!/.mocharc.js

@ -81,6 +81,7 @@
"prefer-single"
],
"indent": "off",
"no-dupe-class-members": "off",
"no-extra-parens": "off",
"no-spaced-func": "off",
"no-unused-vars": "off",
@ -116,6 +117,7 @@
"enforceForArrowConditionals": false
}
],
"@typescript-eslint/no-dupe-class-members": "error",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": ["error", {
"argsIgnorePattern": "^_",

@ -63,7 +63,7 @@ jobs:
path: |
./node_modules
./ee/server/services/node_modules
key: ${{ runner.OS }}-node_modules-2-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
key: ${{ runner.OS }}-node_modules-4-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
- name: Cache meteor local
uses: actions/cache@v2
@ -242,7 +242,7 @@ jobs:
path: |
./node_modules
./ee/server/services/node_modules
key: ${{ runner.OS }}-node_modules-2-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
key: ${{ runner.OS }}-node_modules-4-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
- name: NPM install
if: steps.cache-nodemodules.outputs.cache-hit != 'true' || steps.cache-cypress.outputs.cache-hit != 'true'
@ -306,7 +306,7 @@ jobs:
path: |
./node_modules
./ee/server/services/node_modules
key: ${{ runner.OS }}-node_modules-2-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
key: ${{ runner.OS }}-node_modules-4-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
- name: Cache meteor local
uses: actions/cache@v2

@ -17,8 +17,8 @@ check@1.3.1
ddp-rate-limiter@1.0.9
ddp-common@1.4.0
dynamic-import@0.6.0
ecmascript@0.15.0
typescript@4.1.2
ecmascript@0.15.1
typescript@4.2.2
ejson@1.1.1
email@2.0.0
fastclick@1.0.13
@ -26,7 +26,7 @@ http@1.4.2
logging@1.2.0
meteor-base@1.4.0
mobile-experience@1.1.0
mongo@1.10.1
mongo@1.11.0
random@1.2.0
rate-limit@1.0.9
reactive-dict@1.3.0
@ -62,16 +62,16 @@ pauli:accounts-linkedin
raix:handlebar-helpers
raix:ui-dropped-event
rocketchat:tap-i18n
rocketchat:tap-i18n@1.10.1
underscore@1.0.10
littledata:synced-cron
edgee:slingshot
jalik:ufs-local@1.0.2
accounts-base@1.8.0
accounts-base@1.9.0
accounts-oauth@1.2.0
autoupdate@1.7.0
babel-compiler@7.6.0
babel-compiler@7.6.1
google-oauth@1.3.0
htmljs
less
@ -82,7 +82,7 @@ oauth2@1.3.0
routepolicy@1.1.0
sha@1.0.9
templating
webapp@1.10.0
webapp@1.10.1
webapp-hashing@1.1.0
rocketchat:oauth2-server
rocketchat:i18n

@ -1 +1 @@
METEOR@2.1.1
METEOR@2.2

@ -1,47 +1,47 @@
accounts-base@1.8.0
accounts-base@1.9.0
accounts-facebook@1.3.2
accounts-github@1.4.3
accounts-google@1.3.3
accounts-meteor-developer@1.4.2
accounts-oauth@1.2.0
accounts-password@1.7.0
accounts-password@1.7.1
accounts-twitter@1.4.2
aldeed:simple-schema@1.5.4
allow-deny@1.1.0
autoupdate@1.7.0
babel-compiler@7.6.0
babel-compiler@7.6.1
babel-runtime@1.5.0
base64@1.0.12
binary-heap@1.0.11
blaze@2.3.4
blaze-html-templates@1.1.2
blaze-tools@1.0.10
blaze@2.5.0
blaze-html-templates@1.2.1
blaze-tools@1.1.2
boilerplate-generator@1.7.1
caching-compiler@1.2.2
caching-html-compiler@1.1.3
caching-html-compiler@1.2.0
callback-hook@1.3.0
cfs:http-methods@0.0.32
check@1.3.1
coffeescript@1.0.17
dandv:caret-position@2.1.1
ddp@1.4.0
ddp-client@2.4.0
ddp-client@2.4.1
ddp-common@1.4.0
ddp-rate-limiter@1.0.9
ddp-server@2.3.2
ddp-server@2.3.3
deps@1.0.12
diff-sequence@1.1.1
dispatch:run-as-user@1.1.1
dynamic-import@0.6.0
ecmascript@0.15.0
ecmascript@0.15.1
ecmascript-runtime@0.7.0
ecmascript-runtime-client@0.11.0
ecmascript-runtime-server@0.10.0
ecmascript-runtime-client@0.11.1
ecmascript-runtime-server@0.10.1
edgee:slingshot@0.7.1
ejson@1.1.1
email@2.0.0
es5-shim@4.8.0
facebook-oauth@1.7.4
facebook-oauth@1.8.0
facts-base@1.0.1
fastclick@1.0.13
fetch@0.1.1
@ -49,10 +49,10 @@ geojson-utils@1.0.10
github-oauth@1.2.3
google-oauth@1.3.0
hot-code-push@1.0.4
html-tools@1.0.11
htmljs@1.0.11
http@1.4.3
id-map@1.1.0
html-tools@1.1.2
htmljs@1.1.1
http@1.4.4
id-map@1.1.1
inter-process-messaging@0.1.1
jalik:ufs@1.0.2
jalik:ufs-gridfs@1.0.2
@ -60,11 +60,11 @@ jalik:ufs-local@1.0.2
jparker:crypto-core@0.1.0
jparker:crypto-md5@0.1.1
jparker:gravatar@0.5.1
jquery@1.11.11
jquery@3.0.0
kadira:flow-router@2.12.1
konecty:multiple-instances-status@1.1.0
konecty:user-presence@2.6.3
launch-screen@1.2.0
launch-screen@1.2.1
less@3.0.1
littledata:synced-cron@1.5.1
livedata@1.0.18
@ -75,37 +75,37 @@ mdg:validation-error@0.5.1
meteor@1.9.3
meteor-base@1.4.0
meteor-developer-oauth@1.2.3
meteorhacks:inject-initial@1.0.4
meteorhacks:inject-initial@1.0.5
meteorspark:util@0.2.0
minifier-css@1.5.3
minifier-css@1.5.4
minifier-js@2.6.0
minimongo@1.6.1
minimongo@1.6.2
mizzao:timesync@0.3.4
mobile-experience@1.1.0
mobile-status-bar@1.1.0
modern-browsers@0.1.5
modules@0.16.0
modules-runtime@0.12.0
mongo@1.10.1
mongo@1.11.1
mongo-decimal@0.1.2
mongo-dev-server@1.1.0
mongo-id@1.0.7
mongo-id@1.0.8
mrt:reactive-store@0.0.1
mystor:device-detection@0.2.0
nimble:restivus@0.8.12
nooitaf:colors@1.1.2_1
npm-bcrypt@0.9.3
npm-mongo@3.8.1
npm-bcrypt@0.9.4
npm-mongo@3.9.0
oauth@1.3.2
oauth1@1.3.0
oauth2@1.3.0
observe-sequence@1.0.16
observe-sequence@1.0.18
ordered-dict@1.1.0
ostrio:cookies@2.7.0
pauli:accounts-linkedin@5.0.0
pauli:linkedin-oauth@5.0.0
promise@0.11.2
raix:eventemitter@0.1.3
raix:eventemitter@1.0.0
raix:handlebar-helpers@0.2.5
raix:ui-dropped-event@0.0.7
random@1.2.0
@ -122,7 +122,7 @@ rocketchat:mongo-config@0.0.1
rocketchat:oauth2-server@2.1.0
rocketchat:postcss@1.0.0
rocketchat:streamer@1.1.0
rocketchat:tap-i18n@1.9.1
rocketchat:tap-i18n@1.10.1
rocketchat:version@1.0.0
routepolicy@1.1.0
service-configuration@1.0.11
@ -130,20 +130,20 @@ session@1.2.0
sha@1.0.9
shell-server@0.5.0
simple:json-routes@2.1.0
socket-stream-client@0.3.1
spacebars@1.0.15
spacebars-compiler@1.1.3
socket-stream-client@0.3.3
spacebars@1.2.0
spacebars-compiler@1.2.1
srp@1.1.0
standard-minifier-js@2.6.0
templating@1.3.2
templating-compiler@1.3.3
templating-runtime@1.3.2
templating-tools@1.1.2
templating@1.4.1
templating-compiler@1.4.1
templating-runtime@1.5.0
templating-tools@1.2.0
tracker@1.2.0
twitter-oauth@1.2.0
typescript@4.1.2
typescript@4.2.2
ui@1.0.13
underscore@1.0.10
url@1.3.1
webapp@1.10.0
url@1.3.2
webapp@1.10.1
webapp-hashing@1.1.0

@ -5,7 +5,6 @@
"preserve": true
},
"postcss-media-minmax": {},
"postcss-selector-not": {},
"postcss-nested": {},
"autoprefixer": {}
},

@ -24,7 +24,6 @@ module.exports = {
plugins: [
require('postcss-custom-properties')({ preserve: true }),
require('postcss-media-minmax')(),
require('postcss-selector-not')(),
require('postcss-nested')(),
require('autoprefixer')(),
require('postcss-url')({ url: ({ absolutePath, relativePath, url }) => {

@ -7,7 +7,7 @@ import { Users } from '../../../models/server/raw/index';
import { IUser } from '../../../../definition/IUser';
const sendResetNotification = async function(uid: string): Promise<void> {
const user: IUser = await Users.findOneById(uid, { projection: { language: 1, emails: 1 } });
const user = await Users.findOneById<Pick<IUser, 'language' | 'emails'>>(uid, { projection: { language: 1, emails: 1 } });
if (!user) {
throw new Meteor.Error('invalid-user');
}

@ -23,7 +23,7 @@ export interface IBusinessHourBehavior {
export interface IBusinessHourType {
name: string;
getBusinessHour(id: string): Promise<ILivechatBusinessHour | undefined>;
getBusinessHour(id?: string): Promise<ILivechatBusinessHour | null>;
saveBusinessHour(businessHourData: ILivechatBusinessHour): Promise<ILivechatBusinessHour>;
removeBusinessHourById(id: string): Promise<void>;
}

@ -59,12 +59,12 @@ export class BusinessHourManager {
this.behavior = behavior;
}
async getBusinessHour(id?: string, type?: string): Promise<ILivechatBusinessHour | undefined> {
async getBusinessHour(id?: string, type?: string): Promise<ILivechatBusinessHour | null> {
const businessHourType = this.getBusinessHourType(type as string || LivechatBusinessHourTypes.DEFAULT);
if (!businessHourType) {
return;
return null;
}
return businessHourType.getBusinessHour(id as string);
return businessHourType.getBusinessHour(id);
}
async saveBusinessHour(businessHourData: ILivechatBusinessHour): Promise<void> {

@ -10,7 +10,7 @@ interface IExtraProperties {
export class DefaultBusinessHour extends AbstractBusinessHourType implements IBusinessHourType {
name = LivechatBusinessHourTypes.DEFAULT;
getBusinessHour(): Promise<ILivechatBusinessHour | undefined> {
getBusinessHour(): Promise<ILivechatBusinessHour | null> {
return this.BusinessHourRepository.findOneDefaultBusinessHour();
}

@ -1,4 +1,4 @@
import { Collection, Cursor, FindOneOptions } from 'mongodb';
import { Collection, Cursor, FindOneOptions, WithoutProjection } from 'mongodb';
import { BannerPlatform, IBanner } from '../../../../definition/IBanner';
import { BaseRaw } from './BaseRaw';
@ -16,7 +16,7 @@ export class BannersRaw extends BaseRaw<T> {
]);
}
findActiveByRoleOrId(roles: string[], platform: BannerPlatform, bannerId?: string, options?: FindOneOptions<T>): Cursor<T> {
findActiveByRoleOrId(roles: string[], platform: BannerPlatform, bannerId?: string, options?: WithoutProjection<FindOneOptions<T>>): Cursor<T> {
const today = new Date();
const query = {

@ -1,13 +1,12 @@
import { Collection, Cursor, FindOneOptions } from 'mongodb';
import { Collection, Cursor, FindOneOptions, WithoutProjection } from 'mongodb';
import { IBannerDismiss } from '../../../../definition/IBanner';
import { BaseRaw } from './BaseRaw';
type T = IBannerDismiss;
export class BannersDismissRaw extends BaseRaw<T> {
export class BannersDismissRaw extends BaseRaw<IBannerDismiss> {
constructor(
public readonly col: Collection<T>,
public readonly trash?: Collection<T>,
public readonly col: Collection<IBannerDismiss>,
public readonly trash?: Collection<IBannerDismiss>,
) {
super(col, trash);
@ -16,12 +15,18 @@ export class BannersDismissRaw extends BaseRaw<T> {
]);
}
findByUserIdAndBannerId(userId: string, bannerIds: string[], options?: FindOneOptions<T>): Cursor<T> {
findByUserIdAndBannerId(userId: string, bannerIds: string[]): Cursor<IBannerDismiss>;
findByUserIdAndBannerId(userId: string, bannerIds: string[], options: WithoutProjection<FindOneOptions<IBannerDismiss>>): Cursor<IBannerDismiss>;
findByUserIdAndBannerId<P>(userId: string, bannerIds: string[], options: FindOneOptions<P extends IBannerDismiss ? IBannerDismiss : P>): Cursor<P>;
findByUserIdAndBannerId<P>(userId: string, bannerIds: string[], options?: undefined | WithoutProjection<FindOneOptions<IBannerDismiss>> | FindOneOptions<P extends IBannerDismiss ? IBannerDismiss : P>): Cursor<P> | Cursor<IBannerDismiss> {
const query = {
userId,
bannerId: { $in: bannerIds },
};
return this.col.find(query, options);
return options ? this.col.find(query, options) : this.col.find(query);
}
}

@ -15,6 +15,7 @@ import {
UpdateQuery,
UpdateWriteOpResult,
WithId,
WithoutProjection,
WriteOpResult,
} from 'mongodb';
@ -40,18 +41,22 @@ type ModelOptionalId<T> = EnhancedOmit<T, '_id'> & { _id?: ExtractIdType<T> };
// InsertionModel forces both _id and _updatedAt to be optional, regardless of how they are declared in T
export type InsertionModel<T> = EnhancedOmit<ModelOptionalId<T>, '_updatedAt'> & { _updatedAt?: Date };
interface ITrash {
__collection__: string;
}
export interface IBaseRaw<T> {
col: Collection<T>;
}
const baseName = 'rocketchat_';
const isWithoutProjection = <T>(props: T): props is WithoutProjection<T> => !('projection' in props) && !('fields' in props);
type DefaultFields<Base> = Record<keyof Base, 1> | Record<keyof Base, 0> | void;
type ResultFields<Base, Defaults> = Defaults extends void ? Base : Defaults[keyof Defaults] extends 1 ? Pick<Defaults, keyof Defaults> : Omit<Defaults, keyof Defaults>;
const warnFields = process.env.NODE_ENV !== 'production'
? (...rest: any): void => { console.warn(...rest, new Error().stack); }
: new Function();
export class BaseRaw<T> implements IBaseRaw<T> {
public defaultFields?: Record<string, 1 | 0>;
export class BaseRaw<T, C extends DefaultFields<T> = undefined> implements IBaseRaw<T> {
public readonly defaultFields: C;
protected name: string;
@ -62,47 +67,65 @@ export class BaseRaw<T> implements IBaseRaw<T> {
this.name = this.col.collectionName.replace(baseName, '');
}
_ensureDefaultFields<T>(options: FindOneOptions<T>): FindOneOptions<T> {
if (!this.defaultFields) {
private ensureDefaultFields(options?: undefined): C extends void ? undefined : WithoutProjection<FindOneOptions<T>>;
private ensureDefaultFields(options: WithoutProjection<FindOneOptions<T>>): WithoutProjection<FindOneOptions<T>>;
private ensureDefaultFields<P>(options: FindOneOptions<P>): FindOneOptions<P>;
private ensureDefaultFields<P>(options?: any): FindOneOptions<P> | undefined | WithoutProjection<FindOneOptions<T>> {
if (this.defaultFields === undefined) {
return options;
}
if (!options) {
return { projection: this.defaultFields };
}
const { fields, ...rest } = options || {};
// TODO: change all places using "fields" for raw models and remove the additional condition here
if ((options.projection != null && Object.keys(options.projection).length > 0)
|| (options.fields != null && Object.keys(options.fields).length > 0)) {
return options;
if (fields) {
warnFields('Using \'fields\' in models is deprecated.', options);
}
return {
...options,
projection: this.defaultFields,
...fields && { projection: fields },
...rest,
};
}
async findOneById(_id: string, options: FindOneOptions<T> = {}): Promise<T | undefined> {
return this.findOne({ _id }, options);
async findOneById(_id: string, options?: WithoutProjection<FindOneOptions<T>> | undefined): Promise<T | null>;
async findOneById<P>(_id: string, options: FindOneOptions<P extends T ? T : P>): Promise<P | null>;
async findOneById<P>(_id: string, options?: any): Promise<T | P | null> {
const query = { _id } as FilterQuery<T>;
const optionsDef = this.ensureDefaultFields(options);
return this.col.findOne(query, optionsDef);
}
async findOne(query = {}, options: FindOneOptions<T> = {}): Promise<T | undefined> {
const optionsDef = this._ensureDefaultFields<T>(options);
async findOne(query?: FilterQuery<T> | string, options?: undefined): Promise<T | null>;
if (typeof query === 'string') {
return this.findOneById(query, options);
}
async findOne(query: FilterQuery<T> | string, options: WithoutProjection<FindOneOptions<T>>): Promise<T | null>;
async findOne<P>(query: FilterQuery<T> | string, options: FindOneOptions<P extends T ? T : P>): Promise<P | null>;
async findOne<P>(query: FilterQuery<T> | string = {}, options?: any): Promise<T | P | null> {
const q = typeof query === 'string' ? { _id: query } as FilterQuery<T> : query;
return await this.col.findOne<T>(query, optionsDef) ?? undefined;
const optionsDef = this.ensureDefaultFields(options);
return this.col.findOne(q, optionsDef);
}
findUsersInRoles(): void {
throw new Error('[overwrite-function] You must overwrite this function in the extended classes');
}
find(query = {}, options: FindOneOptions<T> = {}): Cursor<T> {
const optionsDef = this._ensureDefaultFields(options);
find(query?: FilterQuery<T>): Cursor<ResultFields<T, C>>;
find(query: FilterQuery<T>, options: WithoutProjection<FindOneOptions<T>>): Cursor<ResultFields<T, C>>;
find<P>(query: FilterQuery<T>, options: FindOneOptions<P extends T ? T : P>): Cursor<P>;
find<P>(query: FilterQuery<T> | undefined = {}, options?: any): Cursor<P> | Cursor<T> {
const optionsDef = this.ensureDefaultFields(options);
return this.col.find(query, optionsDef);
}
@ -153,19 +176,42 @@ export class BaseRaw<T> implements IBaseRaw<T> {
}
// Trash
trashFind(query: FilterQuery<T & ITrash>, options: FindOneOptions<T>): Cursor<T> | undefined {
return this.trash?.find<T>({
trashFind<P>(query: FilterQuery<T>, options: FindOneOptions<P extends T ? T : P>): Cursor<P> | undefined {
if (!this.trash) {
return undefined;
}
const { trash } = this;
return trash.find({
__collection__: this.name,
...query,
}, options);
}
async trashFindOneById(_id: string, options: FindOneOptions<T> = {}): Promise<T | undefined> {
const query: object = {
trashFindOneById(_id: string): Promise<T | null>;
trashFindOneById(_id: string, options: WithoutProjection<FindOneOptions<T>>): Promise<T | null>;
trashFindOneById<P>(_id: string, options: FindOneOptions<P extends T ? T : P>): Promise<P | null>;
async trashFindOneById<P extends T>(_id: string, options?: undefined | WithoutProjection<FindOneOptions<T>> | FindOneOptions<P extends T ? T : P>): Promise<T | P | null> {
const query = {
_id,
__collection__: this.name,
};
} as FilterQuery<T>;
return await this.trash?.findOne<T>(query, options) ?? undefined;
if (!this.trash) {
return null;
}
const { trash } = this;
if (options === undefined) {
return trash.findOne(query);
}
if (isWithoutProjection(options)) {
return trash.findOne(query, options);
}
return trash.findOne(query, options);
}
}

@ -1,4 +1,4 @@
import { Collection, ObjectId } from 'mongodb';
import { Collection, FindOneOptions, ObjectId, WithoutProjection } from 'mongodb';
import { BaseRaw } from './BaseRaw';
import {
@ -20,7 +20,13 @@ export interface IWorkHoursCronJobsWrapper {
export class LivechatBusinessHoursRaw extends BaseRaw<ILivechatBusinessHour> {
public readonly col!: Collection<ILivechatBusinessHour>;
findOneDefaultBusinessHour(options?: any): Promise<ILivechatBusinessHour | undefined> {
async findOneDefaultBusinessHour(options?: undefined): Promise<ILivechatBusinessHour | null>;
async findOneDefaultBusinessHour(options: WithoutProjection<FindOneOptions<ILivechatBusinessHour>>): Promise<ILivechatBusinessHour | null>;
async findOneDefaultBusinessHour<P>(options: FindOneOptions<P extends ILivechatBusinessHour ? ILivechatBusinessHour : P>): Promise<P | null>;
findOneDefaultBusinessHour<P>(options?: any): Promise<ILivechatBusinessHour | P | null> {
return this.findOne({ type: LivechatBusinessHourTypes.DEFAULT }, options);
}

@ -1,37 +0,0 @@
import { BaseRaw } from './BaseRaw';
export class LivechatDepartmentAgentsRaw extends BaseRaw {
findUsersInQueue(usersList, options) {
const query = {};
if (Array.isArray(usersList) && usersList.length) {
query.username = {
$in: usersList,
};
}
return this.find(query, options);
}
findByAgentId(agentId) {
return this.find({ agentId });
}
findAgentsByDepartmentId(departmentId, options) {
return this.find({ departmentId }, options);
}
findActiveDepartmentsByAgentId(agentId, options) {
const query = {
agentId,
departmentEnabled: true,
};
return this.find(query, options);
}
findByDepartmentIds(departmentIds, options = {}) {
return this.find({ departmentId: { $in: departmentIds } }, options);
}
// eslint-disable-next-line no-unused-vars
findAgentsByAgentIdAndBusinessHourId(agentId, businessHourId) { return []; }
}

@ -0,0 +1,74 @@
import { Cursor, FilterQuery, WithoutProjection, FindOneOptions } from 'mongodb';
import { BaseRaw } from './BaseRaw';
import { ILivechatDepartmentAgents } from '../../../../definition/ILivechatDepartmentAgents';
export class LivechatDepartmentAgentsRaw extends BaseRaw<ILivechatDepartmentAgents> {
findUsersInQueue(usersList: string[]): Cursor<ILivechatDepartmentAgents>;
findUsersInQueue(usersList: string[], options: WithoutProjection<FindOneOptions<ILivechatDepartmentAgents>>): Cursor<ILivechatDepartmentAgents>;
findUsersInQueue<P>(usersList: string[], options: FindOneOptions<P extends ILivechatDepartmentAgents ? ILivechatDepartmentAgents : P>): Cursor<P>;
findUsersInQueue<P>(usersList: string[], options?: undefined | WithoutProjection<FindOneOptions<ILivechatDepartmentAgents>> | FindOneOptions<P extends ILivechatDepartmentAgents ? ILivechatDepartmentAgents : P>): Cursor<ILivechatDepartmentAgents> | Cursor<P> {
const query: FilterQuery<ILivechatDepartmentAgents> = {};
if (Array.isArray(usersList) && usersList.length) { // TODO: Remove
query.username = {
$in: usersList,
};
}
if (options === undefined) {
return this.find(query);
}
return this.find(query, options);
}
findByAgentId(agentId: string): Cursor<ILivechatDepartmentAgents> {
return this.find({ agentId });
}
findAgentsByDepartmentId(departmentId: string): Cursor<ILivechatDepartmentAgents>;
findAgentsByDepartmentId(departmentId: string, options: WithoutProjection<FindOneOptions<ILivechatDepartmentAgents>>): Cursor<ILivechatDepartmentAgents>;
findAgentsByDepartmentId<P>(departmentId: string, options: FindOneOptions<P extends ILivechatDepartmentAgents ? ILivechatDepartmentAgents : P>): Cursor<P>;
findAgentsByDepartmentId<P>(departmentId: string, options?: undefined | WithoutProjection<FindOneOptions<ILivechatDepartmentAgents>> | FindOneOptions<P extends ILivechatDepartmentAgents ? ILivechatDepartmentAgents : P>): Cursor<ILivechatDepartmentAgents> | Cursor<P> {
const query = { departmentId };
if (options === undefined) {
return this.find(query);
}
return this.find(query, options);
}
findActiveDepartmentsByAgentId(agentId: string): Cursor<ILivechatDepartmentAgents>;
findActiveDepartmentsByAgentId(agentId: string, options: WithoutProjection<FindOneOptions<ILivechatDepartmentAgents>>): Cursor<ILivechatDepartmentAgents>;
findActiveDepartmentsByAgentId<P>(agentId: string, options: FindOneOptions<P extends ILivechatDepartmentAgents ? ILivechatDepartmentAgents : P>): Cursor<P>;
findActiveDepartmentsByAgentId<P>(agentId: string, options?: undefined | WithoutProjection<FindOneOptions<ILivechatDepartmentAgents>> | FindOneOptions<P extends ILivechatDepartmentAgents ? ILivechatDepartmentAgents : P>): Cursor<ILivechatDepartmentAgents> | Cursor<P> {
const query = {
agentId,
departmentEnabled: true,
};
if (options === undefined) {
return this.find(query);
}
return this.find(query, options);
}
findByDepartmentIds(departmentIds: string[], options = {}): Cursor<ILivechatDepartmentAgents> {
return this.find({ departmentId: { $in: departmentIds } }, options);
}
findAgentsByAgentIdAndBusinessHourId(_agentId: string, _businessHourId: string): [] { return []; }
}

@ -1,4 +1,4 @@
import { ObjectId, Collection, Cursor, FindOneOptions, UpdateWriteOpResult } from 'mongodb';
import { ObjectId, Collection, Cursor, FindOneOptions, UpdateWriteOpResult, WithoutProjection } from 'mongodb';
import { INpsVote, INpsVoteStatus } from '../../../../definition/INps';
import { BaseRaw } from './BaseRaw';
@ -17,7 +17,7 @@ export class NpsVoteRaw extends BaseRaw<T> {
]);
}
findNotSentByNpsId(npsId: string, options?: FindOneOptions<T>): Cursor<T> {
findNotSentByNpsId(npsId: string, options?: WithoutProjection<FindOneOptions<T>>): Cursor<T> {
const query = {
npsId,
status: INpsVoteStatus.NEW,
@ -28,7 +28,7 @@ export class NpsVoteRaw extends BaseRaw<T> {
.limit(1000);
}
findByNpsIdAndStatus(npsId: string, status: INpsVoteStatus, options?: FindOneOptions<T>): Cursor<T> {
findByNpsIdAndStatus(npsId: string, status: INpsVoteStatus, options?: WithoutProjection<FindOneOptions<T>>): Cursor<T> {
const query = {
npsId,
status,
@ -36,7 +36,7 @@ export class NpsVoteRaw extends BaseRaw<T> {
return this.col.find(query, options);
}
findByNpsId(npsId: string, options?: FindOneOptions<T>): Cursor<T> {
findByNpsId(npsId: string, options?: WithoutProjection<FindOneOptions<T>>): Cursor<T> {
const query = {
npsId,
};

@ -7,12 +7,12 @@ type T = ISetting;
export class SettingsRaw extends BaseRaw<T> {
async getValueById(_id: string): Promise<ISetting['value'] | undefined> {
const setting = await this.findOne({ _id }, { projection: { value: 1 } });
const setting = await this.findOne<Pick<ISetting, 'value'>>({ _id }, { projection: { value: 1 } });
return setting?.value;
}
findOneNotHiddenById(_id: string): Promise<T | undefined> {
findOneNotHiddenById(_id: string): Promise<T | null> {
const query = {
_id,
hidden: { $ne: true },

@ -5,7 +5,7 @@ import { ISubscription } from '../../../../definition/ISubscription';
type T = ISubscription;
export class SubscriptionsRaw extends BaseRaw<T> {
findOneByRoomIdAndUserId(rid: string, uid: string, options: FindOneOptions<T> = {}): Promise<T | undefined> {
findOneByRoomIdAndUserId(rid: string, uid: string, options: FindOneOptions<T> = {}): Promise<T | null> {
const query = {
rid,
'u._id': uid,
@ -47,9 +47,9 @@ export class SubscriptionsRaw extends BaseRaw<T> {
return cursor.count();
}
async isUserInRole(uid: string, roleName: string, rid: string): Promise<T | undefined> {
async isUserInRole(uid: string, roleName: string, rid: string): Promise<T | null> {
if (rid == null) {
return;
return null;
}
const query = {

@ -1,13 +1,12 @@
import { Collection, FindOneOptions, Cursor, UpdateWriteOpResult, DeleteWriteOpResultObject, FilterQuery } from 'mongodb';
import { Collection, WithoutProjection, FindOneOptions, Cursor, UpdateWriteOpResult, DeleteWriteOpResultObject, FilterQuery } from 'mongodb';
import { BaseRaw } from './BaseRaw';
import { ITeam, TEAM_TYPE } from '../../../../definition/ITeam';
type T = ITeam;
export class TeamRaw extends BaseRaw<T> {
export class TeamRaw extends BaseRaw<ITeam> {
constructor(
public readonly col: Collection<T>,
public readonly trash?: Collection<T>,
public readonly col: Collection<ITeam>,
public readonly trash?: Collection<ITeam>,
) {
super(col, trash);
@ -18,23 +17,81 @@ export class TeamRaw extends BaseRaw<T> {
// ]);
}
findByNames(names: Array<string>, options?: FindOneOptions<T>): Cursor<T> {
findByNames(names: Array<string>): Cursor<ITeam>;
findByNames(names: Array<string>, options: WithoutProjection<FindOneOptions<ITeam>>): Cursor<ITeam>;
findByNames<P>(names: Array<string>, options: FindOneOptions<P extends ITeam ? ITeam : P>): Cursor<P>;
findByNames<P>(names: Array<string>, options?: undefined | WithoutProjection<FindOneOptions<ITeam>> | FindOneOptions<P extends ITeam ? ITeam : P>): Cursor<P> | Cursor<ITeam> {
if (options === undefined) {
return this.col.find({ name: { $in: names } });
}
return this.col.find({ name: { $in: names } }, options);
}
findByIds(ids: Array<string>, options?: FindOneOptions<T>, query?: FilterQuery<T>): Cursor<T> {
findByIds(ids: Array<string>, query?: FilterQuery<ITeam>): Cursor<ITeam>;
findByIds(ids: Array<string>, options: WithoutProjection<FindOneOptions<ITeam>>, query?: FilterQuery<ITeam>): Cursor<ITeam>;
findByIds<P>(ids: Array<string>, options: FindOneOptions<P extends ITeam ? ITeam : P>, query?: FilterQuery<ITeam>): Cursor<P>;
findByIds<P>(ids: Array<string>, options?: undefined | WithoutProjection<FindOneOptions<ITeam>> | FindOneOptions<P extends ITeam ? ITeam : P>, query?: FilterQuery<ITeam>): Cursor<P> | Cursor<ITeam> {
if (options === undefined) {
return this.col.find({ _id: { $in: ids }, ...query });
}
return this.col.find({ _id: { $in: ids }, ...query }, options);
}
findByIdsAndType(ids: Array<string>, type: number, options?: FindOneOptions<T>): Cursor<T> {
findByIdsAndType(ids: Array<string>, type: TEAM_TYPE): Cursor<ITeam>;
findByIdsAndType(ids: Array<string>, type: TEAM_TYPE, options: WithoutProjection<FindOneOptions<ITeam>>): Cursor<ITeam>;
findByIdsAndType<P>(ids: Array<string>, type: TEAM_TYPE, options: FindOneOptions<P extends ITeam ? ITeam : P>): Cursor<P>;
findByIdsAndType<P>(ids: Array<string>, type: TEAM_TYPE, options?: undefined | WithoutProjection<FindOneOptions<ITeam>> | FindOneOptions<P extends ITeam ? ITeam : P>): Cursor<P> | Cursor<ITeam> {
if (options === undefined) {
return this.col.find({ _id: { $in: ids }, type });
}
return this.col.find({ _id: { $in: ids }, type }, options);
}
findByType(type: number, options?: FindOneOptions<T>): Cursor<T> {
findByType(type: number,): Cursor<ITeam>;
findByType(type: number, options: WithoutProjection<FindOneOptions<ITeam>>): Cursor<ITeam>;
findByType<P>(type: number, options: FindOneOptions<P extends ITeam ? ITeam : P>): Cursor<P>;
findByType<P>(type: number, options?: undefined | WithoutProjection<FindOneOptions<ITeam>> | FindOneOptions<P extends ITeam ? ITeam : P>): Cursor<ITeam> | Cursor<P> {
if (options === undefined) {
return this.col.find({ type }, options);
}
return this.col.find({ type }, options);
}
findByNameAndTeamIds(name: string | RegExp, teamIds: Array<string>, options?: FindOneOptions<T>): Cursor<T> {
findByNameAndTeamIds(name: string | RegExp, teamIds: Array<string>): Cursor<ITeam>;
findByNameAndTeamIds(name: string | RegExp, teamIds: Array<string>, options: WithoutProjection<FindOneOptions<ITeam>>): Cursor<ITeam>;
findByNameAndTeamIds<P>(name: string | RegExp, teamIds: Array<string>, options: FindOneOptions<P extends ITeam ? ITeam : P>): Cursor<P>;
findByNameAndTeamIds<P>(name: string | RegExp, teamIds: Array<string>, options?: undefined | WithoutProjection<FindOneOptions<ITeam>> | FindOneOptions<P extends ITeam ? ITeam : P>): Cursor<P> | Cursor<ITeam> {
if (options === undefined) {
return this.col.find({
name,
$or: [{
type: 0,
}, {
_id: {
$in: teamIds,
},
}],
});
}
return this.col.find({
name,
$or: [{
@ -47,12 +104,28 @@ export class TeamRaw extends BaseRaw<T> {
}, options);
}
findOneByName(name: string | RegExp, options?: FindOneOptions<T>): Promise<T | null> {
findOneByName(name: string | RegExp): Promise<ITeam | null>;
findOneByName(name: string | RegExp, options: WithoutProjection<FindOneOptions<ITeam>>): Promise<ITeam | null>;
findOneByName<P>(name: string | RegExp, options: FindOneOptions<P>): Promise<P | null>;
findOneByName<P>(name: string | RegExp, options?: undefined | WithoutProjection<FindOneOptions<ITeam>> | FindOneOptions<P extends ITeam ? ITeam : P>): Promise<P | null> | Promise<ITeam | null> {
if (options === undefined) {
return this.col.findOne({ name });
}
return this.col.findOne({ name }, options);
}
findOneByMainRoomId(roomId: string, options?: FindOneOptions<T>): Promise<T | null> {
return this.col.findOne({ roomId }, options);
findOneByMainRoomId(roomId: string): Promise<ITeam | null>;
findOneByMainRoomId(roomId: string, options: WithoutProjection<FindOneOptions<ITeam>>): Promise<ITeam | null>;
findOneByMainRoomId<P>(roomId: string, options: FindOneOptions<P>): Promise<P | null>;
findOneByMainRoomId<P>(roomId: string, options?: undefined | WithoutProjection<FindOneOptions<ITeam>> | FindOneOptions<P extends ITeam ? ITeam : P>): Promise<P | null> | Promise<ITeam | null> {
return options ? this.col.findOne({ roomId }, options) : this.col.findOne({ roomId });
}
updateMainRoomForTeam(id: string, roomId: string): Promise<UpdateWriteOpResult> {

@ -1,4 +1,4 @@
import { Collection, FindOneOptions, Cursor, InsertOneWriteOpResult, UpdateWriteOpResult, DeleteWriteOpResultObject, FilterQuery } from 'mongodb';
import { Collection, WithoutProjection, FindOneOptions, Cursor, InsertOneWriteOpResult, UpdateWriteOpResult, DeleteWriteOpResultObject, FilterQuery } from 'mongodb';
import { BaseRaw } from './BaseRaw';
import { ITeamMember } from '../../../../definition/ITeam';
@ -20,24 +20,56 @@ export class TeamMemberRaw extends BaseRaw<T> {
this.col.createIndex({ teamId: 1, userId: 1 }, { unique: true });
}
findByUserId(userId: string, options?: FindOneOptions<T>): Cursor<T> {
return this.col.find({ userId }, options);
findByUserId(userId: string): Cursor<ITeamMember>;
findByUserId(userId: string, options: WithoutProjection<FindOneOptions<ITeamMember>>): Cursor<ITeamMember>;
findByUserId<P>(userId: string, options: FindOneOptions<P>): Cursor<P>;
findByUserId<P>(userId: string, options?: undefined | WithoutProjection<FindOneOptions<ITeamMember>> | FindOneOptions<P extends ITeamMember ? ITeamMember : P>): Cursor<P> | Cursor<ITeamMember> {
return options ? this.col.find({ userId }, options) : this.col.find({ userId }, options);
}
findOneByUserIdAndTeamId(userId: string, teamId: string, options?: FindOneOptions<T>): Promise<T | null> {
return this.col.findOne({ userId, teamId }, options);
findOneByUserIdAndTeamId(userId: string, teamId: string): Promise<ITeamMember| null>;
findOneByUserIdAndTeamId(userId: string, teamId: string, options: WithoutProjection<FindOneOptions<ITeamMember>>): Promise<ITeamMember| null>;
findOneByUserIdAndTeamId<P>(userId: string, teamId: string, options: FindOneOptions<P>): Promise<P| null>;
findOneByUserIdAndTeamId<P>(userId: string, teamId: string, options?: undefined | WithoutProjection<FindOneOptions<ITeamMember>> | FindOneOptions<P extends ITeamMember ? ITeamMember : P>): Promise<P| null | ITeamMember> {
return options ? this.col.findOne({ userId, teamId }, options) : this.col.findOne({ userId, teamId }, options);
}
findByTeamId(teamId: string, options?: FindOneOptions<T>): Cursor<T> {
return this.col.find({ teamId }, options);
findByTeamId(teamId: string): Cursor<ITeamMember>;
findByTeamId(teamId: string, options: WithoutProjection<FindOneOptions<ITeamMember>>): Cursor<ITeamMember>;
findByTeamId<P>(teamId: string, options: FindOneOptions<P>): Cursor<P>;
findByTeamId<P>(teamId: string, options?: undefined | WithoutProjection<FindOneOptions<ITeamMember>> | FindOneOptions<P extends ITeamMember ? ITeamMember : P>): Cursor<P> | Cursor<ITeamMember> {
return options ? this.col.find({ teamId }, options) : this.col.find({ teamId }, options);
}
findByTeamIds(teamIds: Array<string>, options?: FindOneOptions<T>): Cursor<T> {
return this.col.find({ teamId: { $in: teamIds } }, options);
findByTeamIds(teamIds: Array<string>): Cursor<ITeamMember>;
findByTeamIds(teamIds: Array<string>, options: WithoutProjection<FindOneOptions<ITeamMember>>): Cursor<ITeamMember>;
findByTeamIds<P>(teamIds: Array<string>, options: FindOneOptions<P>): Cursor<P>;
findByTeamIds<P>(teamIds: Array<string>, options?: undefined | WithoutProjection<FindOneOptions<ITeamMember>> | FindOneOptions<P extends ITeamMember ? ITeamMember : P>): Cursor<P> | Cursor<ITeamMember> {
return options ? this.col.find({ teamId: { $in: teamIds } }, options) : this.col.find({ teamId: { $in: teamIds } }, options);
}
findByTeamIdAndRole(teamId: string, role?: string, options?: FindOneOptions<T>): Cursor<T> {
return this.col.find({ teamId, roles: role }, options);
findByTeamIdAndRole(teamId: string, role: string): Cursor<ITeamMember>;
findByTeamIdAndRole(teamId: string, role: string, options: WithoutProjection<FindOneOptions<ITeamMember>>): Cursor<ITeamMember>;
findByTeamIdAndRole<P>(teamId: string, role: string, options: FindOneOptions<P>): Cursor<P>;
findByTeamIdAndRole<P>(teamId: string, role: string, options?: undefined | WithoutProjection<FindOneOptions<ITeamMember>> | FindOneOptions<P extends ITeamMember ? ITeamMember : P>): Cursor<P> | Cursor<ITeamMember> {
return options ? this.col.find({ teamId, roles: role }, options) : this.col.find({ teamId, roles: role });
}
findByUserIdAndTeamIds(userId: string, teamIds: Array<string>, options: FindOneOptions<T> = {}): Cursor<T> {

@ -112,7 +112,7 @@ class NotificationClass {
}
async scheduleItem({ uid, rid, mid, items, user }: { uid: string; rid: string; mid: string; items: NotificationItem[]; user?: Partial<IUser> }): Promise<void> {
const receiver = user || await Users.findOneById(uid, {
const receiver = user || await Users.findOneById<Pick<IUser, 'statusConnection'>>(uid, {
projection: {
statusConnection: 1,
},
@ -122,7 +122,7 @@ class NotificationClass {
return;
}
const { statusConnection } = receiver;
const { statusConnection = 'offline' } = receiver;
let schedule: Date | undefined;

@ -206,7 +206,8 @@ export class CachedCollection extends Emitter {
}
});
this.collection._collection._docs._map = Object.fromEntries(data.records.map((record) => [record._id, record]));
this.collection._collection._docs._map = new Map(data.records.map((record) => [record._id, record]));
this.updatedAt = data.updatedAt || this.updatedAt;
Object.values(this.collection._collection.queries).forEach((query) => this.collection._collection._recomputeResults(query));

@ -16,9 +16,11 @@ const AutoCompleteAgent = (props) => {
useMemo(() => ({ text: debouncedAgentsFilter, haveAll }), [debouncedAgentsFilter, haveAll]),
);
const { phase: agentsPhase, items: agentsItems, itemCount: agentsTotal } = useRecordList(
AgentsList,
);
const {
phase: agentsPhase,
items: agentsItems,
itemCount: agentsTotal,
} = useRecordList(AgentsList);
const sortedByName = agentsItems.sort((a, b) => {
if (a.value === 'all') {

@ -16,11 +16,10 @@ const AutoCompleteDepartment = (props) => {
const debouncedDepartmentsFilter = useDebouncedValue(departmentsFilter, 500);
const { itemsList: departmentsList, loadMoreItems: loadMoreDepartments } = useDepartmentsList(
useMemo(() => ({ filter: debouncedDepartmentsFilter, onlyMyDepartments, haveAll }), [
debouncedDepartmentsFilter,
onlyMyDepartments,
haveAll,
]),
useMemo(
() => ({ filter: debouncedDepartmentsFilter, onlyMyDepartments, haveAll }),
[debouncedDepartmentsFilter, onlyMyDepartments, haveAll],
),
);
const {

@ -16,10 +16,10 @@ const AutoCompleteDepartmentMultiple = (props) => {
const debouncedDepartmentsFilter = useDebouncedValue(departmentsFilter, 500);
const { itemsList: departmentsList, loadMoreItems: loadMoreDepartments } = useDepartmentsList(
useMemo(() => ({ filter: debouncedDepartmentsFilter, onlyMyDepartments }), [
debouncedDepartmentsFilter,
onlyMyDepartments,
]),
useMemo(
() => ({ filter: debouncedDepartmentsFilter, onlyMyDepartments }),
[debouncedDepartmentsFilter, onlyMyDepartments],
),
);
const {

@ -82,9 +82,10 @@ const CustomSelect = ({
const t = useTranslation();
const [selectError, setSelectError] = useState('');
const mappedOptions = useMemo(() => Object.values(options).map((value) => [value, value]), [
options,
]);
const mappedOptions = useMemo(
() => Object.values(options).map((value) => [value, value]),
[options],
);
const verify = useMemo(
() => (!state.length && required ? t('The_field_is_required', label || name) : ''),
[name, label, required, state.length, t],

@ -15,8 +15,7 @@ export default {
const room = {
t: 'c',
name:
'general general general general general general general general general general general general general general general general general general general',
name: 'general general general general general general general general general general general general general general general general general general general',
_id: 'GENERAL',
encrypted: true,
autoTranslate: true,

@ -28,9 +28,10 @@ export type CustomScrollbarsProps = {
const ScrollableContentWrapper = forwardRef<HTMLElement, CustomScrollbarsProps>(
function WrappedComponent({ children, style, onScroll, overflowX, renderView }, ref) {
const scrollbarsStyle = useMemo(() => ({ ...style, ...styleDefault }), [
style,
]) as CSSProperties;
const scrollbarsStyle = useMemo(
() => ({ ...style, ...styleDefault }),
[style],
) as CSSProperties;
return (
<Scrollbars

@ -15,8 +15,7 @@ const user = {
<UserCard.Role>Rocket.Chat</UserCard.Role>,
<UserCard.Role>Team</UserCard.Role>,
],
bio:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla tempus, eros convallis vulputate cursus, nisi neque eleifend libero, eget lacinia justo purus nec est. In at sodales ipsum. Sed lacinia quis purus eget pulvinar. Aenean eu pretium nunc, at aliquam magna. Praesent dignissim, tortor sed volutpat mattis, mauris diam pulvinar leo, porta commodo risus est non purus. Mauris in justo vel lorem ullamcorper hendrerit. Nam est metus, viverra a pellentesque vitae, ornare eget odio. Morbi tempor feugiat mattis. Morbi non felis tempor, aliquam justo sed, sagittis nibh. Mauris consequat ex metus. Praesent sodales sit amet nibh a vulputate. Integer commodo, mi vel bibendum sollicitudin, urna lectus accumsan ante, eget faucibus augue ex id neque. Aenean consectetur, orci a pellentesque mattis, tortor tellus fringilla elit, non ullamcorper risus nunc feugiat risus. Fusce sit amet nisi dapibus turpis commodo placerat. In tortor ante, vehicula sit amet augue et, imperdiet porta sem.',
bio: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla tempus, eros convallis vulputate cursus, nisi neque eleifend libero, eget lacinia justo purus nec est. In at sodales ipsum. Sed lacinia quis purus eget pulvinar. Aenean eu pretium nunc, at aliquam magna. Praesent dignissim, tortor sed volutpat mattis, mauris diam pulvinar leo, porta commodo risus est non purus. Mauris in justo vel lorem ullamcorper hendrerit. Nam est metus, viverra a pellentesque vitae, ornare eget odio. Morbi tempor feugiat mattis. Morbi non felis tempor, aliquam justo sed, sagittis nibh. Mauris consequat ex metus. Praesent sodales sit amet nibh a vulputate. Integer commodo, mi vel bibendum sollicitudin, urna lectus accumsan ante, eget faucibus augue ex id neque. Aenean consectetur, orci a pellentesque mattis, tortor tellus fringilla elit, non ullamcorper risus nunc feugiat risus. Fusce sit amet nisi dapibus turpis commodo placerat. In tortor ante, vehicula sit amet augue et, imperdiet porta sem.',
actions: [<UserCard.Action icon='message' />, <UserCard.Action icon='phone' />],
localTime: 'Local Time: 7:44 AM',
};
@ -30,8 +29,7 @@ const largeName = {
...user,
customStatus:
'🛴 currently working on User Card on User Card on User Card on User Card on User Card ',
name:
'guilherme.gazzo.guilherme.gazzo.guilherme.gazzo.guilherme.gazzo.guilherme.gazzo.guilherme.gazzo.guilherme.gazzo.guilherme.gazzo.',
name: 'guilherme.gazzo.guilherme.gazzo.guilherme.gazzo.guilherme.gazzo.guilherme.gazzo.guilherme.gazzo.guilherme.gazzo.guilherme.gazzo.',
};
const noRoles = {

@ -4,18 +4,21 @@ import React from 'react';
import { ConnectionStatusContext } from '../../contexts/ConnectionStatusContext';
import ConnectionStatusBar from './ConnectionStatusBar';
const stateDecorator = ({ status = 'connected' } = {}) => (storyFn) => (
<ConnectionStatusContext.Provider
value={{
connected: status === 'connected',
status,
retryTime: status === 'waiting' && Date.now() + 300000,
reconnect: action('reconnect'),
}}
>
{storyFn()}
</ConnectionStatusContext.Provider>
);
const stateDecorator =
({ status = 'connected' } = {}) =>
(storyFn) =>
(
<ConnectionStatusContext.Provider
value={{
connected: status === 'connected',
status,
retryTime: status === 'waiting' && Date.now() + 300000,
reconnect: action('reconnect'),
}}
>
{storyFn()}
</ConnectionStatusContext.Provider>
);
export default {
title: 'components/connectionStatus/ConnectionStatusBar',

@ -54,11 +54,10 @@ export const usePermission = (
scope?: string | Mongo.ObjectID,
): boolean => {
const { queryPermission } = useContext(AuthorizationContext);
const subscription = useMemo(() => queryPermission(permission, scope), [
queryPermission,
permission,
scope,
]);
const subscription = useMemo(
() => queryPermission(permission, scope),
[queryPermission, permission, scope],
);
return useSubscription(subscription);
};
@ -67,11 +66,10 @@ export const useAtLeastOnePermission = (
scope?: string | Mongo.ObjectID,
): boolean => {
const { queryAtLeastOnePermission } = useContext(AuthorizationContext);
const subscription = useMemo(() => queryAtLeastOnePermission(permissions, scope), [
queryAtLeastOnePermission,
permissions,
scope,
]);
const subscription = useMemo(
() => queryAtLeastOnePermission(permissions, scope),
[queryAtLeastOnePermission, permissions, scope],
);
return useSubscription(subscription);
};
@ -80,11 +78,10 @@ export const useAllPermissions = (
scope?: string | Mongo.ObjectID,
): boolean => {
const { queryAllPermissions } = useContext(AuthorizationContext);
const subscription = useMemo(() => queryAllPermissions(permissions, scope), [
queryAllPermissions,
permissions,
scope,
]);
const subscription = useMemo(
() => queryAllPermissions(permissions, scope),
[queryAllPermissions, permissions, scope],
);
return useSubscription(subscription);
};

@ -47,10 +47,10 @@ export const useEditableSetting = (_id: SettingId): IEditableSetting | undefined
export const useEditableSettings = (query?: EditableSettingsContextQuery): IEditableSetting[] => {
const { queryEditableSettings } = useContext(EditableSettingsContext);
const subscription = useMemo(() => queryEditableSettings(query ?? {}), [
queryEditableSettings,
query,
]);
const subscription = useMemo(
() => queryEditableSettings(query ?? {}),
[queryEditableSettings, query],
);
return useSubscription(subscription);
};

@ -101,12 +101,10 @@ export const useRoutePath = (
const { queryRoutePath } = useContext(RouterContext);
return useSubscription(
useMemo(() => queryRoutePath(name, parameters, queryStringParameters), [
queryRoutePath,
name,
parameters,
queryStringParameters,
]),
useMemo(
() => queryRoutePath(name, parameters, queryStringParameters),
[queryRoutePath, name, parameters, queryStringParameters],
),
);
};
@ -118,12 +116,10 @@ export const useRouteUrl = (
const { queryRouteUrl } = useContext(RouterContext);
return useSubscription(
useMemo(() => queryRouteUrl(name, parameters, queryStringParameters), [
queryRouteUrl,
name,
parameters,
queryStringParameters,
]),
useMemo(
() => queryRouteUrl(name, parameters, queryStringParameters),
[queryRouteUrl, name, parameters, queryStringParameters],
),
);
};

@ -69,7 +69,7 @@ export const useMethod = <MethodName extends keyof ServerMethods>(
export const useEndpoint = <
Method extends ServerEndpointMethodOf<Path>,
Path extends ServerEndpointPath
Path extends ServerEndpointPath,
>(
httpMethod: Method,
endpoint: Path,
@ -95,10 +95,10 @@ export const useEndpoint = <
export const useUpload = (endpoint: string): ((params: any, formData: any) => Promise<void>) => {
const { uploadToEndpoint } = useContext(ServerContext);
return useCallback((params, formData: any) => uploadToEndpoint(endpoint, params, formData), [
endpoint,
uploadToEndpoint,
]);
return useCallback(
(params, formData: any) => uploadToEndpoint(endpoint, params, formData),
[endpoint, uploadToEndpoint],
);
};
export const useStream = (

@ -68,29 +68,29 @@ export type ServerEndpointMethodOf<Path extends ServerEndpointPath> = keyof Serv
type ServerEndpoint<
Method extends ServerEndpointMethodOf<Path>,
Path extends ServerEndpointPath
Path extends ServerEndpointPath,
> = ServerEndpoints[Path][Method] extends (...args: any[]) => any
? ServerEndpoints[Path][Method]
: (...args: any[]) => any;
export type ServerEndpointRequestPayload<
Method extends ServerEndpointMethodOf<Path>,
Path extends ServerEndpointPath
Path extends ServerEndpointPath,
> = Parameters<ServerEndpoint<Method, Path>>[0];
export type ServerEndpointFormData<
Method extends ServerEndpointMethodOf<Path>,
Path extends ServerEndpointPath
Path extends ServerEndpointPath,
> = Parameters<ServerEndpoint<Method, Path>>[1];
export type ServerEndpointResponsePayload<
Method extends ServerEndpointMethodOf<Path>,
Path extends ServerEndpointPath
Path extends ServerEndpointPath,
> = ReturnType<ServerEndpoint<Method, Path>>;
export type ServerEndpointFunction<
Method extends ServerEndpointMethodOf<Path>,
Path extends ServerEndpointPath
Path extends ServerEndpointPath,
> = {
(params: ServerEndpointRequestPayload<Method, Path>): Promise<
ServerEndpointResponsePayload<Method, Path>

@ -1,9 +1,7 @@
import { IMessage } from '../../../../../../definition/IMessage';
export type FollowMessageEndpoint = {
POST: (params: {
mid: IMessage['_id'];
}) => {
POST: (params: { mid: IMessage['_id'] }) => {
success: true;
statusCode: 200;
body: {};

@ -3,12 +3,7 @@ import { IRoom } from '../../../../../../definition/IRoom';
import { ObjectFromApi } from '../../../../../../definition/ObjectFromApi';
export type GetDiscussionsEndpoint = {
GET: (params: {
roomId: IRoom['_id'];
text?: string;
offset: number;
count: number;
}) => {
GET: (params: { roomId: IRoom['_id']; text?: string; offset: number; count: number }) => {
messages: ObjectFromApi<IMessage>[];
total: number;
};

@ -1,9 +1,7 @@
import { IMessage } from '../../../../../../definition/IMessage';
export type GetMessageEndpoint = {
GET: (params: {
msgId: IMessage['_id'];
}) => {
GET: (params: { msgId: IMessage['_id'] }) => {
message: IMessage;
};
};

@ -1,9 +1,7 @@
import { IMessage } from '../../../../../../definition/IMessage';
export type UnfollowMessageEndpoint = {
POST: (params: {
mid: IMessage['_id'];
}) => {
POST: (params: { mid: IMessage['_id'] }) => {
success: true;
statusCode: 200;
body: {};

@ -1,7 +1,5 @@
export type ListEndpoint = {
GET: (params: {
query: string;
}) => {
GET: (params: { query: string }) => {
statuses: unknown[];
};
};

@ -6,9 +6,7 @@ type EmojiDescriptor = {
};
export type ListEndpoint = {
GET: (params: {
query: string;
}) => {
GET: (params: { query: string }) => {
emojis?: {
update: EmojiDescriptor[];
};

@ -3,12 +3,7 @@ import { IRoom } from '../../../../../../definition/IRoom';
import { ObjectFromApi } from '../../../../../../definition/ObjectFromApi';
export type FilesEndpoint = {
GET: (params: {
roomId: IRoom['_id'];
count: number;
sort: string;
query: string;
}) => {
GET: (params: { roomId: IRoom['_id']; count: number; sort: string; query: string }) => {
files: ObjectFromApi<IMessage>[];
total: number;
};

@ -3,12 +3,7 @@ import { IRoom } from '../../../../../../definition/IRoom';
import { ObjectFromApi } from '../../../../../../definition/ObjectFromApi';
export type FilesEndpoint = {
GET: (params: {
roomId: IRoom['_id'];
count: number;
sort: string;
query: string;
}) => {
GET: (params: { roomId: IRoom['_id']; count: number; sort: string; query: string }) => {
files: ObjectFromApi<IMessage>[];
total: number;
};

@ -1,9 +1,7 @@
import { ISetting } from '../../../../../../definition/ISetting';
export type AppearanceEndpoint = {
GET: (
params: Record<string, never>,
) => {
GET: (params: Record<string, never>) => {
success: boolean;
appearance: ISetting[];
};

@ -2,11 +2,7 @@ import { ILivechatDepartment } from '../../../../../../definition/ILivechatDepar
import { ObjectFromApi } from '../../../../../../definition/ObjectFromApi';
export type LivechatDepartmentsByUnit = {
GET: (params: {
text: string;
offset: number;
count: number;
}) => {
GET: (params: { text: string; offset: number; count: number }) => {
departments: ObjectFromApi<ILivechatDepartment>[];
total: number;
};

@ -2,11 +2,7 @@ import { ILivechatMonitor } from '../../../../../../definition/ILivechatMonitor'
import { ObjectFromApi } from '../../../../../../definition/ObjectFromApi';
export type LivechatMonitorsList = {
GET: (params: {
text: string;
offset: number;
count: number;
}) => {
GET: (params: { text: string; offset: number; count: number }) => {
monitors: ObjectFromApi<ILivechatMonitor>[];
total: number;
};

@ -1,7 +1,5 @@
export type LivechatRoomOnHoldEndpoint = {
POST: (
roomId: string,
) => {
POST: (roomId: string) => {
success: boolean;
};
};

@ -2,11 +2,7 @@ import { ILivechatTag } from '../../../../../../definition/ILivechatTag';
import { ObjectFromApi } from '../../../../../../definition/ObjectFromApi';
export type LivechatTagsList = {
GET: (params: {
text: string;
offset: number;
count: number;
}) => {
GET: (params: { text: string; offset: number; count: number }) => {
tags: ObjectFromApi<ILivechatTag>[];
total: number;
};

@ -1,10 +1,5 @@
export type LivechatUsersAgentEndpoint = {
GET: (params?: {
text?: string;
offset: number;
count: number;
sort: string;
}) => {
GET: (params?: { text?: string; offset: number; count: number; sort: string }) => {
users: {
_id: string;
emails: {

@ -1,7 +1,5 @@
export type LivechatVisitorInfoEndpoint = {
GET: (params: {
visitorId: string;
}) => {
GET: (params: { visitorId: string }) => {
success: boolean;
visitor: {
visitorEmails: Array<{

@ -1,10 +1,7 @@
import { IRoom } from '../../../../../../definition/IRoom';
export type AddRoomsEndpoint = {
POST: (params: {
rooms: IRoom['_id'][];
teamId: string;
}) => {
POST: (params: { rooms: IRoom['_id'][]; teamId: string }) => {
success: true;
statusCode: 200;
body: IRoom[];

@ -87,21 +87,19 @@ export const useLoginWithPassword = (): ((
export const useUserPreference = <T>(key: string, defaultValue?: T): T | undefined => {
const { queryPreference } = useContext(UserContext);
const subscription = useMemo(() => queryPreference(key, defaultValue), [
queryPreference,
key,
defaultValue,
]);
const subscription = useMemo(
() => queryPreference(key, defaultValue),
[queryPreference, key, defaultValue],
);
return useSubscription(subscription);
};
export const useUserSubscription = (rid: string, fields: Fields): ISubscription | undefined => {
const { querySubscription } = useContext(UserContext);
const subscription = useMemo(() => querySubscription({ rid }, fields), [
querySubscription,
rid,
fields,
]);
const subscription = useMemo(
() => querySubscription({ rid }, fields),
[querySubscription, rid, fields],
);
return useSubscription(subscription);
};
@ -116,11 +114,10 @@ export const useUserSubscriptions = (
options?: FindOptions,
): Array<ISubscription> | [] => {
const { querySubscriptions } = useContext(UserContext);
const subscription = useMemo(() => querySubscriptions(query, options), [
querySubscriptions,
query,
options,
]);
const subscription = useMemo(
() => querySubscriptions(query, options),
[querySubscriptions, query, options],
);
return useSubscription(subscription);
};
@ -130,11 +127,9 @@ export const useUserSubscriptionByName = (
sort?: Sort,
): ISubscription | undefined => {
const { querySubscription } = useContext(UserContext);
const subscription = useMemo(() => querySubscription({ name }, fields, sort), [
querySubscription,
name,
fields,
sort,
]);
const subscription = useMemo(
() => querySubscription({ name }, fields, sort),
[querySubscription, name, fields, sort],
);
return useSubscription(subscription);
};

@ -66,13 +66,14 @@ export const useStreamUpdatesForMessageList = (
},
);
const unsubscribeFromDeleteMessageBulk = subscribeToNotifyRoom<NotifyRoomRidDeleteMessageBulkEvent>(
`${rid}/deleteMessageBulk`,
(params) => {
const matchDeleteCriteria = createDeleteCriteria(params);
messageList.prune(matchDeleteCriteria);
},
);
const unsubscribeFromDeleteMessageBulk =
subscribeToNotifyRoom<NotifyRoomRidDeleteMessageBulkEvent>(
`${rid}/deleteMessageBulk`,
(params) => {
const matchDeleteCriteria = createDeleteCriteria(params);
messageList.prune(matchDeleteCriteria);
},
);
return (): void => {
unsubscribeFromRoomMessages();

@ -18,7 +18,7 @@ type ServerGetEndpointPaths = {
export const useEndpointData = <
_T,
Path extends ServerGetEndpointPaths[keyof ServerGetEndpointPaths]
Path extends ServerGetEndpointPaths[keyof ServerGetEndpointPaths],
>(
endpoint: Path,
params: ServerEndpointRequestPayload<'GET', Path> = defaultParams,
@ -26,9 +26,8 @@ export const useEndpointData = <
| ServerEndpointResponsePayload<'GET', Path>
| (() => ServerEndpointResponsePayload<'GET', Path>),
): AsyncState<ServerEndpointResponsePayload<'GET', Path>> & { reload: () => void } => {
const { resolve, reject, reset, ...state } = useAsyncState<
ServerEndpointResponsePayload<'GET', Path>
>(initialValue);
const { resolve, reject, reset, ...state } =
useAsyncState<ServerEndpointResponsePayload<'GET', Path>>(initialValue);
const dispatchToastMessage = useToastMessageDispatch();
const getData = useEndpoint('GET', endpoint);

@ -47,67 +47,71 @@ const initForm = (initialValues: Record<string, unknown>): FormState => {
};
};
const valueChanged = (fieldName: string, newValue: unknown): FormAction => (
state: FormState,
): FormState => {
let { fields } = state;
const field = fields.find(({ name }) => name === fieldName);
if (!field || field.currentValue === newValue) {
return state;
}
const valueChanged =
(fieldName: string, newValue: unknown): FormAction =>
(state: FormState): FormState => {
let { fields } = state;
const field = fields.find(({ name }) => name === fieldName);
if (!field || field.currentValue === newValue) {
return state;
}
const newField = {
...field,
currentValue: newValue,
changed: JSON.stringify(newValue) !== JSON.stringify(field.initialValue),
};
const newField = {
...field,
currentValue: newValue,
changed: JSON.stringify(newValue) !== JSON.stringify(field.initialValue),
};
fields = state.fields.map((field) => {
if (field.name === fieldName) {
return newField;
}
fields = state.fields.map((field) => {
if (field.name === fieldName) {
return newField;
}
return field;
});
return field;
});
return {
...state,
fields,
values: {
...state.values,
[newField.name]: newField.currentValue,
},
hasUnsavedChanges: newField.changed || fields.some((field) => field.changed),
return {
...state,
fields,
values: {
...state.values,
[newField.name]: newField.currentValue,
},
hasUnsavedChanges: newField.changed || fields.some((field) => field.changed),
};
};
};
const formCommitted = (): FormAction => (state: FormState): FormState => ({
...state,
fields: state.fields.map((field) => ({
...field,
initialValue: field.currentValue,
changed: false,
})),
hasUnsavedChanges: false,
});
const formReset = (): FormAction => (state: FormState): FormState => ({
...state,
fields: state.fields.map((field) => ({
...field,
currentValue: field.initialValue,
changed: false,
})),
values: state.fields.reduce(
(values, field) => ({
...values,
[field.name]: field.initialValue,
}),
{},
),
hasUnsavedChanges: false,
});
const formCommitted =
(): FormAction =>
(state: FormState): FormState => ({
...state,
fields: state.fields.map((field) => ({
...field,
initialValue: field.currentValue,
changed: false,
})),
hasUnsavedChanges: false,
});
const formReset =
(): FormAction =>
(state: FormState): FormState => ({
...state,
fields: state.fields.map((field) => ({
...field,
currentValue: field.initialValue,
changed: false,
})),
values: state.fields.reduce(
(values, field) => ({
...values,
[field.name]: field.initialValue,
}),
{},
),
hasUnsavedChanges: false,
});
const isChangeEvent = (x: any): x is ChangeEvent =>
(typeof x === 'object' || typeof x === 'function') && typeof x?.currentTarget !== 'undefined';

@ -27,10 +27,10 @@ export const useRoomIcon = (room: IRoom): ReactNode | { name: string; color?: st
case 'l':
return {
name: 'headset',
color: colors[((room as unknown) as IOmnichannelRoom)?.v.status || 'offline'],
color: colors[(room as unknown as IOmnichannelRoom)?.v.status || 'offline'],
};
case 'd':
const direct = (room as unknown) as IDirectMessageRoom;
const direct = room as unknown as IDirectMessageRoom;
if (direct.uids && direct.uids.length > 2) {
return { name: 'balloon' };
}

@ -151,8 +151,8 @@ const fields = {};
export const useHandleRoom = <T extends IRoom>(rid: IRoom['_id']): AsyncState<T> => {
const { resolve, update, ...state } = useAsyncState<T>();
const uid = useUserId();
const subscription = (useUserSubscription(rid, fields) as unknown) as T;
const _room = (useUserRoom(rid, fields) as unknown) as T;
const subscription = useUserSubscription(rid, fields) as unknown as T;
const _room = useUserRoom(rid, fields) as unknown as T;
const room = uid ? subscription || _room : _room;

@ -16,7 +16,8 @@ type AppLayoutDescriptor = BlazeLayoutDescriptor | ComponentLayoutDescriptor | n
class AppLayoutSubscription
extends Emitter<{ update: void }>
implements Subscription<AppLayoutDescriptor> {
implements Subscription<AppLayoutDescriptor>
{
private descriptor: AppLayoutDescriptor = null;
getCurrentValue = (): AppLayoutDescriptor => this.descriptor;

@ -53,7 +53,8 @@ type ModalDescriptor = BlazeModalDescriptor | ReactModalDescriptor | null;
class ImperativeModalSubscription
extends Emitter<{ update: void }>
implements Subscription<ModalDescriptor> {
implements Subscription<ModalDescriptor>
{
private descriptor: ModalDescriptor = null;
getCurrentValue = (): ModalDescriptor => this.descriptor;

@ -13,10 +13,8 @@ export class RecordList<T extends IRocketChatRecord> extends Emitter {
#index = new Map<T['_id'], T>();
#phase:
| AsyncStatePhase.LOADING
| AsyncStatePhase.UPDATING
| AsyncStatePhase.RESOLVED = AsyncStatePhase.LOADING;
#phase: AsyncStatePhase.LOADING | AsyncStatePhase.UPDATING | AsyncStatePhase.RESOLVED =
AsyncStatePhase.LOADING;
#items: T[] | undefined = undefined;

@ -38,56 +38,67 @@ const $nin = <T extends string>(operand: T[], _options: undefined): ((value: T)
};
};
const $all = <T>(operand: T[], _options: undefined): ((value: T) => boolean) => (
value: T,
): boolean => {
if (!Array.isArray(value)) {
return false;
}
return operand.every((operandElement) =>
value.some((valueElement) => equals(operandElement, valueElement)),
);
};
const $lt = <T>(operand: T, _options: undefined): ((value: T) => boolean) => (value: T): boolean =>
flatSome(value, (x) => compareBSONValues(x, operand) < 0);
const $lte = <T>(operand: T, _options: undefined): ((value: T) => boolean) => (value: T): boolean =>
flatSome(value, (x) => compareBSONValues(x, operand) <= 0);
const $gt = <T>(operand: T, _options: undefined): ((value: T) => boolean) => (value: T): boolean =>
flatSome(value, (x) => compareBSONValues(x, operand) > 0);
const $gte = <T>(operand: T, _options: undefined): ((value: T) => boolean) => (value: T): boolean =>
flatSome(value, (x) => compareBSONValues(x, operand) >= 0);
const $ne = <T>(operand: T, _options: undefined): ((value: T) => boolean) => (value: T): boolean =>
!some(value, (x) => equals(x, operand));
const $exists = <T>(operand: boolean, _options: undefined): ((value: T) => boolean) => (
value: T,
): boolean => operand === (value !== undefined);
const $mod = <T>(
[divisor, remainder]: [number, number],
_options: undefined,
): ((value: T) => boolean) => (value: T): boolean =>
flatSome(value, (x) => Number(x) % divisor === remainder);
const $all =
<T>(operand: T[], _options: undefined): ((value: T) => boolean) =>
(value: T): boolean => {
if (!Array.isArray(value)) {
return false;
}
const $size = <T>(operand: number, _options: undefined): ((value: T) => boolean) => (
value: T,
): boolean => Array.isArray(value) && operand === value.length;
return operand.every((operandElement) =>
value.some((valueElement) => equals(operandElement, valueElement)),
);
};
const $type = <T>(operand: BSONType, _options: undefined): ((value: T) => boolean) => (
value: T,
): boolean => {
if (value === undefined) {
return false;
}
const $lt =
<T>(operand: T, _options: undefined): ((value: T) => boolean) =>
(value: T): boolean =>
flatSome(value, (x) => compareBSONValues(x, operand) < 0);
const $lte =
<T>(operand: T, _options: undefined): ((value: T) => boolean) =>
(value: T): boolean =>
flatSome(value, (x) => compareBSONValues(x, operand) <= 0);
const $gt =
<T>(operand: T, _options: undefined): ((value: T) => boolean) =>
(value: T): boolean =>
flatSome(value, (x) => compareBSONValues(x, operand) > 0);
const $gte =
<T>(operand: T, _options: undefined): ((value: T) => boolean) =>
(value: T): boolean =>
flatSome(value, (x) => compareBSONValues(x, operand) >= 0);
const $ne =
<T>(operand: T, _options: undefined): ((value: T) => boolean) =>
(value: T): boolean =>
!some(value, (x) => equals(x, operand));
const $exists =
<T>(operand: boolean, _options: undefined): ((value: T) => boolean) =>
(value: T): boolean =>
operand === (value !== undefined);
const $mod =
<T>([divisor, remainder]: [number, number], _options: undefined): ((value: T) => boolean) =>
(value: T): boolean =>
flatSome(value, (x) => Number(x) % divisor === remainder);
const $size =
<T>(operand: number, _options: undefined): ((value: T) => boolean) =>
(value: T): boolean =>
Array.isArray(value) && operand === value.length;
const $type =
<T>(operand: BSONType, _options: undefined): ((value: T) => boolean) =>
(value: T): boolean => {
if (value === undefined) {
return false;
}
return flatSome(value, (x) => getBSONType(x) === operand);
};
return flatSome(value, (x) => getBSONType(x) === operand);
};
const $regex = <T>(operand: string | RegExp, options: string): ((value: T) => boolean) => {
let regex: RegExp;
@ -125,9 +136,10 @@ const $not = <T>(operand: FieldExpression<T>, _options: undefined): ((value: T)
return (value: T): boolean => !matcher(value);
};
const dummyOperator = <T>(_operand: unknown, _options: undefined): ((value: T) => boolean) => (
_value: T,
): boolean => true;
const dummyOperator =
<T>(_operand: unknown, _options: undefined): ((value: T) => boolean) =>
(_value: T): boolean =>
true;
const $options = dummyOperator;
const $near = dummyOperator;
@ -191,27 +203,35 @@ const isLogicalOperator = (operator: string): operator is keyof typeof logicalOp
const hasValueOperators = <T>(valueSelector: FieldExpression<T>): boolean =>
Object.keys(valueSelector).every((key) => key.slice(0, 1) === '$');
const compileUndefinedOrNullSelector = <T>(): ((value: T) => boolean) => (value: T): boolean =>
flatSome(value, (x) => x === undefined || x === null);
const compileUndefinedOrNullSelector =
<T>(): ((value: T) => boolean) =>
(value: T): boolean =>
flatSome(value, (x) => x === undefined || x === null);
const compilePrimitiveSelector = <T>(primitive: T) => (value: T): boolean =>
flatSome(value, (x) => x === primitive);
const compilePrimitiveSelector =
<T>(primitive: T) =>
(value: T): boolean =>
flatSome(value, (x) => x === primitive);
const compileRegexSelector = <T>(regex: RegExp) => (value: T): boolean => {
if (value === undefined) {
return false;
}
const compileRegexSelector =
<T>(regex: RegExp) =>
(value: T): boolean => {
if (value === undefined) {
return false;
}
return flatSome(value, (x) => regex.test(String(x)));
};
return flatSome(value, (x) => regex.test(String(x)));
};
const compileArraySelector = <T>(expected: T) => (value: T): boolean => {
if (!Array.isArray(value)) {
return false;
}
const compileArraySelector =
<T>(expected: T) =>
(value: T): boolean => {
if (!Array.isArray(value)) {
return false;
}
return some(value, (x) => equals(expected, x));
};
return some(value, (x) => equals(expected, x));
};
const compileValueOperatorsSelector = <T>(
expression: FieldExpression<T>,
@ -223,7 +243,7 @@ const compileValueOperatorsSelector = <T>(
}
const operand = expression[operator];
const operation = (valueOperators[operator] as unknown) as (
const operation = valueOperators[operator] as unknown as (
operand: unknown,
options: unknown,
) => (value: T) => boolean;
@ -248,7 +268,7 @@ const compileValueSelector = <T>(
}
if (Array.isArray(valueSelector)) {
return compileArraySelector((valueSelector as unknown) as T);
return compileArraySelector(valueSelector as unknown as T);
}
if (hasValueOperators<T>(valueSelector)) {
@ -256,7 +276,7 @@ const compileValueSelector = <T>(
}
return (value: T): boolean =>
flatSome(value, (x) => equals(valueSelector, (x as unknown) as object));
flatSome(value, (x) => equals(valueSelector, x as unknown as object));
};
export const compileDocumentSelector = <T>(

@ -10,7 +10,8 @@ type BlazePortalEntry = {
class BlazePortalsSubscriptions
extends Emitter<{ update: void }>
implements Subscription<BlazePortalEntry[]> {
implements Subscription<BlazePortalEntry[]>
{
private map = new Map<Blaze.TemplateInstance, BlazePortalEntry>();
getCurrentValue = (): BlazePortalEntry[] => Array.from(this.map.values());

@ -3,18 +3,14 @@
return;
}
const CustomEvent = (function <T>(
const CustomEvent = function <T>(
type: string,
{
bubbles = false,
cancelable = false,
detail = (null as unknown) as T,
}: CustomEventInit<T> = {},
{ bubbles = false, cancelable = false, detail = null as unknown as T }: CustomEventInit<T> = {},
): CustomEvent<T> {
const evt = document.createEvent('CustomEvent') as CustomEvent<T>;
evt.initCustomEvent(type, bubbles, cancelable, detail);
return evt;
} as unknown) as {
} as unknown as {
prototype: CustomEvent;
new <T>(typeArg: string, eventInitDict?: CustomEventInit<T>): CustomEvent<T>;
};

@ -5,39 +5,39 @@ interface ISubscriptionFactory<T> {
(...args: any[]): Subscription<T>;
}
export const createReactiveSubscriptionFactory = <T>(
computeCurrentValueWith: (...args: any[]) => T,
): ISubscriptionFactory<T> => (...args: any[]): Subscription<T> => {
const computeCurrentValue = (): T => computeCurrentValueWith(...args);
const callbacks = new Set<Unsubscribe>();
let currentValue = computeCurrentValue();
let computation: Tracker.Computation | undefined;
const timeout = setTimeout(() => {
computation = Tracker.autorun(() => {
currentValue = computeCurrentValue();
callbacks.forEach((callback) => {
callback();
export const createReactiveSubscriptionFactory =
<T>(computeCurrentValueWith: (...args: any[]) => T): ISubscriptionFactory<T> =>
(...args: any[]): Subscription<T> => {
const computeCurrentValue = (): T => computeCurrentValueWith(...args);
const callbacks = new Set<Unsubscribe>();
let currentValue = computeCurrentValue();
let computation: Tracker.Computation | undefined;
const timeout = setTimeout(() => {
computation = Tracker.autorun(() => {
currentValue = computeCurrentValue();
callbacks.forEach((callback) => {
callback();
});
});
});
}, 0);
}, 0);
return {
getCurrentValue: (): T => currentValue,
subscribe: (callback): Unsubscribe => {
callbacks.add(callback);
return {
getCurrentValue: (): T => currentValue,
subscribe: (callback): Unsubscribe => {
callbacks.add(callback);
return (): void => {
clearTimeout(timeout);
return (): void => {
clearTimeout(timeout);
callbacks.delete(callback);
callbacks.delete(callback);
if (callbacks.size === 0) {
computation && computation.stop();
}
};
},
if (callbacks.size === 0) {
computation && computation.stop();
}
};
},
};
};
};

@ -52,44 +52,32 @@ const CreateDiscussion = ({ onClose, defaultParentRoom }: CreateDiscussionProps)
firstMessage: '',
});
const {
name,
parentRoom,
encrypted,
usernames,
firstMessage,
} = values as CreateDiscussionFormValues;
const { name, parentRoom, encrypted, usernames, firstMessage } =
values as CreateDiscussionFormValues;
const {
handleName,
handleParentRoom,
handleEncrypted,
handleUsernames,
handleFirstMessage,
} = handlers;
const { handleName, handleParentRoom, handleEncrypted, handleUsernames, handleFirstMessage } =
handlers;
const canCreate = (parentRoom || defaultParentRoom) && name;
const createDiscussion = useEndpointActionExperimental('POST', 'rooms.createDiscussion');
const create = useMutableCallback(
async (): Promise<void> => {
try {
const result = await createDiscussion({
prid: defaultParentRoom || parentRoom,
// eslint-disable-next-line @typescript-eslint/camelcase
t_name: name,
users: usernames,
reply: encrypted ? undefined : firstMessage,
});
const create = useMutableCallback(async (): Promise<void> => {
try {
const result = await createDiscussion({
prid: defaultParentRoom || parentRoom,
// eslint-disable-next-line @typescript-eslint/camelcase
t_name: name,
users: usernames,
reply: encrypted ? undefined : firstMessage,
});
goToRoomById(result?.discussion?.rid);
onClose();
} catch (error) {
console.warn(error);
}
},
);
goToRoomById(result?.discussion?.rid);
onClose();
} catch (error) {
console.warn(error);
}
});
const onChangeUsers = useMutableCallback((value, action) => {
if (!action) {

@ -141,7 +141,9 @@ const useTheme = () => {
const toVar = (color) =>
color && color[0] === '#' ? color : oldPallet[color] || `var(--${color})`;
const getStyle = ((selector) => (colors) => `
const getStyle = (
(selector) => (colors) =>
`
${selector} {
--rcx-color-neutral-100: ${toVar(colors.n900)};
--rcx-color-neutral-200: ${toVar(colors.n800)};
@ -193,7 +195,8 @@ const getStyle = ((selector) => (colors) => `
.rcx-sidebar {
background-color: ${toVar(colors.sibebarSurface)};
}
`)(isInternetExplorer11 ? ':root' : modifier);
`
)(isInternetExplorer11 ? ':root' : modifier);
const useSidebarPaletteColorIE11 = () => {
const colors = useTheme();

@ -195,9 +195,8 @@ const SearchList = forwardRef(function SearchList({ onClose }, ref) {
let nextSelectedElement = null;
if (dir === 'up') {
nextSelectedElement = selectedElement.current.parentElement.previousSibling.querySelector(
'a',
);
nextSelectedElement =
selectedElement.current.parentElement.previousSibling.querySelector('a');
} else {
nextSelectedElement = selectedElement.current.parentElement.nextSibling.querySelector('a');
}

@ -26,7 +26,7 @@ export const saveUser = (
{ _id: user._id },
{
$set: {
username: user.username,
...(user.username && { username: user.username }),
// name: user.name,
// utcOffset: user.utcOffset,
status: user.status,

@ -95,9 +95,10 @@ function AccountProfileForm({ values, handlers, user, settings, onSaveStateChang
: t('Passwords_do_not_match'),
[t, password, confirmationPassword],
);
const emailError = useMemo(() => (isEmail(email) ? undefined : 'error-invalid-email-address'), [
email,
]);
const emailError = useMemo(
() => (isEmail(email) ? undefined : 'error-invalid-email-address'),
[email],
);
const checkUsername = useDebouncedCallback(
async (username) => {
if (user.username === username) {

@ -41,10 +41,10 @@ function AppSetting({ appSetting, onChange, value, ...props }) {
const { id, type, i18nLabel, i18nDescription, values, required } = appSetting;
const label = (i18nLabel && tApp(i18nLabel)) + (required ? ' *' : '') || id || tApp(id);
const hint = useMemo(() => i18nDescription && <MarkdownText content={tApp(i18nDescription)} />, [
i18nDescription,
tApp,
]);
const hint = useMemo(
() => i18nDescription && <MarkdownText content={tApp(i18nDescription)} />,
[i18nDescription, tApp],
);
return (
<MemoizedSetting

@ -9,8 +9,10 @@ import { AppsContext } from './AppsContext';
import MarketplaceRow from './MarketplaceRow';
import { useFilteredApps } from './hooks/useFilteredApps';
const filterFunction = (text) => ({ name, marketplace }) =>
marketplace !== false && name.toLowerCase().indexOf(text.toLowerCase()) > -1;
const filterFunction =
(text) =>
({ name, marketplace }) =>
marketplace !== false && name.toLowerCase().indexOf(text.toLowerCase()) > -1;
function MarketplaceTable() {
const t = useTranslation();

@ -34,12 +34,10 @@ function EditSound({ close, onChange, data, ...props }) {
setSound(soundFile);
}, []);
const hasUnsavedChanges = useMemo(() => previousName !== name || previousSound !== sound, [
name,
previousName,
previousSound,
sound,
]);
const hasUnsavedChanges = useMemo(
() => previousName !== name || previousSound !== sound,
[name, previousName, previousSound, sound],
);
const saveAction = useCallback(
async (sound) => {

@ -20,10 +20,12 @@ export const EditCustomUserStatusWithData: FC<EditCustomUserStatusWithDataProps>
const t = useTranslation();
const query = useMemo(() => ({ query: JSON.stringify({ _id }) }), [_id]);
const { value: data, phase: state, error, reload } = useEndpointData(
'custom-user-status.list',
query,
);
const {
value: data,
phase: state,
error,
reload,
} = useEndpointData('custom-user-status.list', query);
if (state === AsyncStatePhase.LOADING) {
return (

@ -59,10 +59,10 @@ function ImportOperationSummary({
[valid, status],
);
const canCheckProgress = useMemo(() => valid && ImportingStartedStates.includes(status), [
valid,
status,
]);
const canCheckProgress = useMemo(
() => valid && ImportingStartedStates.includes(status),
[valid, status],
);
const prepareImportRoute = useRoute('admin-import-prepare');
const importProgressRoute = useRoute('admin-import-progress');

@ -58,9 +58,10 @@ function PrepareImportPage() {
const [isImporting, setImporting] = useSafely(useState(false));
const usersCount = useMemo(() => users.filter(({ do_import }) => do_import).length, [users]);
const channelsCount = useMemo(() => channels.filter(({ do_import }) => do_import).length, [
channels,
]);
const channelsCount = useMemo(
() => channels.filter(({ do_import }) => do_import).length,
[channels],
);
const importHistoryRoute = useRoute('admin-import');
const newImportRoute = useRoute('admin-import-new');

@ -22,10 +22,8 @@ const InformationPage = memo(function InformationPage({
}) {
const t = useTranslation();
const {
ref,
contentBoxSize: { inlineSize = DOUBLE_COLUMN_CARD_WIDTH } = {},
} = useResizeObserver();
const { ref, contentBoxSize: { inlineSize = DOUBLE_COLUMN_CARD_WIDTH } = {} } =
useResizeObserver();
const isSmall = inlineSize < DOUBLE_COLUMN_CARD_WIDTH;

@ -27,17 +27,8 @@ export default function IncomingWebhookForm({
const absoluteUrl = useAbsoluteUrl();
const {
enabled,
channel,
username,
name,
alias,
avatarUrl,
emoji,
scriptEnabled,
script,
} = formValues;
const { enabled, channel, username, name, alias, avatarUrl, emoji, scriptEnabled, script } =
formValues;
const {
handleEnabled,

@ -22,10 +22,10 @@ const useQuery = ({ text, type, itemsPerPage, current }, [column, direction]) =>
const useResizeInlineBreakpoint = (sizes = [], debounceDelay = 0) => {
const { ref, borderBoxSize } = useResizeObserver({ debounceDelay });
const inlineSize = borderBoxSize ? borderBoxSize.inlineSize : 0;
sizes = useMemo(() => sizes.map((current) => (inlineSize ? inlineSize > current : true)), [
inlineSize,
sizes,
]);
sizes = useMemo(
() => sizes.map((current) => (inlineSize ? inlineSize > current : true)),
[inlineSize, sizes],
);
return [ref, ...sizes];
};

@ -33,9 +33,10 @@ function EditIncomingWebhook({ data, onChange, ...props }) {
const { values: formValues, handlers: formHandlers, reset } = useForm(getInitialValue(data));
const setModal = useSetModal();
const deleteQuery = useMemo(() => ({ type: 'webhook-incoming', integrationId: data._id }), [
data._id,
]);
const deleteQuery = useMemo(
() => ({ type: 'webhook-incoming', integrationId: data._id }),
[data._id],
);
const deleteIntegration = useEndpointAction('POST', 'integrations.remove', deleteQuery);
const saveIntegration = useMethod('updateIncomingIntegration');

@ -49,9 +49,10 @@ function EditOutgoingWebhook({ data, onChange, setSaveAction, ...props }) {
const router = useRoute('admin-integrations');
const deleteQuery = useMemo(() => ({ type: 'webhook-outgoing', integrationId: data._id }), [
data._id,
]);
const deleteQuery = useMemo(
() => ({ type: 'webhook-outgoing', integrationId: data._id }),
[data._id],
);
const deleteIntegration = useEndpointAction('POST', 'integrations.remove', deleteQuery);
const handleDeleteIntegration = useCallback(() => {

@ -81,8 +81,10 @@ function EditOauthApp({ onChange, data, ...props }) {
</GenericModal>
));
const handleChange = (field, getValue = (e) => e.currentTarget.value) => (e) =>
setNewData({ ...newData, [field]: getValue(e) });
const handleChange =
(field, getValue = (e) => e.currentTarget.value) =>
(e) =>
setNewData({ ...newData, [field]: getValue(e) });
const { active, name, redirectUri } = newData;

@ -41,8 +41,10 @@ export default function OAuthAddApp(props) {
}
}, [close, dispatchToastMessage, newData, saveApp, t]);
const handleChange = (field, getValue = (e) => e.currentTarget.value) => (e) =>
setNewData({ ...newData, [field]: getValue(e) });
const handleChange =
(field, getValue = (e) => e.currentTarget.value) =>
(e) =>
setNewData({ ...newData, [field]: getValue(e) });
const { active, name, redirectUri } = newData;

@ -6,7 +6,12 @@ import { useEndpointData } from '../../../hooks/useEndpointData';
import EditRoom from './EditRoom';
function EditRoomWithData({ rid }) {
const { value: data = {}, phase: state, error, reload } = useEndpointData(
const {
value: data = {},
phase: state,
error,
reload,
} = useEndpointData(
'rooms.adminRooms.getRoom',
useMemo(() => ({ rid }), [rid]),
);

@ -29,9 +29,10 @@ const FilterByTypeAndText = ({ setFilter, ...props }) => {
const t = useTranslation();
const handleChange = useCallback((event) => setText(event.currentTarget.value), []);
const handleCheckBox = useCallback((type) => setTypes({ ...types, [type]: !types[type] }), [
types,
]);
const handleCheckBox = useCallback(
(type) => setTypes({ ...types, [type]: !types[type] }),
[types],
);
useEffect(() => {
if (Object.values(types).filter(Boolean).length === 0) {

@ -21,9 +21,10 @@ function Section({ children, groupId, hasReset = true, help, sectionName, solo }
),
);
const changed = useMemo(() => editableSettings.some(({ changed }) => changed), [
editableSettings,
]);
const changed = useMemo(
() => editableSettings.some(({ changed }) => changed),
[editableSettings],
);
const canReset = useMemo(
() =>

@ -95,10 +95,10 @@ function Setting({ className, settingId, sectionChanged }) {
t.has(i18nDescription) && <MarkdownText preserveHtml={true} content={t(i18nDescription)} />,
[i18nDescription, t],
);
const callout = useMemo(() => alert && <span dangerouslySetInnerHTML={{ __html: t(alert) }} />, [
alert,
t,
]);
const callout = useMemo(
() => alert && <span dangerouslySetInnerHTML={{ __html: t(alert) }} />,
[alert, t],
);
const hasResetButton =
!disableReset &&
!readonly &&

@ -10,7 +10,11 @@ import EditUser from './EditUser';
function EditUserWithData({ uid, ...props }) {
const t = useTranslation();
const { value: roleData, phase: roleState, error: roleError } = useEndpointData('roles.list', '');
const { value: data, phase: state, error } = useEndpointData(
const {
value: data,
phase: state,
error,
} = useEndpointData(
'users.info',
useMemo(() => ({ userId: uid }), [uid]),
);

@ -19,12 +19,17 @@ export function UserInfoWithData({ uid, username, ...props }) {
const showRealNames = useSetting('UI_Use_Real_Name');
const approveManuallyUsers = useSetting('Accounts_ManuallyApproveNewUsers');
const { value: data, phase: state, error, reload } = useEndpointData(
const {
value: data,
phase: state,
error,
reload,
} = useEndpointData(
'users.info',
useMemo(() => ({ ...(uid && { userId: uid }), ...(username && { username }) }), [
uid,
username,
]),
useMemo(
() => ({ ...(uid && { userId: uid }), ...(username && { username }) }),
[uid, username],
),
);
const onChange = useMutableCallback(() => reload());

@ -36,32 +36,34 @@ export const UserInfoActions = ({ username, _id, isActive, isAdmin, onChange })
onChange();
}, [setModal, onChange]);
const confirmOwnerChanges = (action, modalProps = {}) => async () => {
try {
return await action();
} catch (error) {
if (error.xhr?.responseJSON?.errorType === 'user-last-owner') {
const { shouldChangeOwner, shouldBeRemoved } = error.xhr.responseJSON.details;
setModal(
<ConfirmOwnerChangeWarningModal
shouldChangeOwner={shouldChangeOwner}
shouldBeRemoved={shouldBeRemoved}
{...modalProps}
onConfirm={async () => {
await action(true);
setModal();
}}
onCancel={() => {
setModal();
onChange();
}}
/>,
);
return;
const confirmOwnerChanges =
(action, modalProps = {}) =>
async () => {
try {
return await action();
} catch (error) {
if (error.xhr?.responseJSON?.errorType === 'user-last-owner') {
const { shouldChangeOwner, shouldBeRemoved } = error.xhr.responseJSON.details;
setModal(
<ConfirmOwnerChangeWarningModal
shouldChangeOwner={shouldChangeOwner}
shouldBeRemoved={shouldBeRemoved}
{...modalProps}
onConfirm={async () => {
await action(true);
setModal();
}}
onCancel={() => {
setModal();
onChange();
}}
/>,
);
return;
}
dispatchToastMessage({ type: 'error', message: error });
}
dispatchToastMessage({ type: 'error', message: error });
}
};
};
const deleteUserQuery = useMemo(() => ({ userId: _id }), [_id]);
const deleteUserEndpoint = useEndpoint('POST', 'users.delete');

@ -44,9 +44,10 @@ const ResetPassword = () => {
const [isLoading, setIsLoading] = useSafely(useState(false));
const [error, setError] = useSafely(useState());
const handleOnChange = useCallback((event) => setNewPassword(event.currentTarget.value), [
setNewPassword,
]);
const handleOnChange = useCallback(
(event) => setNewPassword(event.currentTarget.value),
[setNewPassword],
);
const isSubmitDisabled = !newPassword.trim() || isLoading;

@ -59,11 +59,12 @@ function AgentsRoute() {
setSort([id, 'asc']);
});
const onRowClick = useMutableCallback((id) => () =>
agentsRoute.push({
context: 'info',
id,
}),
const onRowClick = useMutableCallback(
(id) => () =>
agentsRoute.push({
context: 'info',
id,
}),
);
const { value: data, reload } = useEndpointData('livechat/users/agent', query);

@ -48,9 +48,8 @@ const AppearancePage: FC<AppearancePageProps> = ({ settings }) => {
const t = useTranslation();
const dispatchToastMessage = useToastMessageDispatch();
const save: (
settings: Pick<ISetting, '_id'>[] & { value: unknown }[],
) => Promise<void> = useMethod('livechat:saveAppearance');
const save: (settings: Pick<ISetting, '_id'>[] & { value: unknown }[]) => Promise<void> =
useMethod('livechat:saveAppearance');
const { values, handlers, commit, reset, hasUnsavedChanges } = useForm(
reduceAppearance(settings),

@ -12,9 +12,10 @@ const TimeRangeFieldsAssembler = ({ onChange, daysOpen, daysTime, className }) =
onChange({ ...daysTime, [day]: { start, finish } });
const stableDaysOpen = useStableArray(daysOpen);
const daysList = useMemo(() => DAYS_OF_WEEK.filter((day) => stableDaysOpen.includes(day)), [
stableDaysOpen,
]);
const daysList = useMemo(
() => DAYS_OF_WEEK.filter((day) => stableDaysOpen.includes(day)),
[stableDaysOpen],
);
return (
<>

@ -51,11 +51,12 @@ const CustomFieldsRoute = () => {
setSort([id, 'asc']);
});
const onRowClick = useMutableCallback((id) => () =>
departmentsRoute.push({
context: 'edit',
id,
}),
const onRowClick = useMutableCallback(
(id) => () =>
departmentsRoute.push({
context: 'edit',
id,
}),
);
const { value: data, reload } = useEndpointData('livechat/custom-fields', query);

@ -60,11 +60,12 @@ function DepartmentsRoute() {
setSort([id, 'asc']);
});
const onRowClick = useMutableCallback((id) => () =>
departmentsRoute.push({
context: 'edit',
id,
}),
const onRowClick = useMutableCallback(
(id) => () =>
departmentsRoute.push({
context: 'edit',
id,
}),
);
const { value: data = {}, reload } = useEndpointData('livechat/department', query);

@ -275,10 +275,10 @@ function EditDepartment({ data, id, title, reload, allowedToForwardData }) {
router.push({ ...params, tab: '' });
});
const hasNewAgent = useMemo(() => data.agents.length === agentList.length, [
data.agents,
agentList,
]);
const hasNewAgent = useMemo(
() => data.agents.length === agentList.length,
[data.agents, agentList],
);
return (
<Page flexDirection='row'>

@ -31,9 +31,12 @@ const ChatsContextualBar = ({ chatReload }) => {
directoryRoute.push({ page: 'chats', id, bar: 'info' });
};
const { value: data, phase: state, error, reload: reloadInfo } = useEndpointData(
`rooms.info?roomId=${id}`,
);
const {
value: data,
phase: state,
error,
reload: reloadInfo,
} = useEndpointData(`rooms.info?roomId=${id}`);
if (bar === 'view') {
return <Chat rid={id} />;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save