Regression: Fix reactivity on teamsMembers and roomMembers (#21366)

Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com>
pull/21395/head^2
Douglas Fabris 5 years ago committed by GitHub
parent 0bc70e8ff3
commit cb4ae70b17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      client/sidebar/header/actions/CreateRoomList.js
  2. 67
      client/views/hooks/useMembersList.ts
  3. 0
      client/views/room/contextualBar/Info/ChannelToTeamModal/ChannelToTeamModal.js
  4. 6
      client/views/room/contextualBar/Info/ChannelToTeamModal/StepOne.js
  5. 4
      client/views/room/contextualBar/Info/ChannelToTeamModal/StepTwo.js
  6. 4
      client/views/room/contextualBar/Info/ConvertToTeamModal.js
  7. 4
      client/views/room/contextualBar/Info/RoomInfo/RoomInfo.js
  8. 24
      client/views/room/contextualBar/RoomMembers/AddUsers/AddUsers.js
  9. 8
      client/views/room/contextualBar/RoomMembers/InviteUsers/InviteUsers.js
  10. 48
      client/views/room/contextualBar/RoomMembers/List/RoomMembers.js
  11. 18
      client/views/room/contextualBar/RoomMembers/List/components/MemberItem.js
  12. 16
      client/views/room/hooks/useUserInfoActions.js
  13. 14
      client/views/teams/CreateTeamModal/CreateTeamModal.tsx
  14. 0
      client/views/teams/CreateTeamModal/TeamNameInput.tsx
  15. 4
      client/views/teams/CreateTeamModal/UsersInput.tsx
  16. 0
      client/views/teams/CreateTeamModal/index.ts
  17. 10
      client/views/teams/contextualBar/ChannelDesertionTable.js
  18. 2
      client/views/teams/contextualBar/RoomLinkList.js
  19. 6
      client/views/teams/contextualBar/TeamAutocomplete.js
  20. 6
      client/views/teams/contextualBar/info/Delete/ChannelDeletionTable.js
  21. 0
      client/views/teams/contextualBar/info/Delete/DeleteTeamModal.js
  22. 0
      client/views/teams/contextualBar/info/Delete/DeleteTeamModal.stories.js
  23. 4
      client/views/teams/contextualBar/info/Delete/StepOne.js
  24. 6
      client/views/teams/contextualBar/info/Delete/StepThree.js
  25. 4
      client/views/teams/contextualBar/info/Delete/StepTwo.js
  26. 2
      client/views/teams/contextualBar/info/Delete/index.js
  27. 0
      client/views/teams/contextualBar/info/Leave/LeaveTeamModal.js
  28. 0
      client/views/teams/contextualBar/info/Leave/LeaveTeamModal.stories.js
  29. 4
      client/views/teams/contextualBar/info/Leave/StepOne.js
  30. 6
      client/views/teams/contextualBar/info/Leave/StepTwo.js
  31. 4
      client/views/teams/contextualBar/info/Leave/index.js
  32. 32
      client/views/teams/contextualBar/info/TeamsInfo.js
  33. 2
      client/views/teams/contextualBar/info/TeamsInfo.stories.js
  34. 10
      client/views/teams/contextualBar/info/index.js
  35. 2
      client/views/teams/contextualBar/info/tabBar.ts
  36. 17
      client/views/teams/contextualBar/members/RemoveUsersModal/RemoveUsersModal.js
  37. 0
      client/views/teams/contextualBar/members/RemoveUsersModal/RemoveUsersModal.stories.js
  38. 40
      client/views/teams/contextualBar/members/RemoveUsersModal/index.js
  39. 44
      client/views/teams/contextualBar/members/TeamsMembers.js
  40. 0
      client/views/teams/contextualBar/members/index.js
  41. 2
      client/views/teams/contextualBar/members/tabBar.ts
  42. 5
      client/views/teams/index.js
  43. 15
      client/views/teams/info/RoomLinkList.js
  44. 56
      client/views/teams/members/RemoveUsersModal/index.js
  45. 5
      definition/IUser.ts
  46. 1
      server/methods/getUsersOfRoom.js

@ -8,7 +8,7 @@ import { useAtLeastOnePermission, usePermission } from '../../../contexts/Author
import { useSetting } from '../../../contexts/SettingsContext';
import { useSetModal } from '../../../contexts/ModalContext';
import CreateChannel from '../CreateChannel';
import CreateTeamModal from '../../../views/teams/modals/CreateTeamModal';
import CreateTeamModal from '../../../views/teams/CreateTeamModal';
import CreateRoomListItem from './CreateRoomListItem';
const CREATE_CHANNEL_PERMISSIONS = ['create-c', 'create-p'];

@ -0,0 +1,67 @@
import { useCallback, useMemo, useState } from 'react';
import { getConfig } from '../../../app/ui-utils/client/config';
import { IUser } from '../../../definition/IUser';
import { useMethod } from '../../contexts/ServerContext';
import { useScrollableRecordList } from '../../hooks/lists/useScrollableRecordList';
import { useComponentDidUpdate } from '../../hooks/useComponentDidUpdate';
import { RecordList } from '../../lib/lists/RecordList';
type MembersListOptions = {
rid: string;
type: 'all' | 'autoJoin';
limit: number;
debouncedText: string;
}
export const useMembersList = (
options: MembersListOptions,
): {
membersList: RecordList<IUser>;
initialItemCount: number;
reload: () => void;
loadMoreItems: (start: number, end: number) => void;
} => {
const getUsersMethod = useMethod('getUsersOfRoom');
const [membersList, setMembersList] = useState(() => new RecordList<IUser>());
const reload = useCallback(() => setMembersList(new RecordList<IUser>()), []);
useComponentDidUpdate(() => {
options && reload();
}, [options, reload]);
const fetchData = useCallback(
async (start, end) => {
const { records, total } = await getUsersMethod(
options.rid,
options.type,
{
limit: end - start,
skip: start,
},
options.debouncedText,
);
return {
items: records.map((members: any) => {
members._updatedAt = new Date(members._updatedAt);
return members;
}),
itemCount: total,
};
},
[getUsersMethod, options],
);
const { loadMoreItems, initialItemCount } = useScrollableRecordList(membersList, fetchData, useMemo(() => {
const filesListSize = getConfig('teamsChannelListSize');
return filesListSize ? parseInt(filesListSize, 10) : undefined;
}, []));
return {
reload,
membersList,
loadMoreItems,
initialItemCount,
};
};

@ -1,9 +1,9 @@
import React from 'react';
import { Box } from '@rocket.chat/fuselage';
import { useTranslation } from '../../../../contexts/TranslationContext';
import GenericModal from '../../../../components/GenericModal';
import TeamAutocomplete from '../../TeamAutocomplete';
import { useTranslation } from '../../../../../contexts/TranslationContext';
import GenericModal from '../../../../../components/GenericModal';
import TeamAutocomplete from '../../../../teams/contextualBar/TeamAutocomplete';
const StepOne = ({

@ -1,8 +1,8 @@
import React from 'react';
import { Box } from '@rocket.chat/fuselage';
import { useTranslation } from '../../../../contexts/TranslationContext';
import GenericModal from '../../../../components/GenericModal';
import { useTranslation } from '../../../../../contexts/TranslationContext';
import GenericModal from '../../../../../components/GenericModal';
const StepTwo = ({
onClose,

@ -1,7 +1,7 @@
import React from 'react';
import GenericModal from '../../../components/GenericModal';
import { useTranslation } from '../../../contexts/TranslationContext';
import GenericModal from '../../../../components/GenericModal';
import { useTranslation } from '../../../../contexts/TranslationContext';
const ChannelToTeamModal = ({
onClose,

@ -16,8 +16,8 @@ import { RoomManager } from '../../../../../../app/ui-utils/client/lib/RoomManag
import { usePermission } from '../../../../../contexts/AuthorizationContext';
import WarningModal from '../../../../admin/apps/WarningModal';
import MarkdownText from '../../../../../components/MarkdownText';
import ChannelToTeamModal from '../../../../teams/modals/ChannelToTeamModal/ChannelToTeamModal';
import ConvertToTeamModal from '../../../../teams/modals/ConvertToTeamModal';
import ChannelToTeamModal from '../ChannelToTeamModal/ChannelToTeamModal';
import ConvertToTeamModal from '../ConvertToTeamModal';
import { useTabBarClose } from '../../../providers/ToolboxProvider';
import { useEndpointActionExperimental } from '../../../../../hooks/useEndpointAction';
import InfoPanel, { RetentionPolicyCallout } from '../../../../InfoPanel';

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React from 'react';
import { Field, Button } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
@ -16,7 +16,6 @@ export const AddUsers = ({
onClickSave,
value,
onChange,
errors,
}) => {
const t = useTranslation();
@ -30,10 +29,7 @@ export const AddUsers = ({
<VerticalBar.ScrollableContent>
<Field >
<Field.Label flexGrow={0}>{t('Choose_users')}</Field.Label>
<UserAutoCompleteMultiple errors={errors.users} value={value} onChange={onChange} placeholder={t('Choose_users')} />
{errors.users && <Field.Error>
{errors.users}
</Field.Error>}
<UserAutoCompleteMultiple value={value} onChange={onChange} placeholder={t('Choose_users')} />
</Field>
</VerticalBar.ScrollableContent>
<VerticalBar.Footer>
@ -46,10 +42,10 @@ export const AddUsers = ({
export default ({
rid,
onClickBack,
reload,
}) => {
const t = useTranslation();
const dispatchToastMessage = useToastMessageDispatch();
const [errors, setErrors] = useState({});
const onClickClose = useTabBarClose();
const saveAction = useMethod('addUsersToRoom');
@ -69,21 +65,14 @@ export default ({
});
const handleSave = useMutableCallback(async () => {
if (users.length < 1) {
return setErrors({
users: t('Select_at_least_one_user'),
});
}
try {
await saveAction({ rid, users });
dispatchToastMessage({ type: 'success', message: t('Users_added') });
onClickBack();
} catch (e) {
dispatchToastMessage({ type: 'error', message: e });
reload();
} catch ({ message }) {
dispatchToastMessage({ type: 'error', message });
}
setErrors({});
});
return (
@ -93,7 +82,6 @@ export default ({
onClickSave={handleSave}
value={users}
onChange={onChangeUsers}
errors={errors}
/>
);
};

@ -23,14 +23,14 @@ export const InviteUsers = ({
const { copy } = useClipboardWithToast(linkText);
return (
<Box position='absolute' h='full' w='full'>
<>
<VerticalBar.Header>
{onClickBack && <VerticalBar.Back onClick={onClickBack} />}
<VerticalBar.Text>{t('Invite_Users')}</VerticalBar.Text>
{onClickClose && <VerticalBar.Close onClick={onClickClose} />}
</VerticalBar.Header>
<VerticalBar.Content>
<VerticalBar.ScrollableContent>
<Field>
<Field.Label flexGrow={0}>{t('Invite_Link')}</Field.Label>
<Field.Row>
@ -45,8 +45,8 @@ export const InviteUsers = ({
<Box pb='x16'>
{onClickEdit && <Button onClick={onClickEdit}>{t('Edit_Invite')}</Button>}
</Box>
</VerticalBar.Content>
</Box>
</VerticalBar.ScrollableContent>
</>
);
};

@ -22,23 +22,23 @@ import memoize from 'memoize-one';
import { useTranslation } from '../../../../../contexts/TranslationContext';
import { useUserRoom } from '../../../../../contexts/UserContext';
import VerticalBar from '../../../../../components/VerticalBar';
import { useMethod } from '../../../../../contexts/ServerContext';
import { AsyncStatePhase } from '../../../../../hooks/useAsyncState';
import { useAtLeastOnePermission } from '../../../../../contexts/AuthorizationContext';
import { useDataWithLoadMore } from '../../hooks/useDataWithLoadMore';
import { MemberItem } from './components/MemberItem';
import UserInfoWithData from '../../UserInfo';
import InviteUsers from '../InviteUsers/InviteUsers';
import AddUsers from '../AddUsers/AddUsers';
import { useTabBarClose } from '../../../providers/ToolboxProvider';
import ScrollableContentWrapper from '../../../../../components/ScrollableContentWrapper';
import { useMembersList } from '../../../../hooks/useMembersList';
import { useRecordList } from '../../../../../hooks/lists/useRecordList';
export const createItemData = memoize((onClickView, rid) => ({
onClickView,
rid,
}));
const DefaultRow = React.memo(({ user, data, index }) => {
const DefaultRow = React.memo(({ user, data, index, reload }) => {
const { onClickView, rid } = data;
if (!user) {
@ -53,6 +53,7 @@ const DefaultRow = React.memo(({ user, data, index }) => {
status={user.status}
name={user.name}
onClickView={onClickView}
reload={reload}
/>;
});
@ -72,6 +73,7 @@ export const RoomMembers = ({
loadMoreItems,
renderRow: Row = DefaultRow,
rid,
reload,
}) => {
const t = useTranslation();
const inputRef = useAutoFocus(true);
@ -82,7 +84,7 @@ export const RoomMembers = ({
], [t]);
const itemData = createItemData(onClickView, rid);
const lm = useMutableCallback((start) => loadMoreItems(start + 1, Math.min(50, start + 1 - members.length)));
const lm = useMutableCallback((start) => !loading && loadMoreItems(start));
return (
<>
@ -107,15 +109,16 @@ export const RoomMembers = ({
</Box>
</Box>
{ error && <Box pi='x24' pb='x12'><Callout type='danger'>
{error}
</Callout></Box> }
{loading && <Box pi='x24' pb='x12'><Throbber size='x12' /></Box>}
{error && <Box pi='x24' pb='x12'><Callout type='danger'>
{error.message}
</Callout></Box>}
{!loading && members.length <= 0 && <Box pi='x24' pb='x12'>{t('No_results_found')}</Box>}
{!loading && members.length > 0 && (
<Box pi='x24' pb='x12'>
<Box pi='x18' pb='x12'>
<Box is='span' color='info' fontScale='p1'>
{t('Showing')}: <Box is='span' color='default' fontScale='p2'>{members.length}</Box>
</Box>
@ -145,6 +148,7 @@ export const RoomMembers = ({
data={itemData}
user={data}
index={index}
reload={reload}
/>}
/>}
</Box>
@ -162,11 +166,6 @@ export const RoomMembers = ({
RoomMembers.Option = MemberItem;
const useGetUsersOfRoom = (params) => {
const method = useMethod('getUsersOfRoom');
return useDataWithLoadMore(useCallback((args) => method(...args), [method]), params);
};
export default ({
rid,
}) => {
@ -179,10 +178,11 @@ export default ({
const [type, setType] = useLocalStorage('members-list-type', 'online');
const [text, setText] = useState('');
const debouncedText = useDebouncedValue(text, 500);
const params = useMemo(() => [rid, type === 'all', { limit: 50 }, debouncedText], [rid, type, debouncedText]);
const debouncedText = useDebouncedValue(text, 800);
const { membersList, loadMoreItems, reload } = useMembersList(useMemo(() => ({ rid, type: type === 'all', limit: 50, debouncedText }), [rid, type, debouncedText]));
const { value, phase, more, error } = useGetUsersOfRoom(params);
const { phase, items, itemCount: total } = useRecordList(membersList);
const canAddUsers = useAtLeastOnePermission(useMemo(() => [room.t === 'p' ? 'add-user-to-any-p-room' : 'add-user-to-any-c-room', 'add-user-to-joined-room'], [room.t]), rid);
@ -208,12 +208,6 @@ export default ({
const handleBack = useCallback(() => setState({}), [setState]);
const loadMoreItems = useCallback((start, end) => more(([rid, type, , filter]) => [rid, type, { skip: start, limit: end - start }, filter], (prev, next) => ({
total: next.total,
finished: next.records.length < 50,
records: [...prev.records, ...next.records],
})), [more]);
if (state.tab === 'UserInfo') {
return <UserInfoWithData rid={rid} onClickClose={onClickClose} onClickBack={handleBack} username={state.username} />;
}
@ -223,7 +217,7 @@ export default ({
}
if (state.tab === 'AddUsers') {
return <AddUsers onClickClose={onClickClose} rid={rid} onClickBack={handleBack} />;
return <AddUsers onClickClose={onClickClose} rid={rid} onClickBack={handleBack} reload={reload} />;
}
return (
@ -232,16 +226,16 @@ export default ({
loading={phase === AsyncStatePhase.LOADING}
type={type}
text={text}
error={error}
setType={setType}
setText={handleTextChange}
members={value?.records}
total={value?.total}
members={items}
total={total}
onClickClose={onClickClose}
onClickView={viewUser}
onClickAdd={canAddUsers && addUser}
onClickInvite={canAddUsers && createInvite}
loadMoreItems={loadMoreItems}
reload={reload}
/>
);
};

@ -6,15 +6,14 @@ import {
} from '@rocket.chat/fuselage';
import { usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks';
import { useUserInfoActions } from '../../../../hooks/useUserInfoActions';
import { useActionSpread } from '../../../../../hooks/useActionSpread';
import UserAvatar from '../../../../../../components/avatar/UserAvatar';
import { ReactiveUserStatus } from '../../../../../../components/UserStatus';
import { usePreventProgation } from '../../../../../../hooks/usePreventProgation';
const UserActions = ({ username, _id, rid }) => {
const { menu: menuOptions } = useActionSpread(useUserInfoActions({ _id, username }, rid), 0);
const UserActions = ({ username, _id, rid, reload }) => {
const { menu: menuOptions } = useActionSpread(useUserInfoActions({ _id, username }, rid, reload), 0);
if (!menuOptions) {
return null;
}
@ -27,7 +26,16 @@ const UserActions = ({ username, _id, rid }) => {
/>;
};
export const MemberItem = ({ _id, status, name, username, onClickView, style, rid }) => {
export const MemberItem = ({
_id,
status,
name,
username,
onClickView,
style,
rid,
reload,
}) => {
const [showButton, setShowButton] = useState();
const isReduceMotionEnabled = usePrefersReducedMotion();
@ -50,7 +58,7 @@ export const MemberItem = ({ _id, status, name, username, onClickView, style, ri
<Option.Column><ReactiveUserStatus uid={_id} presence={status}/></Option.Column>
<Option.Content>{name} <Option.Description>({username})</Option.Description></Option.Content>
<Option.Menu onClick={onClick}>
{showButton ? <UserActions username={username} rid={rid} _id={_id} /> : <ActionButton
{showButton ? <UserActions username={username} rid={rid} _id={_id} reload={reload} /> : <ActionButton
ghost
tiny
icon='kebab'

@ -16,7 +16,7 @@ import { roomTypes, RoomMemberActions } from '../../../../app/utils';
import { useEndpointActionExperimental } from '../../../hooks/useEndpointAction';
import { useUserRoom } from './useUserRoom';
import { escapeHTML } from '../../../../lib/escapeHTML';
import RemoveUsersModal from '../../teams/members/RemoveUsersModal';
import RemoveUsersModal from '../../teams/contextualBar/members/RemoveUsersModal';
const useUserHasRoomRole = (uid, rid, role) => useReactiveValue(useCallback(() => !!RoomRoles.findOne({ rid, 'u._id': uid, roles: role }), [uid, rid, role]));
@ -77,7 +77,7 @@ const WarningModal = ({ text, confirmText, close, confirm, ...props }) => {
</Modal>;
};
export const useUserInfoActions = (user = {}, rid) => {
export const useUserInfoActions = (user = {}, rid, reload) => {
const t = useTranslation();
const dispatchToastMessage = useToastMessageDispatch();
const directRoute = useRoute('direct');
@ -299,9 +299,10 @@ export const useUserInfoActions = (user = {}, rid) => {
userId={user?._id}
onClose={closeModal}
onCancel={closeModal}
onConfirm={(rooms) => {
removeFromTeam({ teamId: room.teamId, members: [{ userId: user._id }], rooms });
onConfirm={async (rooms) => {
await removeFromTeam({ teamId: room.teamId, members: [{ userId: user._id }], rooms });
closeModal();
reload && reload();
}}
/>);
}
@ -310,9 +311,14 @@ export const useUserInfoActions = (user = {}, rid) => {
text={t('The_user_will_be_removed_from_s', roomName)}
close={closeModal}
confirmText={t('Yes_remove_user')}
confirm={() => { removeUserAction({ roomId: rid, userId: uid }); closeModal(); }}
confirm={async () => {
await removeUserAction({ roomId: rid, userId: uid });
closeModal();
reload && reload();
}}
/>);
});
const removeUserOption = useMemo(() => roomCanRemove && userCanRemove && {
label: <Box color='danger'>{room.teamMain ? t('Remove_from_team') : t('Remove_from_room')}</Box>,
icon: 'sign-out',

@ -3,13 +3,13 @@ import React, { FC, memo, Ref, useCallback, useEffect, useMemo, useState } from
import { useMutableCallback, useDebouncedCallback, useAutoFocus } from '@rocket.chat/fuselage-hooks';
import { Box, Modal, ButtonGroup, Button, TextInput, Field, ToggleSwitch } from '@rocket.chat/fuselage';
import { IUser } from '../../../../../definition/IUser';
import { useTranslation } from '../../../../contexts/TranslationContext';
import { useForm } from '../../../../hooks/useForm';
import { useEndpointActionExperimental } from '../../../../hooks/useEndpointAction';
import { useSetting } from '../../../../contexts/SettingsContext';
import { usePermission } from '../../../../contexts/AuthorizationContext';
import { useMethod } from '../../../../contexts/ServerContext';
import { IUser } from '../../../../definition/IUser';
import { useTranslation } from '../../../contexts/TranslationContext';
import { useForm } from '../../../hooks/useForm';
import { useEndpointActionExperimental } from '../../../hooks/useEndpointAction';
import { useSetting } from '../../../contexts/SettingsContext';
import { usePermission } from '../../../contexts/AuthorizationContext';
import { useMethod } from '../../../contexts/ServerContext';
import TeamNameInput from './TeamNameInput';
import UsersInput from './UsersInput';

@ -2,8 +2,8 @@ import { AutoComplete, Box, Option, Options, Chip, AutoCompleteProps } from '@ro
import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
import React, { FC, memo, useCallback, useMemo, useState } from 'react';
import UserAvatar from '../../../../components/avatar/UserAvatar';
import { useEndpointData } from '../../../../hooks/useEndpointData';
import UserAvatar from '../../../components/avatar/UserAvatar';
import { useEndpointData } from '../../../hooks/useEndpointData';
type UsersInputProps = {
value: unknown[];

@ -2,10 +2,10 @@ import React from 'react';
import { Box, CheckBox, Table, Icon, Margins } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import GenericTable from '../../components/GenericTable';
import { useRoomIcon } from '../../hooks/useRoomIcon';
import { useFormatDateAndTime } from '../../hooks/useFormatDateAndTime';
import { useTranslation } from '../../contexts/TranslationContext';
import GenericTable from '../../../components/GenericTable';
import { useRoomIcon } from '../../../hooks/useRoomIcon';
import { useFormatDateAndTime } from '../../../hooks/useFormatDateAndTime';
import { useTranslation } from '../../../contexts/TranslationContext';
const ChannelRow = ({ onChange, selected, room, lastOwnerWarning = '', formatDate }) => {
const { name, fname, ts, isLastOwner } = room;
@ -64,7 +64,7 @@ const ChannelDesertionTable = ({
fixed={false}
pagination={false}
>
{({ key, ...room }) => <ChannelRow
{(room, key) => <ChannelRow
formatDate={formatDate}
room={room}
key={key}

@ -1,6 +1,6 @@
import React from 'react';
import { roomTypes } from '../../../../../app/utils';
import { roomTypes } from '../../../../app/utils';
const RoomLinkList = ({ rooms }) => {
const roomsArray = Object.values(rooms);

@ -1,9 +1,9 @@
import React, { useMemo, useState } from 'react';
import { AutoComplete, Option, Options } from '@rocket.chat/fuselage';
import RoomAvatar from '../../components/avatar/RoomAvatar';
import { useEndpointData } from '../../hooks/useEndpointData';
import { useUserId } from '../../contexts/UserContext';
import RoomAvatar from '../../../components/avatar/RoomAvatar';
import { useEndpointData } from '../../../hooks/useEndpointData';
import { useUserId } from '../../../contexts/UserContext';
const Avatar = ({ _id, type, avatarETag, test, ...props }) => <RoomAvatar size={Options.AvatarSize} room={{ type, _id, avatarETag }} {...props} />;

@ -2,9 +2,9 @@ import React from 'react';
import { Box, CheckBox, Table, Icon, Margins } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import GenericTable from '../../../../components/GenericTable';
import { useRoomIcon } from '../../../../hooks/useRoomIcon';
import { useTranslation } from '../../../../contexts/TranslationContext';
import GenericTable from '../../../../../components/GenericTable';
import { useRoomIcon } from '../../../../../hooks/useRoomIcon';
import { useTranslation } from '../../../../../contexts/TranslationContext';
const ChannelRow = ({ onChange, selected, room }) => {
const { name, fname, usersCount } = room;

@ -1,8 +1,8 @@
import React from 'react';
import { Box } from '@rocket.chat/fuselage';
import GenericModal from '../../../../components/GenericModal';
import { useTranslation } from '../../../../contexts/TranslationContext';
import GenericModal from '../../../../../components/GenericModal';
import { useTranslation } from '../../../../../contexts/TranslationContext';
const StepOne = ({ onConfirm, onCancel }) => {
const t = useTranslation();

@ -1,8 +1,8 @@
import React from 'react';
import GenericModal from '../../../../components/GenericModal';
import RoomLinkList from '../RoomLinkList';
import { useTranslation } from '../../../../contexts/TranslationContext';
import GenericModal from '../../../../../components/GenericModal';
import RoomLinkList from '../../RoomLinkList';
import { useTranslation } from '../../../../../contexts/TranslationContext';
export const StepThree = ({ deletedRooms, keptRooms, onConfirm, onReturn, onCancel }) => {
const t = useTranslation();

@ -1,8 +1,8 @@
import React from 'react';
import GenericModal from '../../../../components/GenericModal';
import GenericModal from '../../../../../components/GenericModal';
import ChannelDeletionTable from './ChannelDeletionTable';
import { useTranslation } from '../../../../contexts/TranslationContext';
import { useTranslation } from '../../../../../contexts/TranslationContext';
export const StepTwo = ({
rooms,

@ -3,7 +3,7 @@ import React, { useMemo } from 'react';
import StepOne from './StepOne';
import StepTwo from './StepTwo';
import StepThree from './StepThree';
import { useEndpointData } from '../../../../hooks/useEndpointData';
import { useEndpointData } from '../../../../../hooks/useEndpointData';
import DeleteTeamModal from './DeleteTeamModal';
const DeleteTeamModalWithRooms = ({ teamId, onConfirm, onCancel }) => {

@ -1,8 +1,8 @@
import React from 'react';
import GenericModal from '../../../../components/GenericModal';
import GenericModal from '../../../../../components/GenericModal';
import ChannelDesertionTable from '../../ChannelDesertionTable';
import { useTranslation } from '../../../../contexts/TranslationContext';
import { useTranslation } from '../../../../../contexts/TranslationContext';
export const StepOne = ({
rooms,

@ -1,8 +1,8 @@
import React from 'react';
import GenericModal from '../../../../components/GenericModal';
import RoomLinkList from '../RoomLinkList';
import { useTranslation } from '../../../../contexts/TranslationContext';
import GenericModal from '../../../../../components/GenericModal';
import RoomLinkList from '../../RoomLinkList';
import { useTranslation } from '../../../../../contexts/TranslationContext';
export const StepTwo = ({ lastOwnerRooms, keptRooms, onConfirm, onCancel, onClose }) => {
const t = useTranslation();

@ -2,8 +2,8 @@ import React, { useContext, useState, useEffect } from 'react';
import StepOne from './StepOne';
import StepTwo from './StepTwo';
import { useEndpoint } from '../../../../contexts/ServerContext';
import { UserContext } from '../../../../contexts/UserContext';
import { useEndpoint } from '../../../../../contexts/ServerContext';
import { UserContext } from '../../../../../contexts/UserContext';
import LeaveTeamModal from './LeaveTeamModal';
const useJoinedRoomsWithLastOwner = (teamId) => {

@ -2,24 +2,24 @@ import React, { useMemo, useCallback } from 'react';
import { Box, Button, Callout, Option, Menu } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import VerticalBar from '../../../components/VerticalBar';
import RoomAvatar from '../../../components/avatar/RoomAvatar';
import MarkdownText from '../../../components/MarkdownText';
import VerticalBar from '../../../../components/VerticalBar';
import RoomAvatar from '../../../../components/avatar/RoomAvatar';
import MarkdownText from '../../../../components/MarkdownText';
import DeleteTeamModal from './Delete';
import LeaveTeamModal from './Leave';
import InfoPanel, { RetentionPolicyCallout } from '../../InfoPanel';
import { roomTypes, UiTextContext } from '../../../../app/utils';
import { useTabBarClose, useTabBarOpen } from '../../room/providers/ToolboxProvider';
import { useEndpointActionExperimental } from '../../../hooks/useEndpointAction';
import { GenericModalDoNotAskAgain } from '../../../components/GenericModal';
import { useTranslation } from '../../../contexts/TranslationContext';
import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext';
import { useActionSpread } from '../../hooks/useActionSpread';
import { useRoute } from '../../../contexts/RouterContext';
import { useMethod } from '../../../contexts/ServerContext';
import { useSetModal } from '../../../contexts/ModalContext';
import { useSetting } from '../../../contexts/SettingsContext';
import { usePermission } from '../../../contexts/AuthorizationContext';
import InfoPanel, { RetentionPolicyCallout } from '../../../InfoPanel';
import { roomTypes, UiTextContext } from '../../../../../app/utils';
import { useTabBarClose, useTabBarOpen } from '../../../room/providers/ToolboxProvider';
import { useEndpointActionExperimental } from '../../../../hooks/useEndpointAction';
import { GenericModalDoNotAskAgain } from '../../../../components/GenericModal';
import { useTranslation } from '../../../../contexts/TranslationContext';
import { useToastMessageDispatch } from '../../../../contexts/ToastMessagesContext';
import { useActionSpread } from '../../../hooks/useActionSpread';
import { useRoute } from '../../../../contexts/RouterContext';
import { useMethod } from '../../../../contexts/ServerContext';
import { useSetModal } from '../../../../contexts/ModalContext';
import { useSetting } from '../../../../contexts/SettingsContext';
import { usePermission } from '../../../../contexts/AuthorizationContext';
const retentionPolicyMaxAge = {
c: 'RetentionPolicy_MaxAge_Channels',

@ -1,6 +1,6 @@
import React from 'react';
import VerticalBar from '../../../components/VerticalBar';
import VerticalBar from '../../../../components/VerticalBar';
import { TeamsInfo } from './TeamsInfo';
export default {

@ -2,12 +2,12 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { Callout } from '@rocket.chat/fuselage';
import React, { useState, useMemo } from 'react';
import EditChannelWithData from '../../room/contextualBar/Info/EditRoomInfo/EditRoomInfo.js';
import { AsyncStatePhase } from '../../../hooks/useAsyncState';
import { useEndpointData } from '../../../hooks/useEndpointData';
import { useTranslation } from '../../../contexts/TranslationContext';
import EditChannelWithData from '../../../room/contextualBar/Info/EditRoomInfo/EditRoomInfo.js';
import { AsyncStatePhase } from '../../../../hooks/useAsyncState';
import { useEndpointData } from '../../../../hooks/useEndpointData';
import { useTranslation } from '../../../../contexts/TranslationContext';
import TeamsInfo from './TeamsInfo';
import VerticalBar from '../../../components/VerticalBar';
import VerticalBar from '../../../../components/VerticalBar';
export default function TeamsInfoWithRooms({ rid }) {
const [editing, setEditing] = useState(false);

@ -1,6 +1,6 @@
import { FC, lazy, LazyExoticComponent } from 'react';
import { addAction } from '../../room/lib/Toolbox';
import { addAction } from '../../../room/lib/Toolbox';
addAction('team-info', {
groups: ['team'],

@ -2,10 +2,11 @@ import React, { useState } from 'react';
import { Box, Margins } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useTranslation } from '../../../../contexts/TranslationContext';
import GenericModal from '../../../../components/GenericModal';
import { useTranslation } from '../../../../../contexts/TranslationContext';
import GenericModal from '../../../../../components/GenericModal';
import ChannelDesertionTable from '../../ChannelDesertionTable';
import RoomLinkList from './RoomLinkList';
import RoomLinkList from '../../RoomLinkList';
import { usePermission } from '../../../../../contexts/AuthorizationContext';
const STEPS = {
LIST_ROOMS: 'LIST_ROOMS',
@ -86,12 +87,12 @@ export const RemoveUsersSecondStep = ({
</GenericModal>;
};
const RemoveUsersModal = ({
const BaseRemoveUsersModal = ({
onClose,
onCancel,
onConfirm,
rooms,
currentStep = rooms?.length > 0 ? STEPS.LIST_ROOMS : STEPS.CONFIRM_DELETE,
currentStep = rooms?.length === 0 ? STEPS.CONFIRM_DELETE : STEPS.LIST_ROOMS,
username,
}) => {
const [step, setStep] = useState(currentStep);
@ -102,6 +103,8 @@ const RemoveUsersModal = ({
const onContinue = useMutableCallback(() => setStep(STEPS.CONFIRM_DELETE));
const onReturn = useMutableCallback(() => setStep(STEPS.LIST_ROOMS));
const canViewUserRooms = usePermission('view-all-team-channels');
const onChangeRoomSelection = useMutableCallback((room) => {
if (deletedRooms[room._id]) {
setDeletedRooms((deletedRooms) => ({ ...deletedRooms, [room._id]: undefined }));
@ -123,7 +126,7 @@ const RemoveUsersModal = ({
onContinue();
});
if (step === STEPS.CONFIRM_DELETE) {
if (step === STEPS.CONFIRM_DELETE || !canViewUserRooms) {
return <RemoveUsersSecondStep
onConfirm={onConfirm}
onClose={onClose}
@ -149,4 +152,4 @@ const RemoveUsersModal = ({
/>;
};
export default RemoveUsersModal;
export default BaseRemoveUsersModal;

@ -0,0 +1,40 @@
import React, { useMemo } from 'react';
import { Skeleton } from '@rocket.chat/fuselage';
import { useEndpointData } from '../../../../../hooks/useEndpointData';
import BaseRemoveUsersModal from './RemoveUsersModal';
import GenericModal from '../../../../../components/GenericModal';
import { useTranslation } from '../../../../../contexts/TranslationContext';
import { AsyncStatePhase } from '../../../../../lib/asyncState';
const initialData = { user: { username: '' } };
const RemoveUsersModal = ({ teamId, userId, onClose, onCancel, onConfirm }) => {
const t = useTranslation();
const { value, phase } = useEndpointData('teams.listRoomsOfUser', useMemo(() => ({ teamId, userId }), [teamId, userId]));
const userDataFetch = useEndpointData('users.info', useMemo(() => ({ userId }), [userId]), initialData);
const { user: { username } } = userDataFetch?.value;
if (phase === AsyncStatePhase.LOADING) {
return <GenericModal
variant='warning'
onClose={onClose}
title={<Skeleton width='50%'/>}
confirmText={<Skeleton width='full'/>}
confirmText={t('Cancel')}
onConfirm={onClose}
>
<Skeleton width='full'/>
</GenericModal>;
}
return <BaseRemoveUsersModal
onClose={onClose}
username={username}
onCancel={onCancel}
onConfirm={onConfirm}
rooms={value?.rooms}
/>;
};
export default RemoveUsersModal;

@ -5,21 +5,16 @@ import {
useLocalStorage,
} from '@rocket.chat/fuselage-hooks';
import { useTabBarClose } from '../../room/providers/ToolboxProvider';
import { useUserRoom } from '../../../contexts/UserContext';
import { useAtLeastOnePermission } from '../../../contexts/AuthorizationContext';
import UserInfoWithData from '../../room/contextualBar/UserInfo';
import { AsyncStatePhase } from '../../../hooks/useAsyncState';
import { RoomMembers } from '../../room/contextualBar/RoomMembers/List/RoomMembers';
import { useMethod } from '../../../contexts/ServerContext';
import { useDataWithLoadMore } from '../../room/contextualBar/hooks/useDataWithLoadMore';
import AddUsers from '../../room/contextualBar/RoomMembers/AddUsers';
import InviteUsers from '../../room/contextualBar/RoomMembers/InviteUsers';
const useGetUsersOfRoom = (params) => {
const method = useMethod('getUsersOfRoom');
return useDataWithLoadMore(useCallback((args) => method(...args), [method]), params);
};
import { useTabBarClose } from '../../../room/providers/ToolboxProvider';
import { useUserRoom } from '../../../../contexts/UserContext';
import { useAtLeastOnePermission } from '../../../../contexts/AuthorizationContext';
import UserInfoWithData from '../../../room/contextualBar/UserInfo';
import { AsyncStatePhase } from '../../../../hooks/useAsyncState';
import { RoomMembers } from '../../../room/contextualBar/RoomMembers/List/RoomMembers';
import AddUsers from '../../../room/contextualBar/RoomMembers/AddUsers';
import InviteUsers from '../../../room/contextualBar/RoomMembers/InviteUsers';
import { useMembersList } from '../../../hooks/useMembersList';
import { useRecordList } from '../../../../hooks/lists/useRecordList';
const TeamMembers = ({
rid,
@ -35,9 +30,10 @@ const TeamMembers = ({
const [text, setText] = useState('');
const debouncedText = useDebouncedValue(text, 500);
const params = useMemo(() => [rid, type === 'all', { limit: 50 }, debouncedText], [rid, type, debouncedText]);
const { value, phase, more, error } = useGetUsersOfRoom(params);
const { membersList, loadMoreItems, reload } = useMembersList(useMemo(() => ({ rid, type: type === 'all', limit: 50, debouncedText }), [rid, type, debouncedText]));
const { phase, items, itemCount: total } = useRecordList(membersList);
const canAddUsers = useAtLeastOnePermission(useMemo(() => [room.t === 'p' ? 'add-user-to-any-p-room' : 'add-user-to-any-c-room', 'add-user-to-joined-room'], [room.t]), rid);
@ -63,12 +59,6 @@ const TeamMembers = ({
const handleBack = useCallback(() => setState({}), [setState]);
const loadMoreItems = useCallback((start, end) => more(([rid, type, , filter]) => [rid, type, { skip: start, limit: end - start }, filter], (prev, next) => ({
total: next.total,
finished: next.records.length < 50,
records: [...prev.records, ...next.records],
})), [more]);
if (state.tab === 'UserInfo') {
return <UserInfoWithData rid={rid} onClickClose={onClickClose} onClickBack={handleBack} username={state.username} />;
}
@ -78,7 +68,7 @@ const TeamMembers = ({
}
if (state.tab === 'AddUsers') {
return <AddUsers onClickClose={onClickClose} rid={rid} onClickBack={handleBack} />;
return <AddUsers onClickClose={onClickClose} rid={rid} onClickBack={handleBack} reload={reload} />;
}
return (
@ -87,16 +77,16 @@ const TeamMembers = ({
loading={phase === AsyncStatePhase.LOADING}
type={type}
text={text}
error={error}
setType={setType}
setText={handleTextChange}
members={value?.records}
total={value?.total}
members={items}
total={total}
onClickClose={onClickClose}
onClickView={viewUser}
onClickAdd={canAddUsers && addUser}
onClickInvite={canAddUsers && createInvite}
loadMoreItems={loadMoreItems}
reload={reload}
/>
);
};

@ -1,6 +1,6 @@
import { lazy } from 'react';
import { addAction } from '../../room/lib/Toolbox';
import { addAction } from '../../../room/lib/Toolbox';
addAction('team-members', {
groups: ['team'],

@ -1,4 +1,3 @@
import './contextualBar/channels/tabBar';
import './info/tabBar.ts';
import './members/tabBar';
import './info';
import './contextualBar/info/tabBar';
import './contextualBar/members/tabBar';

@ -1,15 +0,0 @@
import React from 'react';
import { roomTypes } from '../../../../app/utils';
const RoomLinkList = ({ rooms }) => {
const roomsArray = Object.values(rooms);
return roomsArray.map((room, i) => <>
<a href={roomTypes.getRouteLink(room.t, room)}>
#{roomTypes.getRoomName(room.t, room)}
</a>
{i === roomsArray.length - 1 ? '.' : ', '}
</>);
};
export default RoomLinkList;

@ -1,56 +0,0 @@
import React, { useMemo } from 'react';
import { Skeleton } from '@rocket.chat/fuselage';
import { useEndpointData } from '../../../../hooks/useEndpointData';
import BaseRemoveUsersModal, { RemoveUsersSecondStep } from './RemoveUsersModal';
import GenericModal from '../../../../components/GenericModal';
import { usePermission } from '../../../../contexts/AuthorizationContext';
import { useTranslation } from '../../../../contexts/TranslationContext';
import { AsyncStatePhase } from '../../../../lib/asyncState';
const RemoveUsersModalWithRooms = ({ teamId, userId, username, onClose, onCancel, onConfirm }) => {
const { value } = useEndpointData('teams.listRoomsOfUser', useMemo(() => ({ teamId, userId }), [teamId, userId]));
return <BaseRemoveUsersModal
onClose={onClose}
username={username}
onCancel={onCancel}
onConfirm={onConfirm}
rooms={value?.rooms}
/>;
};
const initialData = { user: { username: '' } };
const RemoveUsersModal = (props) => {
const t = useTranslation();
const { userId } = props;
const canViewUserRooms = usePermission('view-all-team-channels');
const { value, phase } = useEndpointData('users.info', useMemo(() => ({ userId }), [userId]), initialData);
const { user: { username } } = value;
if (phase === AsyncStatePhase.LOADING) {
return <GenericModal
variant='warning'
onClose={props.onClose}
title={<Skeleton width='50%'/>}
confirmText={<Skeleton width='full'/>}
confirmText={t('Cancel')}
onConfirm={props.onClose}
>
<Skeleton width='full'/>
</GenericModal>;
}
if (canViewUserRooms) {
return <RemoveUsersModalWithRooms {...props} username={username}/>;
}
return <RemoveUsersSecondStep {...props} username={username}/>;
};
export default RemoveUsersModal;

@ -1,3 +1,4 @@
import { IRocketChatRecord } from './IRocketChatRecord';
import { USER_STATUS } from './UserStatus';
export interface ILoginToken {
@ -85,7 +86,7 @@ export interface IRole {
_id: string;
}
export interface IUser {
export interface IUser extends IRocketChatRecord {
_id: string;
createdAt: Date;
roles: string[];
@ -107,7 +108,7 @@ export interface IUser {
oauth?: {
authorizedClients: string[];
};
_updatedAt?: Date;
_updatedAt: Date;
statusLivechat?: string;
e2e?: {
private_key: string;

@ -12,6 +12,7 @@ function findUsers({ rid, status, skip, limit, filter = '' }) {
nickname: 1,
status: 1,
avatarETag: 1,
_updatedAt: 1,
},
sort: {
statusConnection: -1,

Loading…
Cancel
Save