From 2905aed7c2cbad3b98890e36157a4c2c5fbcd533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Vieira?= <36481167+stephanevieira75@users.noreply.github.com> Date: Mon, 21 Feb 2022 23:22:37 +0100 Subject: [PATCH] #1934 Implement channel members state in frontend (#1940) * #1934 Move channel members types in features/channel-members/types * #1934 Update channel-members-api-client.ts * #1934 Add channel members state in features/channel-members * #1934 Add use channel members hook in features/channel-members * #1934 Implement channel members and pending emails state * #1934 Add use channel guests hook * #1927 Upgrade antd from 4.16.13 to 4.18.3 * #1928 Upgrade react-i18next from 11.12.0 to 11.15.3 * #1934 Implement Channel Members real time Co-authored-by: Romaric Mourgues --- .../channels/web/controllers/channel.ts | 4 +- twake/frontend/package.json | 4 +- .../plugins/mentions/index.ts | 33 +- .../api/channel-members-api-client.ts | 61 +++- .../hooks/use-channel-guests.ts | 53 +++ .../hooks/use-channel-members.ts | 57 +++ .../service/guest-management-service.ts | 124 +------ .../channel-members/state/channel-guests.ts | 11 + .../channel-members/state/channel-members.ts | 11 + .../types/channel-member-types.ts | 19 + .../channel-members/types/guest-types.ts | 6 +- .../utils/channel-members-roles.ts | 5 + .../channels/api/channels-mine-api-client.ts | 2 +- .../api/channels-reachable-api-client.ts | 53 ++- .../app/features/channels/types/channel.ts | 14 +- .../types/channels-reachable-types.ts | 15 + .../api/pending-emails-api-client.ts | 51 +++ .../hooks/use-pending-emails.ts | 47 +++ .../pending-emails/state/pending-emails.ts | 11 + .../types/pending-email.ts | 13 + .../parts/ChannelActivity/ActivityMessage.tsx | 3 +- .../Modals/ChannelMembersList.tsx | 50 +-- .../channels-bar/Modals/GuestManagement.tsx | 58 +-- .../Modals/WorkspaceChannelList.tsx | 11 +- .../WorkspaceChannelRow.tsx | 10 +- .../Parts/Channel/ChannelMenu.tsx | 8 +- .../Parts/Header/MemberChannelRow.tsx | 90 ++--- .../ChannelHeader/ChannelAvatars.tsx | 20 +- .../Pages/WorkspacePartner.tsx | 1 + twake/frontend/yarn.lock | 341 ++++++++++-------- 30 files changed, 739 insertions(+), 447 deletions(-) create mode 100644 twake/frontend/src/app/features/channel-members/hooks/use-channel-guests.ts create mode 100644 twake/frontend/src/app/features/channel-members/hooks/use-channel-members.ts create mode 100644 twake/frontend/src/app/features/channel-members/state/channel-guests.ts create mode 100644 twake/frontend/src/app/features/channel-members/state/channel-members.ts create mode 100644 twake/frontend/src/app/features/channel-members/types/channel-member-types.ts create mode 100644 twake/frontend/src/app/features/channel-members/utils/channel-members-roles.ts create mode 100644 twake/frontend/src/app/features/channels/types/channels-reachable-types.ts create mode 100644 twake/frontend/src/app/features/pending-emails/api/pending-emails-api-client.ts create mode 100644 twake/frontend/src/app/features/pending-emails/hooks/use-pending-emails.ts create mode 100644 twake/frontend/src/app/features/pending-emails/state/pending-emails.ts rename twake/frontend/src/app/features/{workspace-members => pending-emails}/types/pending-email.ts (53%) diff --git a/twake/backend/node/src/services/channels/web/controllers/channel.ts b/twake/backend/node/src/services/channels/web/controllers/channel.ts index cb5da89a47..1add64dbd6 100644 --- a/twake/backend/node/src/services/channels/web/controllers/channel.ts +++ b/twake/backend/node/src/services/channels/web/controllers/channel.ts @@ -378,13 +378,13 @@ export class ChannelCrudController }>, // eslint-disable-next-line @typescript-eslint/no-unused-vars reply: FastifyReply, - ): Promise>> { + ): Promise> { const pendingEmail = await this.pendingEmails.create( getChannelPendingEmailsInstance(request.body.resource), getChannelPendingEmailsExecutionContext(request), ); logger.debug("reqId: %s - save - PendingEmails input %o", request.id, pendingEmail.entity); - return { resource: pendingEmail }; + return { resource: pendingEmail.entity }; } async deletePendingEmails( diff --git a/twake/frontend/package.json b/twake/frontend/package.json index 0432a462d0..351db658da 100644 --- a/twake/frontend/package.json +++ b/twake/frontend/package.json @@ -103,7 +103,7 @@ "@typescript-eslint/parser": "^4.30.0", "anchorme": "1.1.2", "animated-scroll-to": "^2.0.12", - "antd": "^4.16.5", + "antd": "^4.18.3", "babel-cli": "^6.26.0", "babel-core": "^6.26.3", "babel-eslint": "^10.1.0", @@ -187,7 +187,7 @@ "react-fastclick": "^3.0.2", "react-feather": "^2.0.8", "react-google-login": "^5.1.1", - "react-i18next": "^11.11.1", + "react-i18next": "^11.15.3", "react-moment": "^0.9.7", "react-outside-click-handler": "^1.3.0", "react-perfect-scrollbar": "^1.5.8", diff --git a/twake/frontend/src/app/components/rich-text-editor/plugins/mentions/index.ts b/twake/frontend/src/app/components/rich-text-editor/plugins/mentions/index.ts index 571b9ab3d6..7b3ae1e73b 100644 --- a/twake/frontend/src/app/components/rich-text-editor/plugins/mentions/index.ts +++ b/twake/frontend/src/app/components/rich-text-editor/plugins/mentions/index.ts @@ -1,20 +1,20 @@ import { CharacterMetadata, ContentBlock, ContentState, EditorState, Modifier } from 'draft-js'; import { getSelectedBlock } from 'draftjs-utils'; + import { Mention } from './mention'; import MentionSuggestion from './mention-suggestion'; import { EditorSuggestionPlugin, SelectOrInsertOptions } from '../'; import AccessRightsService from 'app/features/workspace-members/services/workspace-members-access-rights-service'; import WorkspaceService from 'app/deprecated/workspaces/workspaces'; -import Collections from 'app/deprecated/CollectionsReact/Collections'; import DepreciatedCollections from 'app/deprecated/CollectionsV1/Collections/Collections'; -import { getChannelMembers } from 'app/deprecated/channels/ChannelCollectionPath'; import { UserType } from 'app/features/users/types/user'; -import { ChannelMemberResource } from 'app/features/channels/types/channel'; import UserService from 'app/features/users/services/current-user-service'; import RouterService from 'app/features/router/services/router-service'; import UserAPIClient from 'app/features/users/api/user-api-client'; import { WorkspaceUserType } from 'app/features/workspaces/types/workspace'; import Strings from 'app/features/global/utils/strings'; +import ChannelMembersAPIClient from 'app/features/channel-members/api/channel-members-api-client'; +import { ChannelMemberType } from 'app/features/channel-members/types/channel-member-types'; import './style.scss'; @@ -36,7 +36,7 @@ const findMentionEntities = ( }, callback); }; -const resolver = ( +const resolver = async ( text: string, max: number, callback: (mentions: MentionSuggestionType[]) => void, @@ -45,20 +45,17 @@ const resolver = ( const { companyId, workspaceId, channelId } = RouterService.getStateFromRoute(); if (AccessRightsService.getCompanyLevel(WorkspaceService.currentGroupId) === 'guest') { - // user is guest, lookup for channel members only - const channelMembersCollection = Collections.get( - getChannelMembers(companyId, workspaceId, channelId), - ChannelMemberResource, - ); - const users = channelMembersCollection - .find({}) - .map(member => DepreciatedCollections.get('users').find(member.id)) - .filter( - (user: UserType) => - `${user.username} ${user.first_name} ${user.last_name} ${user.email}` - .toLocaleLowerCase() - .indexOf(text.toLocaleLowerCase()) >= 0, - ); + const users = + companyId && workspaceId && channelId + ? await ChannelMembersAPIClient.list({ companyId, workspaceId, channelId }) + : ([] as ChannelMemberType[]) + .map(member => DepreciatedCollections.get('users').find(member.user_id)) + .filter( + (user: UserType) => + `${user.username} ${user.first_name} ${user.last_name} ${user.email}` + .toLocaleLowerCase() + .indexOf(text.toLocaleLowerCase()) >= 0, + ); for (let j = 0; j < Math.min(max, users.length); j++) { result[j] = { ...users[j], ...{ autocomplete_id: j } }; diff --git a/twake/frontend/src/app/features/channel-members/api/channel-members-api-client.ts b/twake/frontend/src/app/features/channel-members/api/channel-members-api-client.ts index aa2805a7a6..e095c75e33 100644 --- a/twake/frontend/src/app/features/channel-members/api/channel-members-api-client.ts +++ b/twake/frontend/src/app/features/channel-members/api/channel-members-api-client.ts @@ -1,21 +1,65 @@ -import { ChannelMemberType } from 'app/features/channels/types/channel'; import Api from '../../global/framework/api-service'; import { TwakeService } from '../../global/framework/registry-decorator-service'; +import { + ChannelMemberRole, + ChannelMemberType, +} from 'app/features/channel-members/types/channel-member-types'; +import { WebsocketRoom } from 'app/features/global/types/websocket-types'; type ChannelMembersSaveRequest = { resource: Partial }; type ChannelMembersSaveResponse = { resource: ChannelMemberType }; @TwakeService('ChannelMembersAPIClientService') -class ChannelMembersAPIClient { +class ChannelMembersAPIClientService { private readonly prefix = '/internal/services/channels/v1/companies'; + private realtime: Map< + { companyId: string; workspaceId: string; channelId: string }, + WebsocketRoom[] + > = new Map(); - async get(companyId: string, workspaceId: string, channelId: string) { - return Api.get<{ resources: ChannelMemberType[] }>( - `${this.prefix}/${companyId}/workspaces/${workspaceId}/channels/${channelId}/members`, - ).then(result => result.resources); + websocket({ + companyId, + workspaceId, + channelId, + }: { + companyId: string; + workspaceId: string; + channelId: string; + }): WebsocketRoom[] { + return this.realtime.get({ companyId, workspaceId, channelId }) || []; } - async save( + /** + * Every member of the channel can list members of the channel. + * This returns a list of members in a channel. + * + * @param companyId string + * @param workspaceId string + * @param channelId string + * @returns ChannelMemberType[] + */ + async list( + context: { companyId: string; workspaceId: string; channelId: string }, + filters?: { role: ChannelMemberRole }, + ) { + return Api.get<{ resources: ChannelMemberType[]; websockets: WebsocketRoom[] }>( + `${this.prefix}/${context.companyId}/workspaces/${context.workspaceId}/channels/${ + context.channelId + }/members${filters?.role ? `?company_role=${filters.role}` : ''}?websockets=1`, + ).then(result => { + result.websockets && this.realtime.set(context, result.websockets); + return result.resources; + }); + } + + /** + * Channel member can update their own preferences + * + * @param channelMember ChannelMemberType + * @param partialsToUpdate Partial ChannelMemberType + * @param context Execution context of the update + */ + async updateChannelMemberPreferences( channelMember: ChannelMemberType, partialsToUpdate: Partial, context: { companyId: string; workspaceId: string; channelId: string; userId: string }, @@ -28,5 +72,6 @@ class ChannelMembersAPIClient { ).then(result => result.resource); } } +const ChannelMembersAPIClient = new ChannelMembersAPIClientService(); -export default new ChannelMembersAPIClient(); +export default ChannelMembersAPIClient; diff --git a/twake/frontend/src/app/features/channel-members/hooks/use-channel-guests.ts b/twake/frontend/src/app/features/channel-members/hooks/use-channel-guests.ts new file mode 100644 index 0000000000..dc9c9d962b --- /dev/null +++ b/twake/frontend/src/app/features/channel-members/hooks/use-channel-guests.ts @@ -0,0 +1,53 @@ +import { useRecoilState } from 'recoil'; + +import { + AtomChannelMembersKey, + ChannelMemberType, +} from 'app/features/channel-members/types/channel-member-types'; +import { ChannelGuestsState } from 'app/features/channel-members/state/channel-guests'; +import { useGlobalEffect } from 'app/features/global/hooks/use-global-effect'; +import { LoadingState } from 'app/features/global/state/atoms/Loading'; +import ChannelMembersAPIClient from '../api/channel-members-api-client'; + +export const useChannelGuests = ( + key: AtomChannelMembersKey, +): { + channelGuests: ChannelMemberType[]; + loading: boolean; + refresh: () => Promise; +} => { + const [channelGuests, _setChannelGuests] = useRecoilState(ChannelGuestsState(key)); + const [loading, setLoading] = useRecoilState(LoadingState(`channel-guests-${key.channelId}`)); + + const refresh = async () => { + const { companyId, workspaceId, channelId } = key; + const channelGuestsUpdated = await ChannelMembersAPIClient.list( + { + companyId, + workspaceId, + channelId, + }, + { role: 'guest' }, + ); + + if (channelGuestsUpdated) _setChannelGuests(channelGuestsUpdated); + }; + + useGlobalEffect( + 'useChannelGuests', + async () => { + if (!channelGuests) setLoading(true); + + await refresh(); + + setLoading(false); + }, + [key, channelGuests], + ); + + return { + channelGuests, + loading, + refresh, + }; +}; diff --git a/twake/frontend/src/app/features/channel-members/hooks/use-channel-members.ts b/twake/frontend/src/app/features/channel-members/hooks/use-channel-members.ts new file mode 100644 index 0000000000..d314d9526e --- /dev/null +++ b/twake/frontend/src/app/features/channel-members/hooks/use-channel-members.ts @@ -0,0 +1,57 @@ +import { useRecoilState } from 'recoil'; + +import { + AtomChannelMembersKey, + ChannelMemberType, +} from 'app/features/channel-members/types/channel-member-types'; +import { ChannelMembersState } from 'app/features/channel-members/state/channel-members'; +import { useGlobalEffect } from 'app/features/global/hooks/use-global-effect'; +import { LoadingState } from 'app/features/global/state/atoms/Loading'; +import ChannelMembersAPIClient from '../api/channel-members-api-client'; +import { useRealtimeRoom } from 'app/features/global/hooks/use-realtime'; + +export const useChannelMembers = ( + key: AtomChannelMembersKey, +): { + channelMembers: ChannelMemberType[]; + loading: boolean; + refresh: () => Promise; +} => { + const [channelMembers, _setChannelMembers] = useRecoilState(ChannelMembersState(key)); + const [loading, setLoading] = useRecoilState(LoadingState(`channel-members-${key.channelId}`)); + + const refresh = async () => { + const { companyId, workspaceId, channelId } = key; + const channelMembersUpdated = await ChannelMembersAPIClient.list({ + companyId, + workspaceId, + channelId, + }); + + if (channelMembersUpdated) _setChannelMembers(channelMembersUpdated); + }; + + useGlobalEffect( + 'useChannelMembers', + async () => { + if (!channelMembers) setLoading(true); + + await refresh(); + + setLoading(false); + }, + [key, channelMembers], + ); + + const room = ChannelMembersAPIClient.websocket(key)[0]; + + useRealtimeRoom(room, 'useChannelMembers', (_action, _resource) => { + refresh(); + }); + + return { + channelMembers, + loading, + refresh, + }; +}; diff --git a/twake/frontend/src/app/features/channel-members/service/guest-management-service.ts b/twake/frontend/src/app/features/channel-members/service/guest-management-service.ts index d346371ac3..f8409bf773 100644 --- a/twake/frontend/src/app/features/channel-members/service/guest-management-service.ts +++ b/twake/frontend/src/app/features/channel-members/service/guest-management-service.ts @@ -1,12 +1,6 @@ -import { - PendingEmail, - PendingEmailResource, -} from 'app/features/workspace-members/types/pending-email'; -import { ChannelMemberResource } from 'app/features/channels/types/channel'; -import Collections, { Collection } from 'app/deprecated/CollectionsReact/Collections'; -import RouterServices from 'app/features/router/services/router-service'; +import { PendingEmail } from 'app/features/pending-emails/types/pending-email'; +import { ChannelMemberType } from 'app/features/channel-members/types/channel-member-types'; import UserServices from 'app/features/users/services/current-user-service'; -import ConsoleService from 'app/features/console/services/console-service'; import DepreciatedCollections from 'app/deprecated/CollectionsV1/Collections/Collections'; import { GenericMember } from '../types/guest-types'; @@ -15,21 +9,15 @@ class GuestManagementService { pendingEmails: GenericMember[] = []; list: GenericMember[] = []; - bind({ search, channel_id }: { search: string; channel_id: string }): void { - const { workspaceId, companyId } = RouterServices.getStateFromRoute(); - const channelMembersCollection = this.getChannelMembersCollection( - companyId, - workspaceId, - channel_id, - ); - const channelMembers = channelMembersCollection.find({}, { query: { company_role: 'guest' } }); - const pendingEmailsCollection = this.getPendingEmailCollection( - companyId, - workspaceId, - channel_id, - ); - const pendingEmails = pendingEmailsCollection.find({}); - + bind({ + search, + pendingEmails, + channelMembers, + }: { + search: string; + pendingEmails: PendingEmail[]; + channelMembers: ChannelMemberType[]; + }): void { this.setList(pendingEmails, channelMembers); this.list = this.filterSearch(search); @@ -46,24 +34,24 @@ class GuestManagementService { return this.list; } - setGuests(members: ChannelMemberResource[]): GenericMember[] { - return (this.guests = members.map((member: ChannelMemberResource) => { + setGuests(members: ChannelMemberType[]): GenericMember[] { + return (this.guests = members.map((member: ChannelMemberType) => { return { type: 'guest', filterString: UserServices.getFullName( - DepreciatedCollections.get('users').find(member.data.user_id || ''), + DepreciatedCollections.get('users').find(member.user_id || ''), ), resource: member, - key: member.data.id || '', + key: member.user_id || '', }; })); } - setPendingEmails(pendingEmails: PendingEmailResource[]): GenericMember[] { - const result: GenericMember[] = pendingEmails.map((pendingEmail: PendingEmailResource) => ({ - key: pendingEmail.key, + setPendingEmails(pendingEmails: PendingEmail[]): GenericMember[] { + const result: GenericMember[] = pendingEmails.map((pendingEmail: PendingEmail) => ({ + key: pendingEmail.email, type: 'pending-email', - filterString: pendingEmail.data.email, + filterString: pendingEmail.email, resource: pendingEmail, })); @@ -72,87 +60,15 @@ class GuestManagementService { )); } - setList( - pendingEmails: PendingEmailResource[], - members: ChannelMemberResource[], - ): GenericMember[] { + setList(pendingEmails: PendingEmail[], members: ChannelMemberType[]): GenericMember[] { this.setGuests(members); this.setPendingEmails(pendingEmails); return (this.list = [...this.pendingEmails, ...this.guests]); } - upsertPendingEmail({ - company_id, - workspace_id, - channel_id, - email, - }: PendingEmail): Promise { - const pendingEmailCollection = this.getPendingEmailCollection( - company_id, - workspace_id, - channel_id, - ); - const resourceToAdd = new PendingEmailResource({ company_id, workspace_id, channel_id, email }); - - ConsoleService.addMailsInWorkspace({ - company_id, - workspace_id, - emails: [email], - company_role: 'guest', - }); - - return pendingEmailCollection.upsert(resourceToAdd); - } - - deletePendingEmail(data: PendingEmail): Promise { - return this.getPendingEmailCollection( - data.company_id, - data.workspace_id, - data.channel_id, - ).remove(data); - } - destroyList(): void { this.list = []; } - - private getPendingEmailCollection( - companyId: string = '', - workspaceId: string = '', - channelId: string = '', - ): Collection { - return Collections.get( - this.getPendingEmailsRoute(companyId, workspaceId, channelId), - PendingEmailResource, - ); - } - - private getChannelMembersCollection( - companyId: string = '', - workspaceId: string = '', - channelId: string = '', - ): Collection { - return Collections.get( - this.getGuestMembersRoute(companyId, workspaceId, channelId), - ChannelMemberResource, - ); - } - - private getPendingEmailsRoute( - companyId: string = '', - workspaceId: string = '', - channelId: string = '', - ): string { - return `/channels/v1/companies/${companyId}/workspaces/${workspaceId}/channels/${channelId}/pending_emails/`; - } - - private getGuestMembersRoute( - companyId: string = '', - workspaceId: string = '', - channelId: string = '', - ): string { - return `/channels/v1/companies/${companyId}/workspaces/${workspaceId}/channels/${channelId}/members/::guests`; - } } export default new GuestManagementService(); diff --git a/twake/frontend/src/app/features/channel-members/state/channel-guests.ts b/twake/frontend/src/app/features/channel-members/state/channel-guests.ts new file mode 100644 index 0000000000..0a3c1f0a2e --- /dev/null +++ b/twake/frontend/src/app/features/channel-members/state/channel-guests.ts @@ -0,0 +1,11 @@ +import { atomFamily } from 'recoil'; + +import { + AtomChannelMembersKey, + ChannelMemberType, +} from 'app/features/channel-members/types/channel-member-types'; + +export const ChannelGuestsState = atomFamily({ + key: 'ChannelGuestsState', + default: _key => [], +}); diff --git a/twake/frontend/src/app/features/channel-members/state/channel-members.ts b/twake/frontend/src/app/features/channel-members/state/channel-members.ts new file mode 100644 index 0000000000..9bb8a2af2f --- /dev/null +++ b/twake/frontend/src/app/features/channel-members/state/channel-members.ts @@ -0,0 +1,11 @@ +import { atomFamily } from 'recoil'; + +import { + AtomChannelMembersKey, + ChannelMemberType, +} from 'app/features/channel-members/types/channel-member-types'; + +export const ChannelMembersState = atomFamily({ + key: 'ChannelMembersState', + default: _key => [], +}); diff --git a/twake/frontend/src/app/features/channel-members/types/channel-member-types.ts b/twake/frontend/src/app/features/channel-members/types/channel-member-types.ts new file mode 100644 index 0000000000..c04dadd036 --- /dev/null +++ b/twake/frontend/src/app/features/channel-members/types/channel-member-types.ts @@ -0,0 +1,19 @@ +export type ChannelMemberRole = 'member' | 'guest' | 'bot'; + +export type ChannelMemberNotificationLevel = 'all' | 'none' | 'mentions' | 'me'; + +export type ChannelMemberType = { + user_id?: string; + channel_id?: string; + type?: ChannelMemberRole; + last_access?: number; //Timestamp in seconds + last_increment?: number; //Number + favorite?: boolean; //Did the user add this channel to its favorites + notification_level?: ChannelMemberNotificationLevel; +}; + +export type AtomChannelMembersKey = { + companyId: string; + workspaceId: string; + channelId: string; +}; diff --git a/twake/frontend/src/app/features/channel-members/types/guest-types.ts b/twake/frontend/src/app/features/channel-members/types/guest-types.ts index 083504f41b..5ed9f73c2c 100644 --- a/twake/frontend/src/app/features/channel-members/types/guest-types.ts +++ b/twake/frontend/src/app/features/channel-members/types/guest-types.ts @@ -1,5 +1,5 @@ -import { PendingEmailResource } from 'app/features/workspace-members/types/pending-email'; -import { ChannelMemberResource } from 'app/features/channels/types/channel'; +import { PendingEmail } from 'app/features/pending-emails/types/pending-email'; +import { ChannelMemberType } from './channel-member-types'; export type GenericMemberType = 'pending-email' | 'guest'; @@ -7,5 +7,5 @@ export type GenericMember = { key: string; type: GenericMemberType; filterString: string; - resource: PendingEmailResource | ChannelMemberResource; + resource: PendingEmail | ChannelMemberType; }; diff --git a/twake/frontend/src/app/features/channel-members/utils/channel-members-roles.ts b/twake/frontend/src/app/features/channel-members/utils/channel-members-roles.ts new file mode 100644 index 0000000000..391f733abc --- /dev/null +++ b/twake/frontend/src/app/features/channel-members/utils/channel-members-roles.ts @@ -0,0 +1,5 @@ +import { ChannelMemberRole } from '../types/channel-member-types'; + +export const isGuestMember = (role: ChannelMemberRole) => { + return role === 'member'; +}; diff --git a/twake/frontend/src/app/features/channels/api/channels-mine-api-client.ts b/twake/frontend/src/app/features/channels/api/channels-mine-api-client.ts index ac032a771b..e1bb9c5bcc 100644 --- a/twake/frontend/src/app/features/channels/api/channels-mine-api-client.ts +++ b/twake/frontend/src/app/features/channels/api/channels-mine-api-client.ts @@ -1,5 +1,5 @@ import Api from '../../global/framework/api-service'; -import { ChannelMemberType, ChannelType } from 'app/features/channels/types/channel'; +import { ChannelType } from 'app/features/channels/types/channel'; import { TwakeService } from '../../global/framework/registry-decorator-service'; import { WebsocketRoom } from '../../global/types/websocket-types'; diff --git a/twake/frontend/src/app/features/channels/api/channels-reachable-api-client.ts b/twake/frontend/src/app/features/channels/api/channels-reachable-api-client.ts index 3cab1d05f7..121293bb58 100644 --- a/twake/frontend/src/app/features/channels/api/channels-reachable-api-client.ts +++ b/twake/frontend/src/app/features/channels/api/channels-reachable-api-client.ts @@ -1,17 +1,16 @@ +import { + ChannelsReachableGetResponse, + ChannelsReachableInviteUserRequest, + ChannelsReachableInviteUserResponse, + ChannelsReachableRemoveUserResponse, +} from 'app/features/channels/types/channels-reachable-types'; import Api from '../../global/framework/api-service'; -import { ChannelMemberType, ChannelType } from 'app/features/channels/types/channel'; +import { ChannelType } from 'app/features/channels/types/channel'; import { TwakeService } from '../../global/framework/registry-decorator-service'; - -type ChannelsReachableGetResponse = { resources: ChannelType[] }; -type ChannelsReachableInviteUserResponse = { resource: ChannelMemberType }; -type ChannelsReachableInviteUserRequest = { - resource: { - user_id: string; - }; -}; +import { ChannelMemberType } from 'app/features/channel-members/types/channel-member-types'; @TwakeService('ChannelsReachableAPIClientService') -class ChannelsReachableAPIClient { +class ChannelsReachableAPIClientService { private readonly prefix = '/internal/services/channels/v1/companies'; /** @@ -26,12 +25,12 @@ class ChannelsReachableAPIClient { } /** - * Add or remove user to/from a channel. + * Add user to a channel. * Every user in the channel (except guests) can invite or remove someone. * A system message will be sent on invitations. - * @param _companyId string - * @param _workspaceId string - * @param _userId string + * @param companyId string + * @param workspaceId string + * @param userId string * */ async inviteUser( @@ -41,7 +40,7 @@ class ChannelsReachableAPIClient { userId: string, ): Promise { return Api.post( - `${this.prefix}/${companyId}/workspaces/${workspaceId}/channels/${channelId}/members/${userId}`, + `${this.prefix}/${companyId}/workspaces/${workspaceId}/channels/${channelId}/members`, { resource: { user_id: userId, @@ -49,6 +48,26 @@ class ChannelsReachableAPIClient { }, ).then(result => result.resource); } -} -export default new ChannelsReachableAPIClient(); + /** + * Remove user from a channel. + * Every user in the channel (except guests) can invite or remove someone. + * A system message will be sent on invitations. + * @param companyId string + * @param workspaceId string + * @param userId string + * + */ + async removeUser( + companyId: string, + workspaceId: string, + channelId: string, + userId: string, + ): Promise { + return Api.delete( + `${this.prefix}/${companyId}/workspaces/${workspaceId}/channels/${channelId}/members/${userId}`, + ).then(result => result); + } +} +const ChannelsReachableAPIClient = new ChannelsReachableAPIClientService(); +export default ChannelsReachableAPIClient; diff --git a/twake/frontend/src/app/features/channels/types/channel.ts b/twake/frontend/src/app/features/channels/types/channel.ts index e543ab61f3..a6be01a6db 100644 --- a/twake/frontend/src/app/features/channels/types/channel.ts +++ b/twake/frontend/src/app/features/channels/types/channel.ts @@ -1,17 +1,5 @@ import { Resource } from 'app/deprecated/CollectionsReact/Collections'; - -export type ChannelMemberTypeType = 'member' | 'guest' | 'bot'; -export type ChannelMemberNotificationLevel = 'all' | 'none' | 'mentions' | 'me'; - -export type ChannelMemberType = { - user_id?: string; - channel_id?: string; - type?: ChannelMemberTypeType; - last_access?: number; //Timestamp in seconds - last_increment?: number; //Number - favorite?: boolean; //Did the user add this channel to its favorites - notification_level?: ChannelMemberNotificationLevel; -}; +import { ChannelMemberType } from 'app/features/channel-members/types/channel-member-types'; export type ChannelType = { company_id?: string; diff --git a/twake/frontend/src/app/features/channels/types/channels-reachable-types.ts b/twake/frontend/src/app/features/channels/types/channels-reachable-types.ts new file mode 100644 index 0000000000..21fb63d242 --- /dev/null +++ b/twake/frontend/src/app/features/channels/types/channels-reachable-types.ts @@ -0,0 +1,15 @@ +import { ChannelMemberType } from 'app/features/channel-members/types/channel-member-types'; +import { ChannelType } from './channel'; + +export type ChannelsReachableGetResponse = { resources: ChannelType[] }; +export type ChannelsReachableInviteUserResponse = { resource: ChannelMemberType }; +export type ChannelsReachableInviteUserRequest = { + resource: { + user_id: string; + }; +}; + +export type ChannelsReachableRemoveUserResponse = { + status: 'success' | 'error'; + errors: string[]; //List of errors +}; diff --git a/twake/frontend/src/app/features/pending-emails/api/pending-emails-api-client.ts b/twake/frontend/src/app/features/pending-emails/api/pending-emails-api-client.ts new file mode 100644 index 0000000000..b6073b12fb --- /dev/null +++ b/twake/frontend/src/app/features/pending-emails/api/pending-emails-api-client.ts @@ -0,0 +1,51 @@ +import { + AtomPendingEmailsKey, + DeletePendingEmailResponse, + PendingEmail, + SavePendingEmailRequest, +} from '../types/pending-email'; +import Api from 'app/features/global/framework/api-service'; +import { TwakeService } from 'app/features/global/framework/registry-decorator-service'; + +@TwakeService('PendingEmailsAPIClientService') +class PendingEmailsAPIClientService { + private readonly prefix = '/internal/services/channels/v1/companies'; + + async save(email: string, context: AtomPendingEmailsKey) { + const { companyId, workspaceId, channelId } = context; + return Api.post( + `${this.prefix}/${companyId}/workspaces/${workspaceId}/channels/${channelId}/pending_emails`, + { + resource: { + company_id: companyId, + workspace_id: workspaceId, + channel_id: channelId, + email, + }, + }, + ).then(r => r.resource); + } + + async get(email: string, context: AtomPendingEmailsKey) { + const { companyId, workspaceId, channelId } = context; + return Api.get( + `${this.prefix}/${companyId}/workspaces/${workspaceId}/channels/${channelId}/pending_emails/${email}`, + ); + } + + async list({ companyId, workspaceId, channelId }: AtomPendingEmailsKey) { + return Api.get<{ resources: PendingEmail[] }>( + `${this.prefix}/${companyId}/workspaces/${workspaceId}/channels/${channelId}/pending_emails`, + ).then(r => r.resources); + } + + async delete(email: string, context: AtomPendingEmailsKey) { + const { companyId, workspaceId, channelId } = context; + return Api.delete( + `${this.prefix}/${companyId}/workspaces/${workspaceId}/channels/${channelId}/pending_emails/${email}`, + ); + } +} + +const PendingEmailsAPIClient = new PendingEmailsAPIClientService(); +export default PendingEmailsAPIClient; diff --git a/twake/frontend/src/app/features/pending-emails/hooks/use-pending-emails.ts b/twake/frontend/src/app/features/pending-emails/hooks/use-pending-emails.ts new file mode 100644 index 0000000000..3adf1a35ee --- /dev/null +++ b/twake/frontend/src/app/features/pending-emails/hooks/use-pending-emails.ts @@ -0,0 +1,47 @@ +import { useRecoilState } from 'recoil'; + +import { + AtomPendingEmailsKey, + PendingEmail, +} from 'app/features/pending-emails/types/pending-email'; +import { useGlobalEffect } from 'app/features/global/hooks/use-global-effect'; +import { LoadingState } from 'app/features/global/state/atoms/Loading'; +import { PendingEmailsState } from 'app/features/pending-emails/state/pending-emails'; +import PendingEmailsAPIClient from '../api/pending-emails-api-client'; + +export const usePendingEmails = ( + key: AtomPendingEmailsKey, +): { + pendingEmails: PendingEmail[]; + loading: boolean; + refresh: () => Promise; +} => { + const [pendingEmails, _setPendingEmails] = useRecoilState(PendingEmailsState(key)); + const [loading, setLoading] = useRecoilState(LoadingState(`pending-emails-${key.channelId}`)); + + const refresh = async () => { + const pendingEmailsUpdated: PendingEmail[] | undefined = await PendingEmailsAPIClient.list(key); + + if (pendingEmailsUpdated) _setPendingEmails(pendingEmailsUpdated); + }; + + useGlobalEffect( + 'usePendingEmails', + async () => { + if (!pendingEmails) setLoading(true); + + await refresh(); + + setLoading(false); + }, + [key, pendingEmails], + ); + + // useRealTimeHook + + return { + pendingEmails, + loading, + refresh, + }; +}; diff --git a/twake/frontend/src/app/features/pending-emails/state/pending-emails.ts b/twake/frontend/src/app/features/pending-emails/state/pending-emails.ts new file mode 100644 index 0000000000..6bcc6b6b2f --- /dev/null +++ b/twake/frontend/src/app/features/pending-emails/state/pending-emails.ts @@ -0,0 +1,11 @@ +import { atomFamily } from 'recoil'; + +import { + AtomPendingEmailsKey, + PendingEmail, +} from 'app/features/pending-emails/types/pending-email'; + +export const PendingEmailsState = atomFamily({ + key: 'PendingEmailsState', + default: _key => [], +}); diff --git a/twake/frontend/src/app/features/workspace-members/types/pending-email.ts b/twake/frontend/src/app/features/pending-emails/types/pending-email.ts similarity index 53% rename from twake/frontend/src/app/features/workspace-members/types/pending-email.ts rename to twake/frontend/src/app/features/pending-emails/types/pending-email.ts index d73406f21a..dbebce0e61 100644 --- a/twake/frontend/src/app/features/workspace-members/types/pending-email.ts +++ b/twake/frontend/src/app/features/pending-emails/types/pending-email.ts @@ -10,3 +10,16 @@ export class PendingEmailResource extends Resource { _resourcePrimaryKey: string[] = ['workspace_id', 'company_id', 'channel_id', 'email']; _resourceIdKey: string = 'email'; } + +export type AtomPendingEmailsKey = { companyId: string; workspaceId: string; channelId: string }; + +export type SavePendingEmailRequest = { + resource: { + workspace_id: string; + channel_id: string; + company_id: string; + email: string; + }; +}; + +export type DeletePendingEmailResponse = { status: 'success' | 'error' }; diff --git a/twake/frontend/src/app/views/applications/messages/message/parts/ChannelActivity/ActivityMessage.tsx b/twake/frontend/src/app/views/applications/messages/message/parts/ChannelActivity/ActivityMessage.tsx index 339141b169..3e2ecc0900 100644 --- a/twake/frontend/src/app/views/applications/messages/message/parts/ChannelActivity/ActivityMessage.tsx +++ b/twake/frontend/src/app/views/applications/messages/message/parts/ChannelActivity/ActivityMessage.tsx @@ -3,10 +3,11 @@ import { Row, Typography } from 'antd'; import Languages from 'app/features/global/services/languages-service'; import Emojione from 'app/components/emojione/emojione'; import User from 'app/components/twacode/blocks/user'; -import { ChannelMemberType, ChannelType } from 'app/features/channels/types/channel'; +import { ChannelType } from 'app/features/channels/types/channel'; import { TabType } from 'app/features/tabs/types/tab'; import { getCompanyApplications } from 'app/features/applications/state/company-applications'; import Groups from 'app/deprecated/workspaces/groups.js'; +import { ChannelMemberType } from 'app/features/channel-members/types/channel-member-types'; enum ChannelActivityEnum { CHANNEL_MEMBER_CREATED = 'channel:activity:member:created', diff --git a/twake/frontend/src/app/views/client/channels-bar/Modals/ChannelMembersList.tsx b/twake/frontend/src/app/views/client/channels-bar/Modals/ChannelMembersList.tsx index a55c33fb14..a3ee277a0d 100644 --- a/twake/frontend/src/app/views/client/channels-bar/Modals/ChannelMembersList.tsx +++ b/twake/frontend/src/app/views/client/channels-bar/Modals/ChannelMembersList.tsx @@ -1,19 +1,13 @@ -import React, { FC, useState } from 'react'; -import { Col, Row, Typography, Input } from 'antd'; +import React, { useState } from 'react'; import { Search } from 'react-feather'; +import { Col, Row, Typography, Input } from 'antd'; import PerfectScrollbar from 'react-perfect-scrollbar'; -import { - ChannelMemberResource, - ChannelResource, - ChannelType, -} from 'app/features/channels/types/channel'; import { UserType } from 'app/features/users/types/user'; - +import { ChannelType } from 'app/features/channels/types/channel'; import Strings from 'app/features/global/utils/strings.js'; import Languages from 'app/features/global/services/languages-service'; import UsersService from 'app/features/users/services/current-user-service'; -import Collections from 'app/deprecated/CollectionsReact/Collections'; import MemberChannelRow from 'app/views/client/channels-bar/Parts/Header/MemberChannelRow'; import ObjectModal from 'components/object-modal/object-modal'; @@ -22,6 +16,8 @@ import { useUsersListener } from 'app/features/users/hooks/use-users-listener'; import UserAPIClient from 'app/features/users/api/user-api-client'; import { WorkspaceUserType } from 'app/features/workspaces/types/workspace'; import { delayRequest } from 'app/features/global/utils/managedSearchRequest'; +import { useChannelMembers } from 'app/features/channel-members/hooks/use-channel-members'; +import { ChannelMemberType } from 'app/features/channel-members/types/channel-member-types'; type PropsType = { closable?: boolean; @@ -31,17 +27,19 @@ type PropsType = { const { Link } = Typography; const defaultLimit = 20; -const ChannelMembersList: FC = props => { +const ChannelMembersList = (props: PropsType) => { const { company_id, workspace_id, id } = props.channel; const [search, setSearch] = useState(''); const [limit, setLimit] = useState(defaultLimit); const [searchedUsers, setSearchedUsers] = useState([]); + const { channelMembers } = useChannelMembers({ + companyId: company_id || '', + workspaceId: workspace_id || '', + channelId: id || '', + }); - const collectionPath: string = `/channels/v1/companies/${company_id}/workspaces/${workspace_id}/channels/${id}/members/`; - const channelMembersCollection = Collections.get(collectionPath, ChannelMemberResource); - const channelMembers = channelMembersCollection.useWatcher({}, { limit: limit }); - const channelMembersUid = channelMembers.map(member => member.data.user_id || ''); + const channelMembersUid = channelMembers.map(member => member.user_id || ''); useUsersListener(channelMembersUid); @@ -62,16 +60,18 @@ const ChannelMembersList: FC = props => { const filterUsers = (user: UserType, filter: boolean) => (filter ? user : false); const compareFullname = ( - a: UserType | ChannelMemberResource, - b: UserType | ChannelMemberResource, - isMemberResource?: boolean, + a: UserType | ChannelMemberType, + b: UserType | ChannelMemberType, + isMemberType?: boolean, ) => { let userA = a; let userB = b; - if (isMemberResource) { - userA = DepreciatedCollections.get('users').find(a.id); - userB = DepreciatedCollections.get('users').find(b.id); + + if (isMemberType) { + userA = DepreciatedCollections.get('users').find((a as ChannelMemberType).user_id); + userB = DepreciatedCollections.get('users').find((b as ChannelMemberType).user_id); } + return UsersService.getFullName(userA as UserType).localeCompare( UsersService.getFullName(userB as UserType), ); @@ -119,21 +119,22 @@ const ChannelMembersList: FC = props => { {!search.length && channelMembers.length > 0 && channelMembers + .map(m => m) .sort((a, b) => compareFullname(a, b, true)) .map(user => props.channel.id ? ( -
+
) : ( <> ), )} + {!search.length && !channelMembers.length && ( = props => { key={userId} userId={userId} channelId={props.channel.id} - collection={channelMembersCollection} inAddition={!channelMembersUid.includes(userId || '') ? true : false} />
diff --git a/twake/frontend/src/app/views/client/channels-bar/Modals/GuestManagement.tsx b/twake/frontend/src/app/views/client/channels-bar/Modals/GuestManagement.tsx index f1c4ca3924..29260bbb67 100644 --- a/twake/frontend/src/app/views/client/channels-bar/Modals/GuestManagement.tsx +++ b/twake/frontend/src/app/views/client/channels-bar/Modals/GuestManagement.tsx @@ -1,20 +1,19 @@ import React, { useEffect, useState } from 'react'; -import PerfectScrollbar from 'react-perfect-scrollbar'; import { capitalize } from 'lodash'; import { Input, Row, Typography } from 'antd'; +import PerfectScrollbar from 'react-perfect-scrollbar'; + import ObjectModal from 'app/components/object-modal/object-modal'; -import RouterService from 'app/features/router/services/router-service'; import MemberChannelRow from '../Parts/Header/MemberChannelRow'; -import { - ChannelMemberResource, - ChannelResource, - ChannelType, -} from 'app/features/channels/types/channel'; -import Collections from 'app/deprecated/CollectionsReact/Collections'; -import { PendingEmailResource } from 'app/features/workspace-members/types/pending-email'; +import { ChannelType } from 'app/features/channels/types/channel'; import GuestManagementService from 'app/features/channel-members/service/guest-management-service'; import WorkspacesUsers from 'app/features/workspace-members/services/workspace-members-service'; import Languages from 'app/features/global/services/languages-service'; +import { usePendingEmails } from 'app/features/pending-emails/hooks/use-pending-emails'; +import useRouterCompany from 'app/features/router/hooks/use-router-company'; +import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace'; +import { ChannelMemberType } from 'app/features/channel-members/types/channel-member-types'; +import { useChannelGuests } from 'app/features/channel-members/hooks/use-channel-guests'; type PropsType = { channel: ChannelType; @@ -24,22 +23,27 @@ export default ({ channel }: PropsType): JSX.Element => { const [search, setSearch] = useState(''); const [limit, setLimit] = useState(10); const [shouldDisplayAdditionRow, setShouldDisplayAdditionRow] = useState(false); - const { workspaceId, companyId } = RouterService.getStateFromRoute(); + const companyId = useRouterCompany(); + const workspaceId = useRouterWorkspace(); - GuestManagementService.bind({ search, channel_id: channel.id || '' }); - const { list } = GuestManagementService; + const { pendingEmails } = usePendingEmails({ + companyId, + workspaceId, + channelId: channel.id || '', + }); + const { channelGuests } = useChannelGuests({ + companyId, + workspaceId, + channelId: channel.id || '', + }); - const memberCollectionPath: string = `/channels/v1/companies/${companyId}/workspaces/${workspaceId}/channels/${channel.id}/members/`; - const channelMembersCollection = Collections.get(memberCollectionPath, ChannelMemberResource); + GuestManagementService.bind({ + search, + pendingEmails: pendingEmails.map(o => o), + channelMembers: channelGuests.map(o => o), + }); - const pendingEmailsCollectionPath = `/channels/v1/companies/${companyId}/workspaces/${workspaceId}/channels/${channel.id}/pending_emails/`; - const pendingEmailsCollection = Collections.get( - pendingEmailsCollectionPath, - PendingEmailResource, - ); - - channelMembersCollection.useWatcher({}); - pendingEmailsCollection.useWatcher({}); + const { list } = GuestManagementService; useEffect(() => { const searchedEmail = WorkspacesUsers.fullStringToEmails(search)[0] as string; @@ -84,7 +88,6 @@ export default ({ channel }: PropsType): JSX.Element => { {shouldDisplayAdditionRow && ( {
diff --git a/twake/frontend/src/app/views/client/channels-bar/Modals/WorkspaceChannelList.tsx b/twake/frontend/src/app/views/client/channels-bar/Modals/WorkspaceChannelList.tsx index 18cf4dc60b..0d32801114 100644 --- a/twake/frontend/src/app/views/client/channels-bar/Modals/WorkspaceChannelList.tsx +++ b/twake/frontend/src/app/views/client/channels-bar/Modals/WorkspaceChannelList.tsx @@ -13,7 +13,6 @@ import ModalManager from 'app/components/modal/modal-manager'; import { UserType } from 'app/features/users/types/user'; import UsersService from 'app/features/users/services/current-user-service'; import { ChannelType } from 'app/features/channels/types/channel'; -import { Collection } from 'app/deprecated/CollectionsReact/Collections'; import PerfectScrollbar from 'react-perfect-scrollbar'; import { delayRequest } from 'app/features/global/utils/managedSearchRequest'; import ChannelMembersAPIClient from 'app/features/channel-members/api/channel-members-api-client'; @@ -67,11 +66,11 @@ export default () => { const joinChannel = async (channel: ChannelType) => { if (channel.company_id && channel.workspace_id && channel.id) { - const channelMembers = await ChannelMembersAPIClient.get( - channel.company_id, - channel.workspace_id, - channel.id, - ); + const channelMembers = await ChannelMembersAPIClient.list({ + companyId: channel.company_id, + workspaceId: channel.workspace_id, + channelId: channel.id, + }); const alreadyMemberInChannel = channelMembers.map(m => m.user_id)?.includes(currentUserId); diff --git a/twake/frontend/src/app/views/client/channels-bar/Modals/WorkspaceChannelList/WorkspaceChannelRow.tsx b/twake/frontend/src/app/views/client/channels-bar/Modals/WorkspaceChannelList/WorkspaceChannelRow.tsx index 6edb63f4cc..d73154748f 100644 --- a/twake/frontend/src/app/views/client/channels-bar/Modals/WorkspaceChannelList/WorkspaceChannelRow.tsx +++ b/twake/frontend/src/app/views/client/channels-bar/Modals/WorkspaceChannelList/WorkspaceChannelRow.tsx @@ -35,11 +35,11 @@ export default ({ channel, joined, active }: PropsType) => { const joinChannel = async () => { if (channel.company_id && channel.workspace_id && channel.id) { - const channelMembers = await ChannelMembersAPIClient.get( - channel.company_id, - channel.workspace_id, - channel.id, - ); + const channelMembers = await ChannelMembersAPIClient.list({ + companyId: channel.company_id, + workspaceId: channel.workspace_id, + channelId: channel.id, + }); const alreadyMemberInChannel = channelMembers.map(m => m.user_id)?.includes(userId); diff --git a/twake/frontend/src/app/views/client/channels-bar/Parts/Channel/ChannelMenu.tsx b/twake/frontend/src/app/views/client/channels-bar/Parts/Channel/ChannelMenu.tsx index af5ae30272..b5cb12dbe6 100644 --- a/twake/frontend/src/app/views/client/channels-bar/Parts/Channel/ChannelMenu.tsx +++ b/twake/frontend/src/app/views/client/channels-bar/Parts/Channel/ChannelMenu.tsx @@ -1,7 +1,7 @@ // eslint-disable-next-line @typescript-eslint/no-use-before-define import React from 'react'; -import { ChannelMemberType, ChannelType } from 'app/features/channels/types/channel'; +import { ChannelType } from 'app/features/channels/types/channel'; import ChannelMembersList from 'app/views/client/channels-bar/Modals/ChannelMembersList'; import Icon from 'app/components/icon/icon'; import Menu from 'components/menus/menu'; @@ -41,7 +41,7 @@ export default (props: PropsType): JSX.Element => { const companyId = props.channel.company_id; const { refresh: refreshFavoriteChannels } = useFavoriteChannels(); const { Feature, FeatureNames } = useFeatureToggles(); - const channelMember: ChannelMemberType = props.channel.user_member || {}; + const channelMember = props.channel.user_member || {}; Languages.useListener(); @@ -52,7 +52,7 @@ export default (props: PropsType): JSX.Element => { props.channel.id && currentUser?.id ) { - await ChannelMembersAPIClient.save( + await ChannelMembersAPIClient.updateChannelMemberPreferences( channelMember, { notification_level: preference }, { @@ -72,7 +72,7 @@ export default (props: PropsType): JSX.Element => { props.channel.id && currentUser?.id ) { - await ChannelMembersAPIClient.save( + await ChannelMembersAPIClient.updateChannelMemberPreferences( channelMember, { favorite: state }, { diff --git a/twake/frontend/src/app/views/client/channels-bar/Parts/Header/MemberChannelRow.tsx b/twake/frontend/src/app/views/client/channels-bar/Parts/Header/MemberChannelRow.tsx index e6ed02c29a..70727ecabf 100644 --- a/twake/frontend/src/app/views/client/channels-bar/Parts/Header/MemberChannelRow.tsx +++ b/twake/frontend/src/app/views/client/channels-bar/Parts/Header/MemberChannelRow.tsx @@ -3,20 +3,21 @@ import React, { useState } from 'react'; import { Button, Col, Row, Tag, Typography } from 'antd'; import { Mail, PlusCircle, Trash } from 'react-feather'; -import { ChannelMemberResource } from 'app/features/channels/types/channel'; import { getUserParts } from 'app/components/member/user-parts'; import Languages from 'app/features/global/services/languages-service'; import './MemberChannelRow.scss'; import Menu from 'app/components/menus/menu'; import Icon from 'app/components/icon/icon'; import AccessRightsService from 'app/features/workspace-members/services/workspace-members-access-rights-service'; -import RouterServices from 'app/features/router/services/router-service'; -import Collection from 'app/deprecated/CollectionsV2/Collection'; import UsersService from 'app/features/users/services/current-user-service'; import ModalManager from 'app/components/modal/modal-manager'; -import { PendingEmailResource } from 'app/features/workspace-members/types/pending-email'; -import GuestManagementService from 'app/features/channel-members/service/guest-management-service'; import UserService from 'app/features/users/services/current-user-service'; +import ChannelsReachableAPIClient from 'app/features/channels/api/channels-reachable-api-client'; +import useRouterCompany from 'app/features/router/hooks/use-router-company'; +import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace'; +import { useChannelMembers } from 'app/features/channel-members/hooks/use-channel-members'; +import { usePendingEmails } from 'app/features/pending-emails/hooks/use-pending-emails'; +import PendingEmailsAPIClient from 'app/features/pending-emails/api/pending-emails-api-client'; const { Text } = Typography; @@ -24,7 +25,6 @@ type Props = { channelId: string; userId?: string; inAddition?: boolean; - collection: Collection; userType?: 'member' | 'guest' | 'bot' | 'pending-email'; inPendingEmailAddition?: boolean; pendingEmailToAdd?: string; @@ -32,11 +32,23 @@ type Props = { onPendingEmailDeletion?: () => unknown; }; -export default (props: Props): JSX.Element => { +const MemberChannelRow = (props: Props): JSX.Element => { let userEvents: JSX.Element; + const companyId = useRouterCompany(); + const workspaceId = useRouterWorkspace(); const [isMember, setIsMember] = useState(false); const [selected, setSelected] = useState(false); - const { workspaceId, companyId } = RouterServices.getStateFromRoute(); + const { refresh: refreshChannelMembers } = useChannelMembers({ + companyId, + workspaceId, + channelId: props.channelId, + }); + + const { refresh: refreshPendingEmails } = usePendingEmails({ + companyId, + workspaceId, + channelId: props.channelId, + }); const currentUserId: string = UsersService.getCurrentUserId(); const { avatar, name, users, companyRole } = getUserParts({ @@ -46,46 +58,41 @@ export default (props: Props): JSX.Element => { }); const addUser = async () => { - await props.collection.upsert( - new ChannelMemberResource({ - user_id: props.userId, - channel_id: props.channelId, - type: 'member', // "member" | "guest" | "bot", - }), - ); - return setIsMember(true); + props.userId && + (await ChannelsReachableAPIClient.inviteUser( + companyId, + workspaceId, + props.channelId, + props.userId, + ) + .then(refreshChannelMembers) + .finally(() => setIsMember(true))); }; const leaveChannel = async (channelId: string, userId: string) => { - //Fixme, this is not pretty, we should find a way to do this in one line - const channelMemberResource = new ChannelMemberResource({ - user_id: userId, - channel_id: channelId, - type: 'member', // "member" | "guest" | "bot", - }); - channelMemberResource.setPersisted(); - await props.collection.upsert(channelMemberResource, { withoutBackend: true }); - await props.collection.remove(channelMemberResource); - - setIsMember(false); + await ChannelsReachableAPIClient.removeUser(companyId, workspaceId, channelId, userId) + .then(refreshChannelMembers) + .finally(() => setIsMember(false)); currentUserId === props.userId && ModalManager.close(); }; const savePendingEmail = () => - GuestManagementService.upsertPendingEmail({ - workspace_id: workspaceId || '', - channel_id: props.channelId || '', - company_id: companyId || '', - email: props.pendingEmailToAdd || '', - }).finally(props.onPendingEmailAddition); + props.pendingEmailToAdd && + PendingEmailsAPIClient.save(props.pendingEmailToAdd, { + companyId, + workspaceId, + channelId: props.channelId, + }).finally(refreshPendingEmails); const removePendingEmail = async () => { - const col = props.collection as Collection; - const pendingEmail = col.findOne({ id: props.userId }); - return await GuestManagementService.deletePendingEmail(pendingEmail.data).finally( - props.onPendingEmailDeletion, - ); + props.userId && + props.userType === 'pending-email' && + PendingEmailsAPIClient.delete(props.userId, { + companyId, + workspaceId, + channelId: props.channelId, + }).finally(refreshPendingEmails); }; if (props.inAddition) { @@ -160,8 +167,7 @@ export default (props: Props): JSX.Element => { } if (props.userType === 'pending-email') { - const col = props.collection as Collection; - const pendingEmail = col.findOne({ id: props.userId }); + const pendingEmail: string | undefined = props.userId; const shouldDisplayPendingRow = !!pendingEmail; if (shouldDisplayPendingRow) { @@ -177,7 +183,7 @@ export default (props: Props): JSX.Element => { - {pendingEmail.data.email} + {pendingEmail} @@ -227,3 +233,5 @@ export default (props: Props): JSX.Element => { ); }; + +export default MemberChannelRow; diff --git a/twake/frontend/src/app/views/client/main-view/MainHeader/ChannelHeader/ChannelAvatars.tsx b/twake/frontend/src/app/views/client/main-view/MainHeader/ChannelHeader/ChannelAvatars.tsx index bf8083b52a..0ec1921ba4 100644 --- a/twake/frontend/src/app/views/client/main-view/MainHeader/ChannelHeader/ChannelAvatars.tsx +++ b/twake/frontend/src/app/views/client/main-view/MainHeader/ChannelHeader/ChannelAvatars.tsx @@ -1,19 +1,19 @@ -import RouterServices from 'app/features/router/services/router-service'; import { getUserParts } from 'app/components/member/user-parts'; -import Collections from 'app/deprecated/CollectionsReact/Collections'; -import { ChannelMemberResource } from 'app/features/channels/types/channel'; import { useUsersListener } from 'app/features/users/hooks/use-users-listener'; +import useRouterCompany from 'app/features/router/hooks/use-router-company'; +import useRouterChannel from 'app/features/router/hooks/use-router-channel'; +import { useChannelMembers } from 'app/features/channel-members/hooks/use-channel-members'; -export default (props: { workspaceId: string }): JSX.Element => { - const { companyId, channelId } = RouterServices.getStateFromRoute(); +export default ({ workspaceId }: { workspaceId: string }): JSX.Element => { + const companyId = useRouterCompany(); + const channelId = useRouterChannel(); - const collectionPath: string = `/channels/v1/companies/${companyId}/workspaces/${props.workspaceId}/channels/${channelId}/members/`; - const channelMembersCollection = Collections.get(collectionPath, ChannelMemberResource); + const { channelMembers } = useChannelMembers({ companyId, workspaceId, channelId }); + + const members = channelMembers.filter((_m, i) => i < 10).map(m => m.user_id || ''); - const members = channelMembersCollection - .useWatcher({}, { limit: 10 }) - .map(i => i.data.user_id || ''); useUsersListener(members); + const { avatar } = getUserParts({ usersIds: members, keepMyself: true, max: 7 }); return avatar; diff --git a/twake/frontend/src/app/views/client/popup/WorkspaceParameter/Pages/WorkspacePartner.tsx b/twake/frontend/src/app/views/client/popup/WorkspaceParameter/Pages/WorkspacePartner.tsx index 9331357c3b..70ddd8affa 100755 --- a/twake/frontend/src/app/views/client/popup/WorkspaceParameter/Pages/WorkspacePartner.tsx +++ b/twake/frontend/src/app/views/client/popup/WorkspaceParameter/Pages/WorkspacePartner.tsx @@ -21,6 +21,7 @@ import { useCurrentCompany } from 'app/features/companies/hooks/use-companies'; import './Pages.scss'; export const AdminSwitch = (props: { col: any; adminLevelId: string; onChange: any }) => { + // @ts-ignore workspacesUsers.useListener(useState); const loading = workspacesUsers.updateLevelUserLoading[props.col.user.id]; const checked = props.col.level === props.adminLevelId; diff --git a/twake/frontend/yarn.lock b/twake/frontend/yarn.lock index 63103fbbe6..ae2b948fe2 100644 --- a/twake/frontend/yarn.lock +++ b/twake/frontend/yarn.lock @@ -3,12 +3,11 @@ "@ampproject/remapping@^2.0.0": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.0.2.tgz#f3d9760bf30588c51408dbe7c05ff2bb13069307" - integrity sha512-sE8Gx+qSDMLoJvb3QarJJlDQK7SSY4rK3hxp4XsiANeFOmjU46ZI7Y9adAQRJrmbz8zbtZkp3mJTT+rGxtF0XA== + version "2.1.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.1.tgz#7922fb0817bf3166d8d9e258c57477e3fd1c3610" + integrity sha512-Aolwjd7HSC2PyY0fDj/wA/EimQT4HfEnFYNp5s9CQlrdhyvWTtvZ5YzrUPu6R6/1jKiUlxu8bUhkdSnKHNAHMA== dependencies: - "@jridgewell/trace-mapping" "^0.2.2" - sourcemap-codec "1.4.8" + "@jridgewell/trace-mapping" "^0.3.0" "@ant-design/colors@^6.0.0": version "6.0.0" @@ -122,16 +121,16 @@ source-map "^0.5.0" "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.4.5", "@babel/core@^7.7.5", "@babel/core@^7.8.4", "@babel/core@^7.8.7": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.0.tgz#16b8772b0a567f215839f689c5ded6bb20e864d5" - integrity sha512-x/5Ea+RO5MvF9ize5DeVICJoVrNv0Mi2RnIABrZEKYvPEpldXwauPkgvYA17cKa6WpU3LoYvYbuEMFtSNFsarA== + version "7.17.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.2.tgz#2c77fc430e95139d816d39b113b31bf40fb22337" + integrity sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw== dependencies: "@ampproject/remapping" "^2.0.0" "@babel/code-frame" "^7.16.7" "@babel/generator" "^7.17.0" "@babel/helper-compilation-targets" "^7.16.7" "@babel/helper-module-transforms" "^7.16.7" - "@babel/helpers" "^7.17.0" + "@babel/helpers" "^7.17.2" "@babel/parser" "^7.17.0" "@babel/template" "^7.16.7" "@babel/traverse" "^7.17.0" @@ -194,10 +193,10 @@ browserslist "^4.17.5" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7", "@babel/helper-create-class-features-plugin@^7.17.0", "@babel/helper-create-class-features-plugin@^7.8.3": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.0.tgz#3ba0eae83313d4077319d11a768c46adad026433" - integrity sha512-S3+IHG72pJFb0RmJgeXg/TjVKt641ZsLla028haXJjdqCf9eccE5r1JsdO//L7nzTDzXjtC+hwV/lrkEb2+t0Q== +"@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7", "@babel/helper-create-class-features-plugin@^7.17.1", "@babel/helper-create-class-features-plugin@^7.8.3": + version "7.17.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz#9699f14a88833a7e055ce57dcd3ffdcd25186b21" + integrity sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-environment-visitor" "^7.16.7" @@ -424,10 +423,10 @@ "@babel/traverse" "^7.16.8" "@babel/types" "^7.16.8" -"@babel/helpers@^7.12.1", "@babel/helpers@^7.17.0", "@babel/helpers@^7.9.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.0.tgz#79cdf6c66a579f3a7b5e739371bc63ca0306886b" - integrity sha512-Xe/9NFxjPwELUvW2dsukcMZIp6XwPSbI4ojFBJuX5ramHuVE22SVcZIwqzdWo5uCgeTXW8qV97lMvSOjq+1+nQ== +"@babel/helpers@^7.12.1", "@babel/helpers@^7.17.2", "@babel/helpers@^7.9.0": + version "7.17.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.2.tgz#23f0a0746c8e287773ccd27c14be428891f63417" + integrity sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ== dependencies: "@babel/template" "^7.16.7" "@babel/traverse" "^7.17.0" @@ -521,11 +520,11 @@ "@babel/plugin-syntax-decorators" "^7.8.3" "@babel/plugin-proposal-decorators@^7.16.4": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.17.0.tgz#fc0f689fe2535075056c587bc10c176fa9990443" - integrity sha512-JR8HTf3T1CsdMqfENrZ9pqncwsH4sPcvsyDLpvmv8iIbpDmeyBD7HPfGAIqkQph2j5d3B84hTm+m3qHPAedaPw== + version "7.17.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.17.2.tgz#c36372ddfe0360cac1ee331a238310bddca11493" + integrity sha512-WH8Z95CwTq/W8rFbMqb9p3hicpt4RX4f0K659ax2VHxgOyT6qQmUaEVEjIh4WR9Eh9NymkVn5vwsrE68fAQNUw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.0" + "@babel/helper-create-class-features-plugin" "^7.17.1" "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-replace-supers" "^7.16.7" "@babel/plugin-syntax-decorators" "^7.17.0" @@ -1371,17 +1370,17 @@ "@babel/plugin-transform-typescript" "^7.16.7" "@babel/runtime-corejs2@^7.3.4": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.17.0.tgz#d7c0ce349974f1acee1da9d8994bb5c0b546642a" - integrity sha512-5GnrnCnHtTjveqE/gU7uDEbXwNhffLbNLpxu2dxUtRgYId7NTEQRg//urSeNPlYXivJzMGXAyGh7d1xSNo1Gog== + version "7.17.2" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.17.2.tgz#4d7dbc218e958ce48df6250faa73616e8495bc1a" + integrity sha512-EamjJvKlHTdSmJ8t6yHtqttdiA3xThvTNdmGb0Kh0oqRhV1SU2JGFU5TjVCg35Vnn8MYfUBHHtLZYHIY+W28qw== dependencies: core-js "^2.6.5" regenerator-runtime "^0.13.4" "@babel/runtime-corejs3@^7.10.2": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.17.0.tgz#9de2f75b3ca4b68628c01bd76410b64faa4644f7" - integrity sha512-qeydncU80ravKzovVncW3EYaC1ji3GpntdPgNcJy9g7hHSY6KX+ne1cbV3ov7Zzm4F1z0+QreZPCuw1ynkmYNg== + version "7.17.2" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz#fdca2cd05fba63388babe85d349b6801b008fd13" + integrity sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg== dependencies: core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" @@ -1394,9 +1393,9 @@ regenerator-runtime "^0.13.4" "@babel/runtime@>=7.0.0-beta.56", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.0.tgz#b8d142fc0f7664fb3d9b5833fd40dcbab89276c0" - integrity sha512-etcO/ohMNaNA2UBdaXBBSX/3aEzFMRrVfaPv8Ptc0k+cWpWW0QFiGZ2XnVqQZI1Cf734LbPGmqBKWESfW4x/dQ== + version "7.17.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941" + integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw== dependencies: regenerator-runtime "^0.13.4" @@ -1530,12 +1529,17 @@ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz#b44e52db3b6b20523e0c57ef8c42d315532cb903" integrity sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg== +"@fortawesome/fontawesome-common-types@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.3.0.tgz#949995a05c0d8801be7e0a594f775f1dbaa0d893" + integrity sha512-CA3MAZBTxVsF6SkfkHXDerkhcQs0QPofy43eFdbWJJkZiq3SfiaH1msOkac59rQaqto5EqWnASboY1dBuKen5w== + "@fortawesome/fontawesome-svg-core@^1.2.30": - version "1.2.36" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz#4f2ea6f778298e0c47c6524ce2e7fd58eb6930e3" - integrity sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA== + version "1.3.0" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.3.0.tgz#343fac91fa87daa630d26420bfedfba560f85885" + integrity sha512-UIL6crBWhjTNQcONt96ExjUnKt1D68foe3xjEensLDclqQ6YagwCRYVQdrp/hW0ALRp/5Fv/VKw+MqTUWYYvPg== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.36" + "@fortawesome/fontawesome-common-types" "^0.3.0" "@fortawesome/free-solid-svg-icons@^5.14.0": version "5.15.4" @@ -1934,17 +1938,22 @@ chalk "^4.0.0" "@jridgewell/resolve-uri@^3.0.3": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.4.tgz#b876e3feefb9c8d3aa84014da28b5e52a0640d72" - integrity sha512-cz8HFjOFfUBtvN+NXYSFMHYRdxZMaEl0XypVrhzxBgadKIXhIkRd8aMeHhmF56Sl7SuS8OnUpQ73/k9LE4VnLg== + version "3.0.5" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" + integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== -"@jridgewell/trace-mapping@^0.2.2": - version "0.2.4" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.2.4.tgz#9c3a1f8e260fe59003bac7b0e355fe04ca875389" - integrity sha512-W/qPHey63KLHPC7zzvXeul8ouaugOu232lUPbyBAuoG9s+bmDSP1ANulLjGCf34Je3sGUPtw/Cg52e7ALY9+3w== +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.11" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" + integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== + +"@jridgewell/trace-mapping@^0.3.0": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" + integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== dependencies: "@jridgewell/resolve-uri" "^3.0.3" - sourcemap-codec "1.4.8" + "@jridgewell/sourcemap-codec" "^1.4.10" "@material-ui/core@^4.11.0": version "4.12.3" @@ -2039,9 +2048,9 @@ fastq "^1.6.0" "@npmcli/fs@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.0.tgz#bec1d1b89c170d40e1b73ad6c943b0b75e7d2951" - integrity sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA== + version "1.1.1" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" + integrity sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ== dependencies: "@gar/promisify" "^1.0.1" semver "^7.3.5" @@ -2187,9 +2196,9 @@ prop-types "^15.7.2" "@stripe/stripe-js@^1.0.3": - version "1.22.0" - resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-1.22.0.tgz#9d3d2f0a1ce81f185ec477fd7cc67544b2b2a00c" - integrity sha512-fm8TR8r4LwbXgBIYdPmeMjJJkxxFC66tvoliNnmXOpUgZSgQKoNPW3ON0ZphZIiif1oqWNhAaSrr7tOvGu+AFg== + version "1.23.0" + resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-1.23.0.tgz#62eed14e83c63c3e8c27f14f6b1e6feb8496c867" + integrity sha512-+7w4rVs71Fk8/8uzyzQB5GotHSH9mjOjxM3EYDq/3MR3I2ewELHtvWVMOqfS/9WSKCaKv7h7eFLsMZGpK5jApQ== "@surma/rollup-plugin-off-main-thread@^1.1.1": version "1.4.2" @@ -2623,9 +2632,9 @@ "@babel/types" "^7.3.0" "@types/cheerio@*": - version "0.22.30" - resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.30.tgz#6c1ded70d20d890337f0f5144be2c5e9ce0936e6" - integrity sha512-t7ZVArWZlq3dFa9Yt33qFBQIK4CQd1Q3UJp0V+UhP6vgLWLM6Qug7vZuRSGXg45zXeB1Fm5X2vmBkEX58LV2Tw== + version "0.22.31" + resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.31.tgz#b8538100653d6bb1b08a1e46dec75b4f2a5d5eb6" + integrity sha512-Kt7Cdjjdi2XWSfrZ53v4Of0wG3ZcmaegFXjMmz9tfNrZSkzzo36G0AL1YqSdcIA78Etjt6E609pt5h1xnQkPUw== dependencies: "@types/node" "*" @@ -2640,9 +2649,9 @@ integrity sha512-eI6gvpcGHLk3dAuHYnRCAjX+41gMv1nz/VP55wAe5HtmAKDOoPSfr3f6vkMc08ov1S0NsjvUBxDtHHxqQY1LGA== "@types/draft-js@*", "@types/draft-js@^0.11.2": - version "0.11.8" - resolved "https://registry.yarnpkg.com/@types/draft-js/-/draft-js-0.11.8.tgz#ef1e1dfbb5e3f1d13a8769eb5dfd282cae8b127b" - integrity sha512-WddWQKbjbVEeJEuwS7oczvaVGsfML141ywwgpFeKE2E+NI9/Qk+AU/xTd6A7/dNsS8lf3ziubm8WugrttwQk2g== + version "0.11.9" + resolved "https://registry.yarnpkg.com/@types/draft-js/-/draft-js-0.11.9.tgz#b7535fb2511e8b8c8e33ffac2199a5f2988f4fd5" + integrity sha512-cQJBZjjIlGaPA1tOY+wGz2KhlPtAAZOIXpUvGPxPRw5uzZ2tcj8m6Yu1QDV9YgP36+cqE3cUvgkARBzgUiuI/Q== dependencies: "@types/react" "*" immutable "~3.7.4" @@ -2683,9 +2692,9 @@ "@types/json-schema" "*" "@types/estree@*": - version "0.0.50" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" - integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== + version "0.0.51" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== "@types/estree@0.0.39": version "0.0.39" @@ -2809,14 +2818,14 @@ integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node@*": - version "17.0.14" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.14.tgz#33b9b94f789a8fedd30a68efdbca4dbb06b61f20" - integrity sha512-SbjLmERksKOGzWzPNuW7fJM7fk3YXVTFiZWB/Hs99gwhk+/dnrQRPBQjPW9aO+fi1tAffi9PrwFvsmOKmDTyng== + version "17.0.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.18.tgz#3b4fed5cfb58010e3a2be4b6e74615e4847f1074" + integrity sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA== "@types/node@^12.20.23": - version "12.20.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.43.tgz#6cf47894da4a4748c62fccf720ba269e1b1ff5a4" - integrity sha512-HCfJdaYqJX3BCzeihgZrD7b85Cu05OC/GVJ4kEYIflwUs4jbnUlLLWoq7hw1LBcdvUyehO+gr6P5JQ895/2ZfA== + version "12.20.46" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.46.tgz#7e49dee4c54fd19584e6a9e0da5f3dc2e9136bc7" + integrity sha512-cPjLXj8d6anFPzFvOPxS3fvly3Shm5nTfl6g8X5smexixbuGUf7hfr21J5tX9JW+UPStp/5P5R8qrKL5IyVJ+A== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2829,9 +2838,9 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.0.0": - version "2.4.3" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.3.tgz#a3c65525b91fca7da00ab1a3ac2b5a2a4afbffbf" - integrity sha512-QzSuZMBuG5u8HqYz01qtMdg/Jfctlnvj1z/lYnIDXs/golxw0fxtRAHd9KrzjR7Yxz1qVeI00o0kiO3PmVdJ9w== + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" + integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== "@types/prop-types@*": version "15.7.4" @@ -2899,18 +2908,18 @@ "@types/react" "*" "@types/react@*": - version "17.0.38" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.38.tgz#f24249fefd89357d5fa71f739a686b8d7c7202bd" - integrity sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ== + version "17.0.39" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.39.tgz#d0f4cde092502a6db00a1cded6e6bf2abb7633ce" + integrity sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" csstype "^3.0.2" "@types/react@^16", "@types/react@^16.9.0": - version "16.14.22" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.22.tgz#ee332c031561fa6c5b7fa83d74defce837a2947b" - integrity sha512-4NnkxKDd2UO9SiCckuhCQOCzdO+RtE4Epf1D6eGz3f9jZjiIXOVo+Bk3jqSad+8EOT+LBXwKdkFX0V0F+NFzDQ== + version "16.14.23" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.23.tgz#37201b9f2324c5ff8fa4600dbf19079dfdffc880" + integrity sha512-WngBZLuSkP4IAgPi0HOsGCHo6dn3CcuLQnCfC17VbA7YBgipZiZoTOhObwl/93DsFW0Y2a/ZXeonpW4DxirEJg== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -3483,9 +3492,9 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: uri-js "^4.2.2" ajv@^8.0.1: - version "8.9.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.9.0.tgz#738019146638824dea25edcf299dcba1b0e7eb18" - integrity sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ== + version "8.10.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.10.0.tgz#e573f719bd3af069017e3b66538ab968d040e54d" + integrity sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -3573,10 +3582,10 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -antd@*, antd@^4.16.5: - version "4.18.5" - resolved "https://registry.yarnpkg.com/antd/-/antd-4.18.5.tgz#e5ffbe238fd6fdfcd1ed39ba96e4b1bd5f589757" - integrity sha512-5fN3C2lWAzonhOYYlNpzIw2OHl7vxFZ+4cJ7DK/XZrV+75OY61Y+OkanqMJwrFtDDamIez35OM7cAezGko9tew== +antd@*, antd@^4.18.3: + version "4.18.7" + resolved "https://registry.yarnpkg.com/antd/-/antd-4.18.7.tgz#7355953a6c948b9353fe0f24681d0e1e2ca92781" + integrity sha512-OJsrZOPy4+fEbIVoUFLXQ9quLthkOjQD+AGwIey3nC5+4hebloImbGqqwQ1/ypSFDxou8NtyZ2HCTfPP5WaO4g== dependencies: "@ant-design/colors" "^6.0.0" "@ant-design/icons" "^4.7.0" @@ -3593,7 +3602,7 @@ antd@*, antd@^4.16.5: rc-collapse "~3.1.0" rc-dialog "~8.6.0" rc-drawer "~4.4.2" - rc-dropdown "~3.2.0" + rc-dropdown "~3.2.5" rc-field-form "~1.22.0-2" rc-image "~5.2.5" rc-input-number "~7.3.0" @@ -3949,9 +3958,9 @@ aws4@^1.8.0: integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== axe-core@^4.0.2, axe-core@^4.3.5: - version "4.4.0" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.0.tgz#f93be7f81017eb8bedeb1859cc8092cc918d2dc8" - integrity sha512-btWy2rze3NnxSSxb7LtNhPYYFrRoFBfjiGzmSc/5Hu47wApO2KNXjP/w7Nv2Uz/Fyr/pfEiwOkcXhDxu0jz5FA== + version "4.4.1" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.1.tgz#7dbdc25989298f9ad006645cd396782443757413" + integrity sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw== axobject-query@^2.2.0: version "2.2.0" @@ -5434,9 +5443,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001286: - version "1.0.30001306" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001306.tgz#582592afe243bad2223081b8abab07bf289cc699" - integrity sha512-Wd1OuggRzg1rbnM5hv1wXs2VkxJH/AA+LuudlIqvZiCvivF+wJJe2mgBZC8gPMgI7D76PP5CTx8Luvaqc1V6OQ== + version "1.0.30001312" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz#e11eba4b87e24d22697dae05455d5aea28550d5f" + integrity sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ== capture-exit@^2.0.0: version "2.0.0" @@ -6921,9 +6930,9 @@ ejs@^2.6.1: integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== electron-to-chromium@^1.3.47, electron-to-chromium@^1.3.564, electron-to-chromium@^1.4.17: - version "1.4.63" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.63.tgz#866db72d1221fda89419dc22669d03833e11625d" - integrity sha512-e0PX/LRJPFRU4kzJKLvTobxyFdnANCvcoDCe8XcyTqP58nTWIwdsHvXLIl1RkB39X5yaosLaroMASWB0oIsgCA== + version "1.4.71" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.71.tgz#17056914465da0890ce00351a3b946fd4cd51ff6" + integrity sha512-Hk61vXXKRb2cd3znPE9F+2pLWdIOmP7GjiTj45y6L3W/lO+hSnUSUhq+6lEaERWBdZOHbk2s3YV5c9xVl3boVw== elliptic@^6.5.3: version "6.5.4" @@ -7126,9 +7135,9 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" error-stack-parser@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8" - integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ== + version "2.0.7" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.7.tgz#b0c6e2ce27d0495cf78ad98715e0cad1219abb57" + integrity sha512-chLOW0ZGRf4s8raLrDxa5sdkvPec5YdvwbFnqJme4rk0rFajP8mPtrDL1+I+CwrQDCjswDA5sREX7jYQDQs9vA== dependencies: stackframe "^1.1.1" @@ -8052,9 +8061,9 @@ flush-write-stream@^1.0.0: readable-stream "^2.3.6" follow-redirects@^1.0.0: - version "1.14.7" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" - integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== + version "1.14.8" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" + integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== for-each@^0.3.3: version "0.3.3" @@ -8219,9 +8228,9 @@ fsevents@^2.1.2, fsevents@^2.1.3, fsevents@~2.3.2: integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== fullcalendar@^5.1.0: - version "5.10.1" - resolved "https://registry.yarnpkg.com/fullcalendar/-/fullcalendar-5.10.1.tgz#b3fd67ed1698df05d6bd0516d81bbf2f45a872da" - integrity sha512-0jgDxiWRuC36MzAUK3+Equmu4R0+vAPEtttsXLX9GNNDUHEZ5HjcH+dUtWut4vlJtxGJgVZ+eZ76/7qhcu+RMA== + version "5.10.2" + resolved "https://registry.yarnpkg.com/fullcalendar/-/fullcalendar-5.10.2.tgz#182cdfb285b8a36da652bd990e43655469304312" + integrity sha512-a5qS3RaR5kRZyl5lVGJaPMQorY5hyuJvVONFwnQzNSqL32HZ9COq9ROuoaa/jSGcYqv57T7eRIvIgnu80qs7Nw== function-bind@^1.1.1: version "1.1.1" @@ -9679,9 +9688,9 @@ istanbul-lib-source-maps@^4.0.0: source-map "^0.6.1" istanbul-reports@^3.0.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.3.tgz#4bcae3103b94518117930d51283690960b50d3c2" - integrity sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg== + version "3.1.4" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" + integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" @@ -10229,9 +10238,9 @@ jest-worker@^26.2.1, jest-worker@^26.5.0, jest-worker@^26.6.2: supports-color "^7.0.0" jest-worker@^27.3.1: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.4.6.tgz#5d2d93db419566cb680752ca0792780e71b3273e" - integrity sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw== + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== dependencies: "@types/node" "*" merge-stream "^2.0.0" @@ -11197,13 +11206,27 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -minimatch@3.0.4, minimatch@^3.0.4, minimatch@~3.0.2: +minimatch@3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" +minimatch@^3.0.4: + version "3.1.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.1.tgz#879ad447200773912898b46cd516a7abbb5e50b0" + integrity sha512-reLxBcKUPNBnc/sVtAbxgRVFSegoGeLaSjmphNhcwcolhYLRgtJscn5mRl6YRZNQv40Y7P6JM2YhSIsbL9OB5A== + dependencies: + brace-expansion "^1.1.7" + +minimatch@~3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.7.tgz#e78aeb8dceccb0d12b57a75872da43bc68e7d7ca" + integrity sha512-pYjbG0o9W2Wb3KVBuV6s7R/bzS/iS3HPiHcFcDee5GGiN1M5MErXqgS4jGn8pwVwTZAoy7B8bYb/+AqQU0NhZA== + dependencies: + brace-expansion "^1.1.7" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -11219,9 +11242,9 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== minimongo@^6.10.0: - version "6.11.5" - resolved "https://registry.yarnpkg.com/minimongo/-/minimongo-6.11.5.tgz#bc6013215d3eea9302e888945fcc1f33ec19d69f" - integrity sha512-TA/VBZ+bjcty0QULLJV8YIX9QPgbOoZuZVYkSa1HvMTXTZ+yyECVGTYPNBKbYexd6oBL+8sGEtXQGA12dJhhsA== + version "6.11.6" + resolved "https://registry.yarnpkg.com/minimongo/-/minimongo-6.11.6.tgz#ae660043fd6f9ba6b759b30555277a1563161477" + integrity sha512-4bgL5uLkKnyUU1/uSYmUVvZuzSgOTZGTNQCfM+wlcv/zUtUZ/w9CeZqvfuWo4qO01QuNutkqESNso+Yej237Lw== dependencies: "@turf/boolean-crosses" "^6.0.1" "@turf/boolean-point-in-polygon" "^6.0.1" @@ -11377,9 +11400,9 @@ nan@^2.12.1, nan@^2.13.2: integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== nanoid@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" - integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== + version "3.3.0" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.0.tgz#5906f776fd886c66c24f3653e0c46fcb1d4ad6b0" + integrity sha512-JzxqqT5u/x+/KOFSd7JP15DOo9nOoHpx6DYatqIHUW2+flybkm+mdcraotSQR5WcnZr+qhGVh8Ted0KdfSMxlg== nanomatch@^1.2.9: version "1.2.13" @@ -11537,9 +11560,9 @@ node-releases@^1.1.61: integrity sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ== node-releases@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" - integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" + integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== node-sass@^6.0.1: version "6.0.1" @@ -13104,9 +13127,9 @@ pretty-format@^26.6.0, pretty-format@^26.6.2: react-is "^17.0.1" pretty-format@^27.0.2: - version "27.4.6" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.4.6.tgz#1b784d2f53c68db31797b2348fa39b49e31846b7" - integrity sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g== + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== dependencies: ansi-regex "^5.0.1" ansi-styles "^5.0.0" @@ -13402,9 +13425,9 @@ rc-align@^4.0.0: resize-observer-polyfill "^1.5.1" rc-cascader@~3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.2.1.tgz#fc928d67d96c3d9f358263e4a9127bcf4257cc6b" - integrity sha512-Raxam9tFzBL4TCgHoyVcf7+Q2KSFneUk3FZXi9w1tfxEihLlezSH0oCNMjHJN8hxWwwx9ZbI9UzWTfFImjXc0Q== + version "3.2.6" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.2.6.tgz#499cf7f65625569eff6dc3854612298de4f24093" + integrity sha512-3CmlJP7jPVlP5jT+O3PrP8E9yxees48Na7Hiir84ktcw11pUUU5YawAhuRoSc09SGVvRcP70a9gCu94Hqp3ZwA== dependencies: "@babel/runtime" "^7.12.5" array-tree-filter "^2.1.0" @@ -13452,15 +13475,15 @@ rc-drawer@~4.4.2: rc-util "^5.7.0" rc-dropdown@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.3.0.tgz#3bd47a8c7d2f81f03d477b6761d4122b78811688" - integrity sha512-UvNfeqpaNFbCyMUN4m0r+EPqXQgcwVSA4R4Gt4cs/MzIAIK0X6v0WNfwzlmvrwAY8lzY+zn9qakhehHnD87NCA== + version "3.3.2" + resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.3.2.tgz#097c2ec1b6d55c10eeb94dcf6120ba034c7a58e0" + integrity sha512-49GOz42oNvLtYGoJ2X5UWXJFp7aUiSZkj9OcgTV1UpxFZqHQMw+xijkaL5k3XDkMbb92XsuFnFt7IGG3/C0DKw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" rc-trigger "^5.0.4" -rc-dropdown@~3.2.0: +rc-dropdown@~3.2.5: version "3.2.5" resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.2.5.tgz#c211e571d29d15e7f725b5a75fc8c7f371fc3348" integrity sha512-dVO2eulOSbEf+F4OyhCY5iGiMVhUYY/qeXxL7Ex2jDBt/xc89jU07mNoowV6aWxwVOc70pxEINff0oM2ogjluA== @@ -13536,13 +13559,13 @@ rc-menu@~9.2.1: shallowequal "^1.1.0" rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.2.0, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4: - version "2.4.4" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.4.4.tgz#e995d5fa24fc93065c24f714857cf2677d655bb0" - integrity sha512-ms7n1+/TZQBS0Ydd2Q5P4+wJTSOrhIrwNxLXCZpR7Fa3/oac7Yi803HDALc2hLAKaCTQtw9LmQeB58zcwOsqlQ== + version "2.4.5" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.4.5.tgz#b061c50bb29ecd3d735d5f4c40924a3c78226cbd" + integrity sha512-f3uJHR4gcpeZS/s8/nYFSOrXt2Wu/h9GrEcbJmC0qmKrVNgwL1pTgrT5kW7lgG6PFeoL4yHDmpQoEKkrPtKIzQ== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" - rc-util "^5.2.1" + rc-util "^5.18.1" rc-notification@~4.5.7: version "4.5.7" @@ -13555,14 +13578,14 @@ rc-notification@~4.5.7: rc-util "^5.0.1" rc-overflow@^1.0.0, rc-overflow@^1.2.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.2.2.tgz#95b0222016c0cdbdc0db85f569c262e7706a5f22" - integrity sha512-X5kj9LDU1ue5wHkqvCprJWLKC+ZLs3p4He/oxjZ1Q4NKaqKBaYf5OdSzRSgh3WH8kSdrfU8LjvlbWnHgJOEkNQ== + version "1.2.3" + resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.2.3.tgz#1754216d807f5473304272b0321c3aba7615f47a" + integrity sha512-Bz6dXTn/ww8nmu70tUQfRV0wT3BkfXY6j1lB1O38OVkDPz4xwfAcGK+LJ2zewUR5cTXkJ8hAN7YULohG8z4M7Q== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-resize-observer "^1.0.0" - rc-util "^5.5.1" + rc-util "^5.15.0" rc-pagination@~3.1.9: version "3.1.15" @@ -13615,9 +13638,9 @@ rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.2.0: resize-observer-polyfill "^1.5.1" rc-select@~14.0.0-alpha.15, rc-select@~14.0.0-alpha.23, rc-select@~14.0.0-alpha.8: - version "14.0.0-alpha.25" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.0.0-alpha.25.tgz#9e6ca83b090e020a730fdfdab07c1050549426e4" - integrity sha512-U9AMzXsOCCdtn96YIZdUrYbxk+5u6uWUCaYH2129X3FTjQITqAjEPYHfPcxU/G7+lwiD0pIaU95W0NMkg+26qw== + version "14.0.0-alpha.26" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.0.0-alpha.26.tgz#51ae0aee882d3a729648f86fe99fe7d0006d9bdb" + integrity sha512-5+vpP+qkYg9TiQb06BIVMTdnKwjXW/4ud8NWaCtnLGsyeqN6Hg7HGTUwlTTOyNOU5zMjbKHrAIvMk8NipGKqtA== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -13699,9 +13722,9 @@ rc-tooltip@^5.0.1, rc-tooltip@~5.1.1: rc-trigger "^5.0.0" rc-tree-select@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.1.2.tgz#0dfe12f01b9b0dc9ce658b96501692d4421b8366" - integrity sha512-sTulyQZB1SgF2HzAR49i4vjMNvV/tfPxCTc+qNuWOwaAtSm2bwGH6/wfi3k3Dw2/5PPOGpRRpgCMltoP9aG0+w== + version "5.1.3" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.1.3.tgz#492f76adec6b4f69beedb0ad59595cd79f671d62" + integrity sha512-nfOhsUM3SiEo/Kt+LhinC3LI3VJGCU4+TCRBAmdt0frV3Ix9GAoC3aIaHIUs2tkDf3X0qOmf6qYcyUn/RaIuoQ== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -13740,10 +13763,10 @@ rc-upload@~4.3.0: classnames "^2.2.5" rc-util "^5.2.0" -rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.0.7, rc-util@^5.12.0, rc-util@^5.14.0, rc-util@^5.15.0, rc-util@^5.16.1, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.3.0, rc-util@^5.4.0, rc-util@^5.5.0, rc-util@^5.5.1, rc-util@^5.6.1, rc-util@^5.7.0, rc-util@^5.8.0, rc-util@^5.9.4, rc-util@^5.9.8: - version "5.17.0" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.17.0.tgz#6b0788038075c3d5c215541539573a4a03827070" - integrity sha512-HWuTIKzBeZQQ7IBqdokE0wMp/xx39/KfUJ0gcquBigoldDCrf3YBcWFHrrQlJG7sI82Wg8mwp1uAKV3zMGfAgg== +rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.0.7, rc-util@^5.12.0, rc-util@^5.14.0, rc-util@^5.15.0, rc-util@^5.16.1, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.3.0, rc-util@^5.4.0, rc-util@^5.5.0, rc-util@^5.6.1, rc-util@^5.7.0, rc-util@^5.8.0, rc-util@^5.9.4, rc-util@^5.9.8: + version "5.18.1" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.18.1.tgz#80bd1450b5254655d2fbea63e3d34f6871e9be79" + integrity sha512-24xaSrMZUEKh1+suDOtJWfPe9E6YrwryViZcoPO0miJTKzP4qhUlV5AAlKQ82AJilz/AOHfi3l6HoX8qa1ye8w== dependencies: "@babel/runtime" "^7.12.5" react-is "^16.12.0" @@ -13886,10 +13909,10 @@ react-google-login@^5.1.1: "@types/react" "*" prop-types "^15.6.0" -react-i18next@^11.11.1: - version "11.15.3" - resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.15.3.tgz#7608fb3cacc02ac75a62fc2d68b579f140b198dd" - integrity sha512-RSUEM4So3Tu2JHV0JsZ5Yje+4nz66YViMfPZoywxOy0xyn3L7tE2CHvJ7Y9LUsrTU7vGmZ5bwb8PpjnkatdIxg== +react-i18next@^11.15.3: + version "11.15.4" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.15.4.tgz#fe0c792ea93f038548e838cecae3ed4173822937" + integrity sha512-jKJNAcVcbPGK+yrTcXhLblgPY16n6NbpZZL3Mk8nswj1v3ayIiUBVDU09SgqnT+DluyQBS97hwSvPU5yVFG0yg== dependencies: "@babel/runtime" "^7.14.5" html-escaper "^2.0.2" @@ -15180,9 +15203,9 @@ side-channel@^1.0.4: object-inspect "^1.9.0" signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.6" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" - integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== simple-swizzle@^0.2.2: version "0.2.2" @@ -15367,7 +15390,7 @@ source-map@^0.7.3, source-map@~0.7.2: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== -sourcemap-codec@1.4.8, sourcemap-codec@^1.4.4: +sourcemap-codec@^1.4.4: version "1.4.8" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== @@ -15487,9 +15510,9 @@ stack-utils@^2.0.2: escape-string-regexp "^2.0.0" stackframe@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303" - integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA== + version "1.2.1" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.1.tgz#1033a3473ee67f08e2f2fc8eba6aef4f845124e1" + integrity sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg== static-extend@^0.1.1: version "0.1.2" @@ -16427,9 +16450,9 @@ url-parse@1.4.4: requires-port "^1.0.0" url-parse@^1.4.3, url-parse@^1.4.5, url-parse@^1.5.3: - version "1.5.4" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.4.tgz#e4f645a7e2a0852cc8a66b14b292a3e9a11a97fd" - integrity sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg== + version "1.5.6" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.6.tgz#b2a41d5a233645f3c31204cc8be60e76a15230a2" + integrity sha512-xj3QdUJ1DttD1LeSfvJlU1eiF1RvBSBfUu8GplFGdUzSO28y5yUtEl7wb//PI4Af6qh0o/K8545vUmucRrfWsw== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" @@ -17243,9 +17266,9 @@ ws@^6.1.2, ws@^6.2.1: async-limiter "~1.0.0" ws@^7.4.6: - version "7.5.6" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" - integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== + version "7.5.7" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" + integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== ws@~7.4.2: version "7.4.6"