snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { isRoomFederated } from '@rocket.chat/core-typings';
|
||||
import type { IRoom } from '@rocket.chat/core-typings';
|
||||
import { useStableCallback } from '@rocket.chat/fuselage-hooks';
|
||||
import { GenericModal } from '@rocket.chat/ui-client';
|
||||
import { useSetModal, useToastMessageDispatch, useTranslation, useEndpoint, usePermission } from '@rocket.chat/ui-contexts';
|
||||
|
||||
import { useCanEditRoom } from '../useCanEditRoom';
|
||||
|
||||
export const useRoomConvertToTeam = (room: IRoom) => {
|
||||
const t = useTranslation();
|
||||
const setModal = useSetModal();
|
||||
const dispatchToastMessage = useToastMessageDispatch();
|
||||
|
||||
const canEdit = useCanEditRoom(room);
|
||||
const hasPermissionToConvertRoomToTeam = usePermission('create-team');
|
||||
const canConvertRoomToTeam = isRoomFederated(room) ? false : hasPermissionToConvertRoomToTeam;
|
||||
const allowConvertToTeam = !room.teamId && !room.prid && canConvertRoomToTeam && canEdit;
|
||||
|
||||
const convertRoomToTeam = useEndpoint('POST', room.t === 'c' ? '/v1/channels.convertToTeam' : '/v1/groups.convertToTeam');
|
||||
|
||||
const handleConvertToTeam = useStableCallback(async () => {
|
||||
const onConfirm = async () => {
|
||||
try {
|
||||
await convertRoomToTeam(room.t === 'c' ? { channelId: room._id } : { roomId: room._id });
|
||||
dispatchToastMessage({ type: 'success', message: t('Room_has_been_converted') });
|
||||
} catch (error) {
|
||||
dispatchToastMessage({ type: 'error', message: error });
|
||||
} finally {
|
||||
setModal(null);
|
||||
}
|
||||
};
|
||||
|
||||
setModal(
|
||||
<GenericModal
|
||||
title={t('Confirmation')}
|
||||
variant='warning'
|
||||
onClose={() => setModal(null)}
|
||||
onCancel={() => setModal(null)}
|
||||
onConfirm={onConfirm}
|
||||
confirmText={t('Convert')}
|
||||
>
|
||||
{t('Converting_channel_to_a_team')}
|
||||
</GenericModal>,
|
||||
);
|
||||
});
|
||||
|
||||
return allowConvertToTeam ? handleConvertToTeam : null;
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import { mockAppRoot } from '@rocket.chat/mock-providers';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
|
||||
import { useRoomLeave } from './useRoomLeave';
|
||||
import { createFakeRoom, createFakeSubscription } from '../../../../../../../tests/mocks/data';
|
||||
|
||||
const mockRoom = createFakeRoom({ _id: 'room1', t: 'c', name: 'room1', fname: 'Room 1' });
|
||||
const mockSubscription = createFakeSubscription({ name: 'room1', t: 'c', disableNotifications: false, rid: 'room1' });
|
||||
|
||||
jest.mock('../../../../../../../client/lib/LegacyRoomManager', () => ({
|
||||
LegacyRoomManager: {
|
||||
close: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('../../../../../../../client/lib/rooms/roomCoordinator', () => ({
|
||||
roomCoordinator: {
|
||||
getRoomDirectives: () => ({
|
||||
getUiText: () => 'leaveWarning',
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
it('should return leave function if user has subscription', () => {
|
||||
const wrapper = mockAppRoot()
|
||||
.withPermission('leave-c')
|
||||
.withSubscription({ ...mockSubscription, rid: 'room1' })
|
||||
.build();
|
||||
|
||||
const { result } = renderHook(() => useRoomLeave(mockRoom), { wrapper });
|
||||
expect(typeof result.current).toBe('function');
|
||||
});
|
||||
|
||||
it('should return null if user does not have subscription', () => {
|
||||
const wrapper = mockAppRoot().withPermission('leave-c').build();
|
||||
|
||||
const { result } = renderHook(() => useRoomLeave(mockRoom), { wrapper });
|
||||
expect(result.current).toBeNull();
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
import type { IRoom } from '@rocket.chat/core-typings';
|
||||
import { useStableCallback } from '@rocket.chat/fuselage-hooks';
|
||||
import type { TranslationKey } from '@rocket.chat/ui-contexts';
|
||||
import { useRouter, useSetModal, useToastMessageDispatch, useEndpoint, usePermission, useUserSubscription } from '@rocket.chat/ui-contexts';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { UiTextContext } from '../../../../../../../definition/IRoomTypeConfig';
|
||||
import WarningModal from '../../../../../../components/WarningModal';
|
||||
import { LegacyRoomManager } from '../../../../../../lib/LegacyRoomManager';
|
||||
import { roomCoordinator } from '../../../../../../lib/rooms/roomCoordinator';
|
||||
|
||||
export const useRoomLeave = (room: IRoom) => {
|
||||
const { t } = useTranslation();
|
||||
const subscription = useUserSubscription(room._id);
|
||||
const setModal = useSetModal();
|
||||
const dispatchToastMessage = useToastMessageDispatch();
|
||||
const leaveChannel = useEndpoint('POST', '/v1/channels.leave');
|
||||
const leaveGroup = useEndpoint('POST', '/v1/groups.leave');
|
||||
const leaveDirect = useEndpoint('POST', '/v1/im.leave');
|
||||
const router = useRouter();
|
||||
|
||||
const canLeave = usePermission(room.t === 'c' ? 'leave-c' : 'leave-p') && room.cl !== false && Boolean(subscription);
|
||||
|
||||
const handleLeave = useStableCallback(() => {
|
||||
const leaveAction = async () => {
|
||||
try {
|
||||
if (room.t === 'c') {
|
||||
await leaveChannel({ roomId: room._id });
|
||||
} else if (room.t === 'p') {
|
||||
await leaveGroup({ roomId: room._id });
|
||||
} else if (room.t === 'd') {
|
||||
await leaveDirect({ roomId: room._id });
|
||||
}
|
||||
router.navigate('/home');
|
||||
|
||||
if (room.name) {
|
||||
LegacyRoomManager.close(`${room.t}${room.name}`);
|
||||
}
|
||||
} catch (error) {
|
||||
dispatchToastMessage({ type: 'error', message: error });
|
||||
}
|
||||
setModal(null);
|
||||
};
|
||||
|
||||
const warnText = roomCoordinator.getRoomDirectives(room.t).getUiText(UiTextContext.LEAVE_WARNING);
|
||||
|
||||
setModal(
|
||||
<WarningModal
|
||||
text={t(warnText as TranslationKey, { roomName: room.fname || room.name })}
|
||||
confirmText={t('Leave_room')}
|
||||
close={() => setModal(null)}
|
||||
cancelText={t('Cancel')}
|
||||
confirm={leaveAction}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
return canLeave ? handleLeave : null;
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import { isRoomFederated } from '@rocket.chat/core-typings';
|
||||
import type { IRoom } from '@rocket.chat/core-typings';
|
||||
import { useStableCallback } from '@rocket.chat/fuselage-hooks';
|
||||
import { useSetModal, useToastMessageDispatch, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts';
|
||||
|
||||
import ChannelToTeamModal from '../../ChannelToTeamModal';
|
||||
import { useCanEditRoom } from '../useCanEditRoom';
|
||||
|
||||
export const useRoomMoveToTeam = (room: IRoom) => {
|
||||
const t = useTranslation();
|
||||
const setModal = useSetModal();
|
||||
const dispatchToastMessage = useToastMessageDispatch();
|
||||
|
||||
const canEdit = useCanEditRoom(room);
|
||||
const canMoveToTeam = !isRoomFederated(room) && !room.teamId && !room.prid && canEdit;
|
||||
|
||||
const moveChannelToTeam = useEndpoint('POST', '/v1/teams.addRooms');
|
||||
|
||||
const handleMoveToTeam = useStableCallback(async () => {
|
||||
const onConfirm = async (teamId: IRoom['teamId']) => {
|
||||
if (!teamId) {
|
||||
throw new Error('teamId not provided');
|
||||
}
|
||||
|
||||
try {
|
||||
await moveChannelToTeam({ rooms: [room._id], teamId });
|
||||
dispatchToastMessage({ type: 'success', message: t('Rooms_added_successfully') });
|
||||
} catch (error) {
|
||||
dispatchToastMessage({ type: 'error', message: error });
|
||||
} finally {
|
||||
setModal(null);
|
||||
}
|
||||
};
|
||||
|
||||
setModal(<ChannelToTeamModal onCancel={() => setModal(null)} onConfirm={onConfirm} />);
|
||||
});
|
||||
|
||||
return canMoveToTeam ? handleMoveToTeam : null;
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { IRoom } from '@rocket.chat/core-typings';
|
||||
import { isRoomFederated } from '@rocket.chat/core-typings';
|
||||
import { usePermission, useUser } from '@rocket.chat/ui-contexts';
|
||||
|
||||
import * as Federation from '../../../../../lib/federation/Federation';
|
||||
import { useRoomSubscription } from '../../../contexts/RoomContext';
|
||||
|
||||
export const useCanEditRoom = (room: IRoom) => {
|
||||
const user = useUser();
|
||||
const subscription = useRoomSubscription();
|
||||
|
||||
const hasPermissionToEdit = usePermission('edit-room', room._id);
|
||||
const isFederated = isRoomFederated(room);
|
||||
const canEdit =
|
||||
user && isFederated ? Federation.isEditableByTheUser(user, room, subscription) && hasPermissionToEdit : hasPermissionToEdit;
|
||||
|
||||
return canEdit;
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
import type { IRoom } from '@rocket.chat/core-typings';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useRoomConvertToTeam } from './actions/useRoomConvertToTeam';
|
||||
import { useRoomLeave } from './actions/useRoomLeave';
|
||||
import { useRoomMoveToTeam } from './actions/useRoomMoveToTeam';
|
||||
import { useHideRoomAction } from '../../../../../hooks/useHideRoomAction';
|
||||
import { useDeleteRoom } from '../../../../hooks/roomActions/useDeleteRoom';
|
||||
|
||||
type UseRoomActionsOptions = {
|
||||
onClickEnterRoom?: () => void;
|
||||
onClickEdit?: () => void;
|
||||
resetState?: () => void;
|
||||
};
|
||||
|
||||
export const useRoomActions = (room: IRoom, options: UseRoomActionsOptions) => {
|
||||
const { onClickEnterRoom, onClickEdit, resetState } = options;
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleLeave = useRoomLeave(room);
|
||||
const { handleDelete, canDeleteRoom } = useDeleteRoom(room, { reload: resetState });
|
||||
const handleMoveToTeam = useRoomMoveToTeam(room);
|
||||
const handleConvertToTeam = useRoomConvertToTeam(room);
|
||||
const handleHide = useHideRoomAction({ rid: room._id, type: room.t, name: room.name ?? '' });
|
||||
|
||||
return useMemo(() => {
|
||||
const memoizedActions = {
|
||||
items: [
|
||||
{
|
||||
id: 'hide',
|
||||
content: t('Hide'),
|
||||
icon: 'eye-off' as const,
|
||||
onClick: handleHide,
|
||||
},
|
||||
|
||||
...(onClickEnterRoom
|
||||
? [
|
||||
{
|
||||
id: 'enter',
|
||||
content: t('Enter'),
|
||||
icon: 'login' as const,
|
||||
onClick: onClickEnterRoom,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(onClickEdit
|
||||
? [
|
||||
{
|
||||
id: 'edit',
|
||||
content: t('Edit'),
|
||||
icon: 'edit' as const,
|
||||
onClick: onClickEdit,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(handleLeave
|
||||
? [
|
||||
{
|
||||
id: 'leave',
|
||||
content: t('Leave'),
|
||||
icon: 'sign-out' as const,
|
||||
onClick: handleLeave,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(handleMoveToTeam
|
||||
? [
|
||||
{
|
||||
id: 'move_channel_team',
|
||||
content: t('Teams_move_channel_to_team'),
|
||||
icon: 'team-arrow-right' as const,
|
||||
onClick: handleMoveToTeam,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(handleConvertToTeam
|
||||
? [
|
||||
{
|
||||
id: 'convert_channel_team',
|
||||
content: t('Teams_convert_channel_to_team'),
|
||||
icon: 'team' as const,
|
||||
onClick: handleConvertToTeam,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(canDeleteRoom
|
||||
? [
|
||||
{
|
||||
id: 'delete',
|
||||
content: t('Delete'),
|
||||
icon: 'trash' as const,
|
||||
onClick: handleDelete,
|
||||
variant: 'danger',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
};
|
||||
|
||||
return memoizedActions;
|
||||
}, [canDeleteRoom, handleConvertToTeam, handleDelete, handleHide, handleLeave, handleMoveToTeam, onClickEdit, onClickEnterRoom, t]);
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import type { RoomInfoActionsProps } from '../RoomInfo/RoomInfoActions';
|
||||
|
||||
type UseSplitRoomActionsOptions = {
|
||||
size?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param room
|
||||
* @param options
|
||||
* @returns If more than two room actions are enabled `menu.regular` will be a non-empty array
|
||||
*/
|
||||
export const useSplitRoomActions = (actions: RoomInfoActionsProps['actions'], options?: UseSplitRoomActionsOptions) => {
|
||||
const size = options?.size || 2;
|
||||
|
||||
if (actions.items.length <= size) {
|
||||
return { buttons: actions };
|
||||
}
|
||||
|
||||
const buttons = { items: actions.items.slice(0, size) };
|
||||
const regular = actions.items.slice(size);
|
||||
const firstDanger = regular.findIndex((item) => item.variant);
|
||||
const danger = regular.splice(firstDanger);
|
||||
|
||||
const menu = [{ items: regular }, { items: danger }];
|
||||
|
||||
return { buttons, menu };
|
||||
};
|
||||
Reference in New Issue
Block a user