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,141 @@
|
||||
import {
|
||||
Modal,
|
||||
Button,
|
||||
Box,
|
||||
Icon,
|
||||
Label,
|
||||
CheckBox,
|
||||
ModalHeader,
|
||||
ModalHeaderText,
|
||||
ModalTitle,
|
||||
ModalClose,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalFooterAnnotation,
|
||||
ModalFooterControllers,
|
||||
} from '@rocket.chat/fuselage';
|
||||
import type { ComponentProps } from 'react';
|
||||
import { useId, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export type AddMatrixUsersModalProps = {
|
||||
matrixIdVerifiedStatus: Map<string, string>;
|
||||
completeUserList: string[];
|
||||
onClose: () => void;
|
||||
onSave: (args_0: { users: string[]; unbanConfirmed?: boolean }) => Promise<void>;
|
||||
};
|
||||
|
||||
type FormValues = {
|
||||
usersToInvite: string[];
|
||||
};
|
||||
|
||||
const verificationStatusAsIcon = (verificationStatus: string): ComponentProps<typeof Icon>['name'] => {
|
||||
if (verificationStatus === 'VERIFIED') {
|
||||
return 'circle-check';
|
||||
}
|
||||
|
||||
if (verificationStatus === 'UNVERIFIED') {
|
||||
return 'circle-cross';
|
||||
}
|
||||
return 'circle-exclamation';
|
||||
};
|
||||
|
||||
const AddMatrixUsersModal = ({ onClose, matrixIdVerifiedStatus, onSave, completeUserList }: AddMatrixUsersModalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const checkboxId = useId();
|
||||
const [bannedError, setBannedError] = useState(false);
|
||||
const [unbanConfirmed, setUnbanConfirmed] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string>();
|
||||
|
||||
const usersToInvite = completeUserList.filter(
|
||||
(user) => !(matrixIdVerifiedStatus.has(user) && matrixIdVerifiedStatus.get(user) === 'UNVERIFIED'),
|
||||
);
|
||||
const rocketChatUsers = usersToInvite.filter((user) => !matrixIdVerifiedStatus.has(user));
|
||||
|
||||
const { handleSubmit } = useForm<FormValues>({
|
||||
defaultValues: {
|
||||
usersToInvite,
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = async (data: FormValues) => {
|
||||
setLoading(true);
|
||||
setError(undefined);
|
||||
try {
|
||||
await onSave({ users: data.usersToInvite, unbanConfirmed });
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
if (err?.error === 'error-user-is-banned') {
|
||||
setBannedError(true);
|
||||
setUnbanConfirmed(false);
|
||||
} else {
|
||||
setError(err?.message || t('Something_went_wrong'));
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal>
|
||||
<ModalHeader>
|
||||
<ModalHeaderText>
|
||||
<ModalTitle>{t('Continue_Adding')}</ModalTitle>
|
||||
</ModalHeaderText>
|
||||
<ModalClose title={t('Close')} onClick={onClose} />
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Box is='ul'>
|
||||
{[...matrixIdVerifiedStatus.entries()].map(([_matrixId, _verificationStatus]) => (
|
||||
<Box is='li' display='flex' key={_matrixId}>
|
||||
{_matrixId}{' '}
|
||||
<Icon marginInlineStart={4} name={verificationStatusAsIcon(_verificationStatus)} title={t(_verificationStatus)} size='x20' />
|
||||
</Box>
|
||||
))}
|
||||
{rocketChatUsers.map((_user) => (
|
||||
<Box is='li' key={`rocket-chat-${_user}`}>
|
||||
{_user}
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
{bannedError && (
|
||||
<Box color='danger' fontScale='c1' marginBlockStart={8}>
|
||||
{t('User_is_banned_from_room_confirm_unban')}
|
||||
</Box>
|
||||
)}
|
||||
{error && (
|
||||
<Box color='danger' fontScale='c1' marginBlockStart={8}>
|
||||
{error}
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
<ModalFooter justifyContent={bannedError ? 'space-between' : 'flex-end'}>
|
||||
{bannedError && (
|
||||
<ModalFooterAnnotation>
|
||||
<Box display='flex' alignItems='center'>
|
||||
<CheckBox checked={unbanConfirmed} onChange={() => setUnbanConfirmed((prev) => !prev)} id={checkboxId} />
|
||||
<Label htmlFor={checkboxId} marginInlineStart={8}>
|
||||
{t('Yes_unban_user')}
|
||||
</Label>
|
||||
</Box>
|
||||
</ModalFooterAnnotation>
|
||||
)}
|
||||
<ModalFooterControllers>
|
||||
<Button onClick={onClose}>{t('Cancel')}</Button>
|
||||
<Button
|
||||
primary
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
disabled={!(usersToInvite.length > 0) || (bannedError && !unbanConfirmed)}
|
||||
loading={loading}
|
||||
>
|
||||
{t('Yes_continue')}
|
||||
</Button>
|
||||
</ModalFooterControllers>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddMatrixUsersModal;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { useStableCallback } from '@rocket.chat/fuselage-hooks';
|
||||
import { useSetModal, useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
|
||||
import AddMatrixUsersModal from './AddMatrixUsersModal';
|
||||
|
||||
export type useAddMatrixUsersProps = {
|
||||
handleSave: (args_0: { users: string[]; unbanConfirmed?: boolean }) => Promise<void>;
|
||||
users: string[];
|
||||
};
|
||||
|
||||
export const useAddMatrixUsers = () => {
|
||||
const setModal = useSetModal();
|
||||
const dispatchToastMessage = useToastMessageDispatch();
|
||||
const handleClose = useStableCallback(() => setModal(null));
|
||||
const dispatchVerifyEndpoint = useEndpoint('GET', '/v1/federation/matrixIds.verify');
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ users, handleSave }: useAddMatrixUsersProps) => {
|
||||
try {
|
||||
let matrixIdVerificationMap = new Map();
|
||||
const matrixIds = users.filter((user) => user.startsWith('@'));
|
||||
if (matrixIds.length > 0) {
|
||||
const matrixIdsVerificationResponse = await dispatchVerifyEndpoint({ matrixIds });
|
||||
const { results: matrixIdsVerificationResults } = matrixIdsVerificationResponse;
|
||||
matrixIdVerificationMap = new Map(Object.entries(matrixIdsVerificationResults));
|
||||
}
|
||||
|
||||
setModal(
|
||||
<AddMatrixUsersModal
|
||||
completeUserList={users}
|
||||
onClose={handleClose}
|
||||
onSave={handleSave}
|
||||
matrixIdVerifiedStatus={matrixIdVerificationMap as Map<string, string>}
|
||||
/>,
|
||||
);
|
||||
} catch (error) {
|
||||
dispatchToastMessage({ type: 'error', message: error as Error });
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Contextualbar } from '@rocket.chat/ui-client';
|
||||
import type { StoryObj, Meta } from '@storybook/react';
|
||||
|
||||
import AddUsers from './AddUsers';
|
||||
|
||||
export default {
|
||||
component: AddUsers,
|
||||
parameters: {
|
||||
layout: 'fullscreen',
|
||||
actions: { argTypesRegex: '^on.*' },
|
||||
},
|
||||
decorators: [(fn) => <Contextualbar height='100vh'>{fn()}</Contextualbar>],
|
||||
} satisfies Meta<typeof AddUsers>;
|
||||
|
||||
export const Default: StoryObj<typeof AddUsers> = {
|
||||
name: 'AddUsers',
|
||||
};
|
||||
@@ -0,0 +1,175 @@
|
||||
import type { IRoom } from '@rocket.chat/core-typings';
|
||||
import { isRoomFederated, isRoomNativeFederated } from '@rocket.chat/core-typings';
|
||||
import { Field, FieldError, FieldLabel, Button, ButtonGroup, FieldGroup } from '@rocket.chat/fuselage';
|
||||
import { useStableCallback } from '@rocket.chat/fuselage-hooks';
|
||||
import {
|
||||
ContextualbarHeader,
|
||||
ContextualbarBack,
|
||||
ContextualbarTitle,
|
||||
ContextualbarClose,
|
||||
ContextualbarScrollableContent,
|
||||
ContextualbarFooter,
|
||||
ContextualbarDialog,
|
||||
} from '@rocket.chat/ui-client';
|
||||
import { useToastMessageDispatch, useSetModal, useEndpoint, useRoomToolbox } from '@rocket.chat/ui-contexts';
|
||||
import { useId } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useAddMatrixUsers } from './AddMatrixUsers/useAddMatrixUsers';
|
||||
import BannedUsersUnbanModal from './BannedUsersUnbanModal';
|
||||
import UserAutoCompleteMultiple from '../../../../../components/UserAutoCompleteMultiple';
|
||||
import { useRoom } from '../../../contexts/RoomContext';
|
||||
|
||||
const hasExternalUsers = (users: string[]): boolean => users.some((user) => user.startsWith('@'));
|
||||
|
||||
export type AddUsersProps = {
|
||||
rid: IRoom['_id'];
|
||||
onClickBack: () => void;
|
||||
reload: () => void;
|
||||
};
|
||||
|
||||
const AddUsers = ({ rid, onClickBack, reload }: AddUsersProps) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatchToastMessage = useToastMessageDispatch();
|
||||
const room = useRoom();
|
||||
const usersFieldId = useId();
|
||||
const roomIsFederated = isRoomFederated(room);
|
||||
// we are dropping the non native federation for now
|
||||
const isFederationBlocked = room && !isRoomNativeFederated(room);
|
||||
const isFederated = roomIsFederated && !isFederationBlocked;
|
||||
|
||||
const setModal = useSetModal();
|
||||
const { closeTab } = useRoomToolbox();
|
||||
const inviteToChannel = useEndpoint('POST', '/v1/channels.invite');
|
||||
const inviteToGroup = useEndpoint('POST', '/v1/groups.invite');
|
||||
const getBannedUsers = useEndpoint('GET', '/v1/rooms.bannedUsers');
|
||||
const unbanUser = useEndpoint('POST', '/v1/rooms.unbanUser');
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
control,
|
||||
getValues,
|
||||
formState: { isDirty, isSubmitting, errors },
|
||||
} = useForm({ defaultValues: { users: [] } });
|
||||
|
||||
const handleSave = useStableCallback(async ({ users, unbanConfirmed }: { users: string[]; unbanConfirmed?: boolean }) => {
|
||||
if (unbanConfirmed) {
|
||||
const { bannedUsers } = await getBannedUsers({ roomId: rid });
|
||||
const bannedSet = new Set(bannedUsers.map((u) => u.username));
|
||||
const usersToUnban = users.filter((username) => bannedSet.has(username));
|
||||
|
||||
if (usersToUnban.length) {
|
||||
await Promise.all(usersToUnban.map((username) => unbanUser({ roomId: rid, username })));
|
||||
}
|
||||
}
|
||||
await Promise.all(
|
||||
users.map((username) => (room.t === 'c' ? inviteToChannel({ roomId: rid, username }) : inviteToGroup({ roomId: rid, username }))),
|
||||
);
|
||||
dispatchToastMessage({ type: 'success', message: t(roomIsFederated && !isFederationBlocked ? 'Users_invited' : 'Users_added') });
|
||||
onClickBack();
|
||||
reload();
|
||||
});
|
||||
|
||||
const handleSaveWithBannedCheck = useStableCallback(async ({ users }: { users: string[] }) => {
|
||||
try {
|
||||
await handleSave({ users });
|
||||
} catch (error: any) {
|
||||
if (error.error === 'error-user-is-banned') {
|
||||
const { bannedUsers } = await getBannedUsers({ roomId: rid });
|
||||
const bannedSet = new Set(bannedUsers.map((u) => u.username));
|
||||
const usersToUnban = users.filter((username) => bannedSet.has(username));
|
||||
|
||||
if (usersToUnban.length) {
|
||||
setModal(
|
||||
<BannedUsersUnbanModal
|
||||
onClose={() => setModal(null)}
|
||||
onConfirm={async () => {
|
||||
await Promise.all(usersToUnban.map((username) => unbanUser({ roomId: rid, username })));
|
||||
await Promise.all(
|
||||
users.map((username) =>
|
||||
room.t === 'c' ? inviteToChannel({ roomId: rid, username }) : inviteToGroup({ roomId: rid, username }),
|
||||
),
|
||||
);
|
||||
setModal(null);
|
||||
dispatchToastMessage({
|
||||
type: 'success',
|
||||
message: t(roomIsFederated && !isFederationBlocked ? 'Users_invited' : 'Users_added'),
|
||||
});
|
||||
onClickBack();
|
||||
reload();
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
dispatchToastMessage({ type: 'error', message: error as Error });
|
||||
}
|
||||
});
|
||||
|
||||
const addClickHandler = useAddMatrixUsers();
|
||||
|
||||
return (
|
||||
<ContextualbarDialog>
|
||||
<ContextualbarHeader>
|
||||
{onClickBack && <ContextualbarBack onClick={onClickBack} />}
|
||||
<ContextualbarTitle>{t('Add_users')}</ContextualbarTitle>
|
||||
{closeTab && <ContextualbarClose onClick={closeTab} />}
|
||||
</ContextualbarHeader>
|
||||
<ContextualbarScrollableContent>
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel flexGrow={0}>{t('Choose_users')}</FieldLabel>
|
||||
<Controller
|
||||
name='users'
|
||||
control={control}
|
||||
rules={{
|
||||
validate: (users) => !isFederated && (!hasExternalUsers(users) || t('You_cannot_add_external_users_to_non_federated_room')),
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<UserAutoCompleteMultiple
|
||||
federated={isFederated}
|
||||
placeholder={t('Choose_users')}
|
||||
aria-describedby={`${usersFieldId}-error`}
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.users && (
|
||||
<FieldError role='alert' id={`${usersFieldId}-error`}>
|
||||
{errors.users.message}
|
||||
</FieldError>
|
||||
)}
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</ContextualbarScrollableContent>
|
||||
<ContextualbarFooter>
|
||||
<ButtonGroup stretch>
|
||||
{roomIsFederated ? (
|
||||
!isFederationBlocked && (
|
||||
<Button
|
||||
primary
|
||||
disabled={addClickHandler.isPending || !isDirty}
|
||||
onClick={() =>
|
||||
addClickHandler.mutate({
|
||||
users: getValues('users'),
|
||||
handleSave,
|
||||
})
|
||||
}
|
||||
>
|
||||
{t('Add_users')}
|
||||
</Button>
|
||||
)
|
||||
) : (
|
||||
<Button primary loading={isSubmitting} disabled={!isDirty} onClick={handleSubmit(handleSaveWithBannedCheck)}>
|
||||
{t('Add_users')}
|
||||
</Button>
|
||||
)}
|
||||
</ButtonGroup>
|
||||
</ContextualbarFooter>
|
||||
</ContextualbarDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddUsers;
|
||||
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
Modal,
|
||||
Box,
|
||||
Button,
|
||||
Label,
|
||||
CheckBox,
|
||||
ModalHeader,
|
||||
ModalHeaderText,
|
||||
ModalTitle,
|
||||
ModalClose,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalFooterAnnotation,
|
||||
ModalFooterControllers,
|
||||
} from '@rocket.chat/fuselage';
|
||||
import { useId, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export type BannedUsersUnbanModalProps = {
|
||||
onClose: () => void;
|
||||
onConfirm: () => Promise<void>;
|
||||
};
|
||||
|
||||
const BannedUsersUnbanModal = ({ onClose, onConfirm }: BannedUsersUnbanModalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const checkboxId = useId();
|
||||
const [unbanConfirmed, setUnbanConfirmed] = useState(false);
|
||||
const [error, setError] = useState<string>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleConfirm = async () => {
|
||||
setLoading(true);
|
||||
setError(undefined);
|
||||
try {
|
||||
await onConfirm();
|
||||
} catch (err: any) {
|
||||
setError(err?.message || t('Something_went_wrong'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal>
|
||||
<ModalHeader>
|
||||
<ModalHeaderText>
|
||||
<ModalTitle>{t('User_is_banned')}</ModalTitle>
|
||||
</ModalHeaderText>
|
||||
<ModalClose title={t('Close')} onClick={onClose} />
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Box>{t('User_is_banned_from_room_confirm_unban')}</Box>
|
||||
{error && (
|
||||
<Box color='danger' fontScale='c1' marginBlockStart={8}>
|
||||
{error}
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
<ModalFooter justifyContent='space-between'>
|
||||
<ModalFooterAnnotation>
|
||||
<Box display='flex' alignItems='center'>
|
||||
<CheckBox checked={unbanConfirmed} onChange={() => setUnbanConfirmed((prev) => !prev)} id={checkboxId} />
|
||||
<Label htmlFor={checkboxId} marginInlineStart={8}>
|
||||
{t('Yes_unban_user')}
|
||||
</Label>
|
||||
</Box>
|
||||
</ModalFooterAnnotation>
|
||||
<ModalFooterControllers>
|
||||
<Button onClick={onClose}>{t('Cancel')}</Button>
|
||||
<Button primary onClick={handleConfirm} disabled={!unbanConfirmed} loading={loading}>
|
||||
{t('Add_users')}
|
||||
</Button>
|
||||
</ModalFooterControllers>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default BannedUsersUnbanModal;
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './AddUsers';
|
||||
Reference in New Issue
Block a user