Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: EditRoomInfo to typescript #28318

Merged
merged 7 commits into from
Oct 13, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const RoomAvatarEditor = ({ disabled = false, room, roomAvatar, onChangeAvatar }
danger
icon='trash'
title={t('Accounts_SetDefaultAvatar')}
disabled={roomAvatar === null || isRoomFederated(room) || disabled}
disabled={!roomAvatar || isRoomFederated(room) || disabled}
onClick={clickReset}
/>
</ButtonGroup>
Expand Down
97 changes: 17 additions & 80 deletions apps/meteor/client/views/admin/rooms/EditRoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,17 @@ import {
TextAreaInput,
} from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useSetModal, useToastMessageDispatch, useRoute, usePermission, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts';
import { useTranslation } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React, { useState, useMemo } from 'react';
import React, { useMemo } from 'react';

import { RoomSettingsEnum } from '../../../../definition/IRoomTypeConfig';
import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar';
import GenericModal from '../../../components/GenericModal';
import RoomAvatarEditor from '../../../components/avatar/RoomAvatarEditor';
import { useEndpointAction } from '../../../hooks/useEndpointAction';
import { useForm } from '../../../hooks/useForm';
import { roomCoordinator } from '../../../lib/rooms/roomCoordinator';
import DeleteTeamModalWithRooms from '../../teams/contextualBar/info/DeleteTeam';
import { useDeleteRoom } from '../../hooks/roomActions/useDeleteRoom';

type EditRoomProps = {
room: Pick<IRoom, RoomAdminFieldsType>;
Expand Down Expand Up @@ -65,11 +64,6 @@ const getInitialValues = (room: Pick<IRoom, RoomAdminFieldsType>): EditRoomFormV
const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => {
const t = useTranslation();

const [deleting, setDeleting] = useState(false);

const setModal = useSetModal();
const dispatchToastMessage = useToastMessageDispatch();

const { values, handlers, hasUnsavedChanges, reset } = useForm(getInitialValues(room));

const [canViewName, canViewTopic, canViewAnnouncement, canViewArchived, canViewDescription, canViewType, canViewReadOnly] =
Expand Down Expand Up @@ -119,9 +113,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement =>

const changeArchivation = archived !== !!room.archived;

const roomsRoute = useRoute('admin-rooms');

const canDelete = usePermission(`delete-${room.t}`);
const { handleDelete, canDeleteRoom, isDeleting } = useDeleteRoom(room, { reload: onDelete });

const archiveSelector = room.archived ? 'unarchive' : 'archive';
const archiveMessage = room.archived ? 'Room_has_been_unarchived' : 'Room_has_been_archived';
Expand Down Expand Up @@ -161,61 +153,6 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement =>
handleRoomType(roomType === 'p' ? 'c' : 'p');
});

const deleteRoom = useEndpoint('POST', '/v1/rooms.delete');
const deleteTeam = useEndpoint('POST', '/v1/teams.delete');

const handleDelete = useMutableCallback(() => {
const handleDeleteTeam = async (roomsToRemove: IRoom['_id'][]) => {
try {
setDeleting(true);
setModal(null);
await deleteTeam({ teamId: room.teamId as string, ...(roomsToRemove.length && { roomsToRemove }) });
dispatchToastMessage({ type: 'success', message: t('Team_has_been_deleted') });
roomsRoute.push({});
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
setDeleting(false);
} finally {
onDelete();
}
};

if (room.teamMain) {
setModal(
<DeleteTeamModalWithRooms onConfirm={handleDeleteTeam} onCancel={(): void => setModal(null)} teamId={room.teamId as string} />,
);

return;
}

const handleDeleteRoom = async (): Promise<void> => {
try {
setDeleting(true);
setModal(null);
await deleteRoom({ roomId: room._id });
dispatchToastMessage({ type: 'success', message: t('Room_has_been_deleted') });
roomsRoute.push({});
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
setDeleting(false);
} finally {
onDelete();
}
};

setModal(
<GenericModal
variant='danger'
onConfirm={handleDeleteRoom}
onClose={(): void => setModal(null)}
onCancel={(): void => setModal(null)}
confirmText={t('Yes_delete_it')}
>
{t('Delete_Room_Warning')}
</GenericModal>,
);
});

return (
<>
<ContextualbarScrollableContent is='form' onSubmit={useMutableCallback((e) => e.preventDefault())}>
Expand All @@ -227,7 +164,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement =>
<Field>
<FieldLabel required>{t('Name')}</FieldLabel>
<FieldRow>
<TextInput disabled={deleting || !canViewName} value={roomName} onChange={handleRoomName} flexGrow={1} />
<TextInput disabled={isDeleting || !canViewName} value={roomName} onChange={handleRoomName} flexGrow={1} />
</FieldRow>
</Field>
{room.t !== 'd' && (
Expand All @@ -246,7 +183,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement =>
<FieldRow>
<TextAreaInput
rows={4}
disabled={deleting || isRoomFederated(room)}
disabled={isDeleting || isRoomFederated(room)}
value={roomDescription}
onChange={handleRoomDescription}
flexGrow={1}
Expand All @@ -260,7 +197,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement =>
<FieldRow>
<TextAreaInput
rows={4}
disabled={deleting || isRoomFederated(room)}
disabled={isDeleting || isRoomFederated(room)}
value={roomAnnouncement}
onChange={handleRoomAnnouncement}
flexGrow={1}
Expand All @@ -272,7 +209,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement =>
<Field>
<FieldLabel>{t('Topic')}</FieldLabel>
<FieldRow>
<TextAreaInput rows={4} disabled={deleting} value={roomTopic} onChange={handleRoomTopic} flexGrow={1} />
<TextAreaInput rows={4} disabled={isDeleting} value={roomTopic} onChange={handleRoomTopic} flexGrow={1} />
</FieldRow>
</Field>
)}
Expand All @@ -281,7 +218,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement =>
<Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}>
<FieldLabel>{t('Private')}</FieldLabel>
<FieldRow>
<ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={roomType === 'p'} onChange={changeRoomType} />
<ToggleSwitch disabled={isDeleting || isRoomFederated(room)} checked={roomType === 'p'} onChange={changeRoomType} />
</FieldRow>
</Box>
<FieldHint>{t('Just_invited_people_can_access_this_channel')}</FieldHint>
Expand All @@ -292,7 +229,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement =>
<Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}>
<FieldLabel>{t('Read_only')}</FieldLabel>
<FieldRow>
<ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={readOnly} onChange={handleReadOnly} />
<ToggleSwitch disabled={isDeleting || isRoomFederated(room)} checked={readOnly} onChange={handleReadOnly} />
</FieldRow>
</Box>
<FieldHint>{t('Only_authorized_users_can_write_new_messages')}</FieldHint>
Expand All @@ -314,7 +251,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement =>
<Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}>
<FieldLabel>{t('Room_archivation_state_true')}</FieldLabel>
<FieldRow>
<ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={archived} onChange={handleArchived} />
<ToggleSwitch disabled={isDeleting || isRoomFederated(room)} checked={archived} onChange={handleArchived} />
</FieldRow>
</Box>
</Field>
Expand All @@ -325,38 +262,38 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement =>
<Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}>
<FieldLabel>{t('Default')}</FieldLabel>
<FieldRow>
<ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={isDefault} onChange={handleIsDefault} />
<ToggleSwitch disabled={isDeleting || isRoomFederated(room)} checked={isDefault} onChange={handleIsDefault} />
</FieldRow>
</Box>
</Field>
<Field>
<Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}>
<FieldLabel>{t('Favorite')}</FieldLabel>
<FieldRow>
<ToggleSwitch disabled={deleting} checked={favorite} onChange={handleFavorite} />
<ToggleSwitch disabled={isDeleting} checked={favorite} onChange={handleFavorite} />
</FieldRow>
</Box>
</Field>
<Field>
<Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}>
<FieldLabel>{t('Featured')}</FieldLabel>
<FieldRow>
<ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={featured} onChange={handleFeatured} />
<ToggleSwitch disabled={isDeleting || isRoomFederated(room)} checked={featured} onChange={handleFeatured} />
</FieldRow>
</Box>
</Field>
</ContextualbarScrollableContent>
<ContextualbarFooter>
<ButtonGroup stretch>
<Button type='reset' disabled={!hasUnsavedChanges || deleting} onClick={reset}>
<Button type='reset' disabled={!hasUnsavedChanges || isDeleting} onClick={reset}>
{t('Reset')}
</Button>
<Button disabled={!hasUnsavedChanges || deleting} onClick={handleSave}>
<Button disabled={!hasUnsavedChanges || isDeleting} onClick={handleSave}>
{t('Save')}
</Button>
</ButtonGroup>
<ButtonGroup mbs={8} stretch>
<Button icon='trash' danger disabled={deleting || !canDelete || isRoomFederated(room)} onClick={handleDelete}>
<Button icon='trash' danger disabled={isDeleting || !canDeleteRoom || isRoomFederated(room)} onClick={handleDelete}>
{t('Delete')}
</Button>
</ButtonGroup>
Expand Down
89 changes: 89 additions & 0 deletions apps/meteor/client/views/hooks/roomActions/useDeleteRoom.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import type { IRoom, RoomAdminFieldsType } from '@rocket.chat/core-typings';
import { isRoomFederated } from '@rocket.chat/core-typings';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useSetModal, useToastMessageDispatch, useRouter, usePermission, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts';
import { useMutation } from '@tanstack/react-query';
import React from 'react';

