Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions client/components/RoomAutoComplete/RoomAutoComplete.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AutoComplete, Option } from '@rocket.chat/fuselage';
import { AutoComplete, Option, Box } from '@rocket.chat/fuselage';
import React, { memo, useMemo, useState } from 'react';

import { useEndpointData } from '../../hooks/useEndpointData';
Expand Down Expand Up @@ -31,8 +31,12 @@ const RoomAutoComplete = (props) => {
setFilter={setFilter}
renderSelected={({ value, label }) => (
<>
<RoomAvatar size='x20' room={{ type: label?.type || 'c', _id: value, ...label }} />{' '}
{label?.name}
<Box margin='none' mi='x2'>
<RoomAvatar size='x20' room={{ type: label?.type || 'c', _id: value, ...label }} />{' '}
</Box>
<Box margin='none' mi='x2'>
{label?.name}
</Box>
</>
)}
renderItem={({ value, label, ...props }) => (
Expand Down
183 changes: 183 additions & 0 deletions client/sidebar/header/CreateDiscussion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import {
Modal,
Field,
FieldGroup,
ToggleSwitch,
TextInput,
TextAreaInput,
ButtonGroup,
Button,
Icon,
} from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React, { ReactElement } from 'react';

import { IRoom } from '../../../definition/IRoom';
import { IUser } from '../../../definition/IUser';
import RoomAutoComplete from '../../components/RoomAutoComplete';
import UserAutoCompleteMultiple from '../../components/UserAutoCompleteMultiple';
import { useTranslation } from '../../contexts/TranslationContext';
import { useEndpointActionExperimental } from '../../hooks/useEndpointAction';
import { useForm } from '../../hooks/useForm';
import { goToRoomById } from '../../lib/goToRoomById';

type CreateDiscussionFormValues = {
name: string;
parentRoom: IRoom['_id'];
encrypted: boolean;
usernames: Array<IUser['username']>;
firstMessage: string;
};

type CreateDiscussionProps = {
onClose: () => void;
defaultParentRoom?: IRoom['_id'];
defaultParentRoomName?: string;
};

/*
** TO-DO: For other discussion creation locations, some default props should be provided
** such as the parent room or parent message. This component does not support this
** behavior at this time, but should be implemented when needed.
*/

const CreateDiscussion = ({ onClose, defaultParentRoom }: CreateDiscussionProps): ReactElement => {
const t = useTranslation();

const { values, handlers } = useForm({
name: '',
parentRoom: '',
encrypted: false,
usernames: [],
firstMessage: '',
});

const {
name,
parentRoom,
encrypted,
usernames,
firstMessage,
} = values as CreateDiscussionFormValues;

const {
handleName,
handleParentRoom,
handleEncrypted,
handleUsernames,
handleFirstMessage,
} = handlers;

const canCreate = (parentRoom || defaultParentRoom) && name;

const createDiscussion = useEndpointActionExperimental('POST', 'rooms.createDiscussion');

const create = useMutableCallback(
async (): Promise<void> => {
try {
const result = await createDiscussion({
prid: defaultParentRoom || parentRoom,
// eslint-disable-next-line @typescript-eslint/camelcase
t_name: name,
users: usernames,
reply: encrypted ? undefined : firstMessage,
});

goToRoomById(result?.discussion?.rid);
onClose();
} catch (error) {
console.warn(error);
}
},
);

const onChangeUsers = useMutableCallback((value, action) => {
if (!action) {
if (usernames.includes(value)) {
return;
}
return handleUsernames([...usernames, value]);
}
handleUsernames(usernames.filter((current) => current !== value));
});

return (
<Modal>
<Modal.Header>
<Modal.Title>{t('Discussion_title')}</Modal.Title>
<Modal.Close onClick={onClose} />
</Modal.Header>
<Modal.Content>
<FieldGroup>
<Field>
<Field.Description>{t('Discussion_description')}</Field.Description>
</Field>
<Field>
<Field.Label>{t('Discussion_target_channel')}</Field.Label>
<Field.Row>
<RoomAutoComplete
value={parentRoom}
onChange={handleParentRoom}
placeholder={t('Discussion_target_channel_description')}
disabled={defaultParentRoom}
/>
</Field.Row>
</Field>
<Field display='flex' flexDirection='row' justifyContent='spaceBetween' flexGrow={1}>
<Field.Label>{t('Encrypted')}</Field.Label>
<Field.Row>
<ToggleSwitch checked={encrypted} onChange={handleEncrypted} />
</Field.Row>
</Field>
<Field>
<Field.Label>{t('Discussion_name')}</Field.Label>
<Field.Row>
<TextInput
value={name}
onChange={handleName}
addon={<Icon name='baloons' size='x20' />}
placeholder={t('New_discussion_name')}
/>
</Field.Row>
</Field>
<Field>
<Field.Label>{t('Invite_Users')}</Field.Label>
<Field.Row>
<UserAutoCompleteMultiple
value={usernames}
onChange={onChangeUsers}
placeholder={t('Username_Placeholder')}
/>
</Field.Row>
</Field>
<Field>
<Field.Label>{t('Discussion_first_message_title')}</Field.Label>
<Field.Row>
<TextAreaInput
value={firstMessage}
onChange={handleFirstMessage}
placeholder={t('New_discussion_first_message')}
rows={5}
disabled={encrypted}
/>
</Field.Row>
{encrypted && (
<Field.Description>
{t('Discussion_first_message_disabled_due_to_e2e')}
</Field.Description>
)}
</Field>
</FieldGroup>
</Modal.Content>
<Modal.Footer>
<ButtonGroup align='end'>
<Button primary disabled={!canCreate} onClick={create}>
{t('Create')}
</Button>
</ButtonGroup>
</Modal.Footer>
</Modal>
);
};

export default CreateDiscussion;
29 changes: 6 additions & 23 deletions client/sidebar/header/actions/CreateRoomList.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,26 @@ import { Box, Margins } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React from 'react';

import { modal, popover } from '../../../../app/ui-utils/client';
import { useAtLeastOnePermission, usePermission } from '../../../contexts/AuthorizationContext';
import { popover } from '../../../../app/ui-utils/client';
import { useAtLeastOnePermission } from '../../../contexts/AuthorizationContext';
import { useSetModal } from '../../../contexts/ModalContext';
import { useSetting } from '../../../contexts/SettingsContext';
import { useTranslation } from '../../../contexts/TranslationContext';
import CreateTeamModal from '../../../views/teams/CreateTeamModal';
import CreateChannelWithData from '../CreateChannelWithData';
import CreateDirectMessage from '../CreateDirectMessage';
import CreateDiscussion from '../CreateDiscussion';
import CreateRoomListItem from './CreateRoomListItem';

const CREATE_CHANNEL_PERMISSIONS = ['create-c', 'create-p'];
const CREATE_TEAM_PERMISSIONS = ['create-team'];
const CREATE_DIRECT_PERMISSIONS = ['create-d'];
const CREATE_DISCUSSION_PERMISSIONS = ['start-discussion', 'start-discussion-other-user'];

const style = {
textTransform: 'uppercase',
};

const useAction = (title, content) =>
useMutableCallback((e) => {
e.preventDefault();
popover.close();
modal.open({
title,
content,
data: {
onCreate() {
modal.close();
},
},
modifier: 'modal',
showConfirmButton: false,
showCancelButton: false,
confirmOnEnter: false,
});
});

const useReactModal = (Component) => {
const setModal = useSetModal();

Expand All @@ -60,13 +43,13 @@ function CreateRoomList() {

const canCreateChannel = useAtLeastOnePermission(CREATE_CHANNEL_PERMISSIONS);
const canCreateTeam = useAtLeastOnePermission(CREATE_TEAM_PERMISSIONS);
const canCreateDirectMessages = usePermission('create-d');
const canCreateDirectMessages = useAtLeastOnePermission(CREATE_DIRECT_PERMISSIONS);
const canCreateDiscussion = useAtLeastOnePermission(CREATE_DISCUSSION_PERMISSIONS);

const createChannel = useReactModal(CreateChannelWithData);
const createTeam = useReactModal(CreateTeamModal);
const createDiscussion = useReactModal(CreateDiscussion);
const createDirectMessage = useReactModal(CreateDirectMessage);
const createDiscussion = useAction(t('Discussion_title'), 'CreateDiscussion');

const discussionEnabled = useSetting('Discussion_enabled');

Expand Down