From 75ccf1064bf3e362f497a2c815ae121a92878863 Mon Sep 17 00:00:00 2001 From: Romaric Mourgues Date: Thu, 21 Apr 2022 17:04:56 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A0=20=20Before=20go=20to=20production?= =?UTF-8?q?=2021/04=20fixes=20(#2105)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix securirty issue * Fix ensure badge is reachable * Fix missing user name in mentions * Fix channels bar load time and bold channels * Fix search for start direct chat with user * Fix #2083 --- .../src/Twake/Drive/Services/DrivePreview.php | 1 - .../notifications/services/badges/service.ts | 19 +++++++++++---- .../twacode/pseudo-markdown-dictionary.tsx | 2 +- .../src/app/components/ui/user-or-mail.tsx | 12 +++++++++- .../user-list-manager/user-list-manager.tsx | 23 ++++++++++++------- .../src/app/deprecated/user/CurrentUser.ts | 3 ++- .../features/messages/hooks/use-message.ts | 4 +--- .../users/hooks/use-search-user-list.ts | 10 +++++--- .../app/features/users/hooks/use-user-list.ts | 5 +++- .../users/services/current-user-service.ts | 14 ++++------- .../app/features/users/state/atoms/user.ts | 18 --------------- .../messages/message/parts/Reactions.tsx | 3 ++- .../ChannelsUser/ChannelsUser.tsx | 17 ++++++++++++-- .../Parts/Channel/ChannelIntermediate.tsx | 4 +++- .../Pages/Workspace/DeleteWorkspacePopup.tsx | 2 +- 15 files changed, 81 insertions(+), 56 deletions(-) delete mode 100644 twake/frontend/src/app/features/users/state/atoms/user.ts diff --git a/twake/backend/core/src/Twake/Drive/Services/DrivePreview.php b/twake/backend/core/src/Twake/Drive/Services/DrivePreview.php index c0377d8193..7113004810 100755 --- a/twake/backend/core/src/Twake/Drive/Services/DrivePreview.php +++ b/twake/backend/core/src/Twake/Drive/Services/DrivePreview.php @@ -45,7 +45,6 @@ public function generatePreview($filename, $file, $path, $ext, $entity = null) $filetype === 'image/gif' || $filetype === 'image/x-icon' || $filetype === 'image/jpeg' || - $filetype === 'image/svg+xml' || $filetype === 'image/tiff' || $filetype === 'image/webp' || $this->isImage($ext)) { diff --git a/twake/backend/node/src/services/notifications/services/badges/service.ts b/twake/backend/node/src/services/notifications/services/badges/service.ts index 3407c39ba1..7c75876227 100644 --- a/twake/backend/node/src/services/notifications/services/badges/service.ts +++ b/twake/backend/node/src/services/notifications/services/badges/service.ts @@ -162,7 +162,11 @@ export class UserNotificationBadgeService implements UserNotificationBadgeServic user: { id: channelMemberPk.user_id, server_request: true }, channel: { id: channelId, ...channelMemberPk }, }; - const exists = await this.channelsService.members.get(channelMemberPk, context); + const exists = + (await this.channelsService.channels.get({ + id: channelId, + ..._.pick(channelMemberPk, "company_id", "workspace_id"), + })) && (await this.channelsService.members.get(channelMemberPk, context)); if (!exists) { for (const badge of badges.getEntities()) { if (badge.channel_id === channelId) this.removeUserChannelBadges(badge); @@ -179,10 +183,15 @@ export class UserNotificationBadgeService implements UserNotificationBadgeServic continue; } try { - const exists = await this.userService.workspaces.getUser({ - workspaceId, - userId, - }); + const exists = + (await this.userService.workspaces.get({ + id: workspaceId, + company_id: companyId, + })) && + (await this.userService.workspaces.getUser({ + workspaceId, + userId, + })); if (!exists) { await this.channelsService.members.ensureUserNotInWorkspaceIsNotInChannel( { id: userId }, diff --git a/twake/frontend/src/app/components/twacode/pseudo-markdown-dictionary.tsx b/twake/frontend/src/app/components/twacode/pseudo-markdown-dictionary.tsx index 759d551213..75c0eee21e 100644 --- a/twake/frontend/src/app/components/twacode/pseudo-markdown-dictionary.tsx +++ b/twake/frontend/src/app/components/twacode/pseudo-markdown-dictionary.tsx @@ -215,7 +215,7 @@ export const DynamicComponent = ({ (data.full_width ? 'full_width ' : '') } defaultValue={data.content || ''} - placeholder={data.placeholder} + placeholder={data.placeholder || 'Write something...'} onChange={(evt: any) => { eventContainer.onAction( 'interactive_change', diff --git a/twake/frontend/src/app/components/ui/user-or-mail.tsx b/twake/frontend/src/app/components/ui/user-or-mail.tsx index d985d169a7..82fa5fc2df 100755 --- a/twake/frontend/src/app/components/ui/user-or-mail.tsx +++ b/twake/frontend/src/app/components/ui/user-or-mail.tsx @@ -27,12 +27,22 @@ const EmailRow = ({ email }: { email: string }): JSX.Element => { const UserRow = ({ id }: { id: string }): JSX.Element => { const user = useUser(id); + if (user) { + console.log(UsersService.getFullName(user)); + } else { + console.log('user not found'); + } + return user ? ( <> - + {UsersService.getFullName(user)} {user.email ? `, ${user.email}` : ''} diff --git a/twake/frontend/src/app/components/user-list-manager/user-list-manager.tsx b/twake/frontend/src/app/components/user-list-manager/user-list-manager.tsx index 0d11035006..5f844b9bd4 100755 --- a/twake/frontend/src/app/components/user-list-manager/user-list-manager.tsx +++ b/twake/frontend/src/app/components/user-list-manager/user-list-manager.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { Button, Col, Row, Typography } from 'antd'; import classNames from 'classnames'; import TrashIcon from '@material-ui/icons/DeleteOutlined'; @@ -30,6 +30,7 @@ const UserListManager = (props: PropsType) => { const [input, setInput] = useState(''); const [editing, setEditing] = useState(props.autoFocus ? props.autoFocus : false); const [usersIds, setUsersIds] = useState([...props.users]); + const callback = useRef(() => {}); let savedUserProps: string; useEffect(() => { @@ -37,6 +38,10 @@ const UserListManager = (props: PropsType) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useEffect(() => { + callback.current(result.map(u => u.id)); + }, [result]); + const updateStateFromProps = (props: PropsType, force?: boolean) => { let anti_duplicates: string[] = []; @@ -54,7 +59,7 @@ const UserListManager = (props: PropsType) => { } }; - const filter = (text: string, callback: (arr: any[]) => any) => { + const filter = (text: string, cb: (arr: any[]) => any) => { setInput(text); if ((text || '').indexOf('@') > 0) { if ( @@ -62,15 +67,17 @@ const UserListManager = (props: PropsType) => { Strings.verifyMail(text) && usersIds.indexOf(text.toLocaleLowerCase()) < 0 ) { - callback([{ email: text.toLocaleLowerCase() }]); + cb([{ email: text.toLocaleLowerCase() }]); return; } - callback([]); + cb([]); return; } - search(text); - callback([...result.map(u => u.id)]); + const tmp = search(text); + cb([...tmp.map(u => u.id)]); + + callback.current = cb; }; const renderLine = (item: any, added?: boolean): JSX.Element => { @@ -139,7 +146,7 @@ const UserListManager = (props: PropsType) => {
{usersIds.map((item: string, index: number) => (
- + {renderLine(item, true)}
@@ -182,7 +189,7 @@ const UserListManager = (props: PropsType) => { } render={(user: UserType) => ( }> - + diff --git a/twake/frontend/src/app/deprecated/user/CurrentUser.ts b/twake/frontend/src/app/deprecated/user/CurrentUser.ts index b526c6c40a..e2ab53fb39 100755 --- a/twake/frontend/src/app/deprecated/user/CurrentUser.ts +++ b/twake/frontend/src/app/deprecated/user/CurrentUser.ts @@ -13,6 +13,7 @@ import JWTStorage from 'app/features/auth/jwt-storage-service'; import Globals from 'app/features/global/services/globals-twake-app-service'; import { useCurrentUser } from 'app/features/users/hooks/use-current-user'; import UserAPIClient from '../../features/users/api/user-api-client'; +import { getUser } from 'app/features/users/hooks/use-user-list'; class CurrentUser extends Observable { loading: boolean; @@ -48,7 +49,7 @@ class CurrentUser extends Observable { start() {} get() { - return Collections.get('users').find(Login.currentUserId); + return getUser(Login.currentUserId); } updateUserStatus = (newStatus: string[]) => { diff --git a/twake/frontend/src/app/features/messages/hooks/use-message.ts b/twake/frontend/src/app/features/messages/hooks/use-message.ts index 1516fcae8c..c75b7a268b 100644 --- a/twake/frontend/src/app/features/messages/hooks/use-message.ts +++ b/twake/frontend/src/app/features/messages/hooks/use-message.ts @@ -5,9 +5,7 @@ import { useRecoilCallback, useRecoilState } from 'recoil'; import { AtomMessageKey, MessageState } from '../state/atoms/messages'; import { NodeMessage, NodeMessageSubType, ReactionType } from 'app/features/messages/types/message'; import { messageToMessageWithReplies } from '../utils/message-with-replies'; -import { UserState } from 'app/features/users/state/atoms/user'; -import { setUserList, useSetUserList } from 'app/features/users/hooks/use-user-list'; -import { UserListState } from 'app/features/users/state/atoms/user-list'; +import { useSetUserList } from 'app/features/users/hooks/use-user-list'; export const useMessage = (partialKey: AtomMessageKey) => { const key = { diff --git a/twake/frontend/src/app/features/users/hooks/use-search-user-list.ts b/twake/frontend/src/app/features/users/hooks/use-search-user-list.ts index de6740f6c6..740daa36c6 100644 --- a/twake/frontend/src/app/features/users/hooks/use-search-user-list.ts +++ b/twake/frontend/src/app/features/users/hooks/use-search-user-list.ts @@ -7,6 +7,7 @@ import UserAPIClient, { SearchContextType } from '../api/user-api-client'; import { delayRequest } from 'app/features/global/utils/managedSearchRequest'; import Strings from 'app/features/global/utils/strings'; import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace'; +import _ from 'lodash'; export const searchBackend = async ( query: string | undefined, @@ -102,16 +103,19 @@ export const useSearchUserList = ({ }: { scope: SearchContextType['scope']; }): { - search: (str?: string) => void; + search: (str?: string) => UserType[]; result: UserType[]; } => { const { set: setUserList } = useSetUserList('use-search-user-list'); const [query, setQuery] = useState(); - const { userList } = useUserList(); + let { userList } = useUserList(); + userList = _.uniqBy(userList, 'id'); + const companyId = useRouterCompany(); const workspaceId = useRouterWorkspace(); - const search = async (str?: string) => { + const search = (str?: string) => { setQuery(str); + return searchFrontend(str, { workspaceId, scope, companyId, userList: userList || [] }); }; useEffect(() => { diff --git a/twake/frontend/src/app/features/users/hooks/use-user-list.ts b/twake/frontend/src/app/features/users/hooks/use-user-list.ts index cb1f64af83..2b6a0472d5 100644 --- a/twake/frontend/src/app/features/users/hooks/use-user-list.ts +++ b/twake/frontend/src/app/features/users/hooks/use-user-list.ts @@ -1,6 +1,6 @@ import { useEffect } from 'react'; import { cloneDeep, concat, isEqual, uniqBy } from 'lodash'; -import { RecoilState, useRecoilCallback, useRecoilValueLoadable } from 'recoil'; +import { useRecoilCallback, useRecoilValueLoadable } from 'recoil'; import useRouterCompany from 'app/features/router/hooks/use-router-company'; import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace'; @@ -8,6 +8,8 @@ import { UserCompanyType, UserType, UserWorkspaceType } from 'app/features/users import WorkspaceUserAPIClient from 'app/features/workspace-members/api/workspace-members-api-client'; import { UserListState } from '../state/atoms/user-list'; import Logger from 'app/features/global/framework/logger-service'; +import Collections from 'app/deprecated/CollectionsV1/Collections/Collections'; +import _ from 'lodash'; export const usePreloadSomeUsers = () => { const companyId = useRouterCompany(); @@ -84,6 +86,7 @@ export function useSetUserList(key: string) { if (currentList && newList && !isEqual(currentList, newList)) { set(UserListState, newList); + newList.forEach(user => Collections.get('users').completeObject(_.cloneDeep(user))); currentUserList = newList; } } diff --git a/twake/frontend/src/app/features/users/services/current-user-service.ts b/twake/frontend/src/app/features/users/services/current-user-service.ts index ffde1fb524..17e6292aa1 100755 --- a/twake/frontend/src/app/features/users/services/current-user-service.ts +++ b/twake/frontend/src/app/features/users/services/current-user-service.ts @@ -5,6 +5,7 @@ import Languages from 'app/features/global/services/languages-service'; import { UserType } from 'app/features/users/types/user'; import { TwakeService } from 'app/features/global/framework/registry-decorator-service'; import { addApiUrlIfNeeded, getAsFrontUrl } from 'app/features/global/utils/URLUtils'; +import { getUser } from '../hooks/use-user-list'; type SearchQueryType = { searching: boolean; @@ -29,7 +30,7 @@ class User { } getCurrentUser(): UserType & { id: string } { - return Collections.get('users').find(Login.currentUserId); + return getUser(Login.currentUserId) as UserType & { id: string }; } getCurrentUserId(): string { @@ -45,14 +46,9 @@ class User { if (user.deleted) { name = Languages.t('general.user.deleted'); - } - - if (user.first_name?.length) { - name = user.first_name; - } - - if (user.first_name?.length && user.last_name?.length) { - name = `${user.first_name} ${user.last_name}`; + } else { + name = [user.first_name, user.last_name].filter(a => a).join(' '); + name = name || user.username; } return name.charAt(0).toUpperCase() + name.slice(1); diff --git a/twake/frontend/src/app/features/users/state/atoms/user.ts b/twake/frontend/src/app/features/users/state/atoms/user.ts deleted file mode 100644 index 25f4a1dfd9..0000000000 --- a/twake/frontend/src/app/features/users/state/atoms/user.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { atomFamily, RecoilState, useRecoilCallback } from 'recoil'; - -import UserAPIClient from 'app/features/users/api/user-api-client'; -import { UserType } from 'app/features/users/types/user'; -import Collections from 'app/deprecated/CollectionsV1/Collections/Collections'; -import _ from 'lodash'; - -export const UserState = atomFamily({ - key: 'UserState', - default: undefined, - - //Retro-compatibility - effects_UNSTABLE: () => [ - ({ onSet }) => { - onSet(user => Collections.get('users').updateObject(_.cloneDeep(user))); - }, - ], -}); diff --git a/twake/frontend/src/app/views/applications/messages/message/parts/Reactions.tsx b/twake/frontend/src/app/views/applications/messages/message/parts/Reactions.tsx index 3dd8c591f2..ce30de929e 100644 --- a/twake/frontend/src/app/views/applications/messages/message/parts/Reactions.tsx +++ b/twake/frontend/src/app/views/applications/messages/message/parts/Reactions.tsx @@ -9,6 +9,7 @@ import classNames from 'classnames'; import { MessageContext } from '../message-with-replies'; import { useMessage } from 'app/features/messages/hooks/use-message'; import _ from 'lodash'; +import { getUser } from 'app/features/users/hooks/use-user-list'; export default () => { const context = useContext(MessageContext); @@ -64,7 +65,7 @@ const Reaction = ({ const ReactionTooltip = ({ users }: { users: ReactionType['users'] }): JSX.Element => ( <> {users.map(id => { - const user = Collections.get('users').find(id); + const user = getUser(id); if (!user) return <>; diff --git a/twake/frontend/src/app/views/client/channels-bar/ChannelsUser/ChannelsUser.tsx b/twake/frontend/src/app/views/client/channels-bar/ChannelsUser/ChannelsUser.tsx index 68dabc7d92..8ceb599c23 100755 --- a/twake/frontend/src/app/views/client/channels-bar/ChannelsUser/ChannelsUser.tsx +++ b/twake/frontend/src/app/views/client/channels-bar/ChannelsUser/ChannelsUser.tsx @@ -17,6 +17,7 @@ import { useDirectChannels } from 'app/features/channels/hooks/use-direct-channe export default () => { const { companyId } = RouterServices.getStateFromRoute(); let { directChannels } = useDirectChannels(); + let [max, setMax] = useState(20); const openConv = () => { return MediumPopupComponent.open(, { @@ -36,6 +37,10 @@ export default () => { return <>; } + const nonfavoriteDirectChannels = directChannels.filter( + channel => !channel.user_member?.favorite, + ); + return (
{ )} onAdd={AccessRightsService.hasCompanyLevel(companyId, 'member') ? () => openConv() : null} /> - {directChannels - .filter(channel => !channel.user_member?.favorite) + {nonfavoriteDirectChannels .sort( (a, b) => (parseInt(b.last_activity?.toString() || '') || 0) - (parseInt(a.last_activity?.toString() || '') || 0), ) + .slice(0, max) .map(channel => { return ; })} + {max < nonfavoriteDirectChannels.length && ( + + )} + {directChannels.length === 0 && (
{Languages.t( diff --git a/twake/frontend/src/app/views/client/channels-bar/Parts/Channel/ChannelIntermediate.tsx b/twake/frontend/src/app/views/client/channels-bar/Parts/Channel/ChannelIntermediate.tsx index c41eca3bd4..35064517f7 100644 --- a/twake/frontend/src/app/views/client/channels-bar/Parts/Channel/ChannelIntermediate.tsx +++ b/twake/frontend/src/app/views/client/channels-bar/Parts/Channel/ChannelIntermediate.tsx @@ -36,8 +36,10 @@ export default (props: Props): JSX.Element => { : { avatar: '', name: '' }; const unreadMessages = + (channel.last_activity || 0) !== 0 && (channel.last_activity || 0) > (channel?.user_member?.last_access || 0) && - channel.last_message?.sender !== channel.user_member?.user_id; + channel.last_message?.sender !== channel.user_member?.user_id && + !(isDirectChannel && notifications.length === 0); const channelIcon = isDirectChannel ? avatar : channel.icon || ''; const channelName = isDirectChannel ? name : channel.name || ''; diff --git a/twake/frontend/src/app/views/client/popup/WorkspaceParameter/Pages/Workspace/DeleteWorkspacePopup.tsx b/twake/frontend/src/app/views/client/popup/WorkspaceParameter/Pages/Workspace/DeleteWorkspacePopup.tsx index ca923a998b..3fb90c7cbd 100644 --- a/twake/frontend/src/app/views/client/popup/WorkspaceParameter/Pages/Workspace/DeleteWorkspacePopup.tsx +++ b/twake/frontend/src/app/views/client/popup/WorkspaceParameter/Pages/Workspace/DeleteWorkspacePopup.tsx @@ -80,7 +80,7 @@ export default () => { onClick={async () => { setLoading(true); await WorkspaceAPIClient.delete(workspace.company_id, workspace.id); - (window as any).location.reload(); + (window as any).location.replace('/'); setLoading(false); ModalManager.close(); }}