import GenericModal from '../../../components/GenericModal';
import DeleteTeamModal from '../../teams/contextualBar/info/DeleteTeam';

export const useDeleteRoom = (room: IRoom | Pick<IRoom, RoomAdminFieldsType>, { reload }: { reload?: () => void } = {}) => {
const t = useTranslation();
const router = useRouter();
const setModal = useSetModal();
const dispatchToastMessage = useToastMessageDispatch();
const hasPermissionToDelete = usePermission(`delete-${room.t}`, room._id);
const canDeleteRoom = isRoomFederated(room) ? false : hasPermissionToDelete;

const isAdminRoute = router.getRouteName() === 'admin-rooms';

const deleteRoomEndpoint = useEndpoint('POST', '/v1/rooms.delete');
const deleteTeamEndpoint = useEndpoint('POST', '/v1/teams.delete');

const deleteRoomMutation = useMutation({
mutationFn: deleteRoomEndpoint,
onSuccess: () => {
dispatchToastMessage({ type: 'success', message: t('Room_has_been_deleted') });
if (isAdminRoute) {
return router.navigate('/admin/rooms');
}

return router.navigate('/home');
},
onError: (error) => {
dispatchToastMessage({ type: 'error', message: error });
},
onSettled: () => {
setModal(null);
reload?.();
},
});

const deleteTeamMutation = useMutation({
mutationFn: deleteTeamEndpoint,
onSuccess: () => {
dispatchToastMessage({ type: 'success', message: t('Team_has_been_deleted') });
if (isAdminRoute) {
return router.navigate('/admin/rooms');
}

return router.navigate('/home');
},
onError: (error) => {
dispatchToastMessage({ type: 'error', message: error });
},
onSettled: () => {
setModal(null);
reload?.();
},
});

const isDeleting = deleteTeamMutation.isLoading || deleteRoomMutation.isLoading;

const handleDelete = useMutableCallback(() => {
const handleDeleteTeam = async (roomsToRemove: IRoom['_id'][]) => {
if (!room.teamId) {
return;
}

deleteTeamMutation.mutateAsync({ teamId: room.teamId, ...(roomsToRemove.length && { roomsToRemove }) });
};

if (room.teamMain && room.teamId) {
return setModal(<DeleteTeamModal onConfirm={handleDeleteTeam} onCancel={(): void => setModal(null)} teamId={room.teamId} />);
}

const handleDeleteRoom = async () => {
deleteRoomMutation.mutateAsync({ roomId: room._id });
};

setModal(
<GenericModal variant='danger' onConfirm={handleDeleteRoom} onCancel={(): void => setModal(null)} confirmText={t('Yes_delete_it')}>
{t('Delete_Room_Warning')}
</GenericModal>,
);
});

return { handleDelete, canDeleteRoom, isDeleting };
};
Loading
Loading