TelegramPWA/src/components/right/management/ManageGroupAdminRights.tsx

452 lines
14 KiB
TypeScript

import type { FC } from '../../../lib/teact/teact';
import type React from '../../../lib/teact/teact';
import {
memo, useCallback, useEffect, useMemo, useState,
} from '../../../lib/teact/teact';
import { getActions, getGlobal, withGlobal } from '../../../global';
import type {
ApiChat, ApiChatAdminRights, ApiChatMember, ApiUser,
} from '../../../api/types';
import { ManagementScreens } from '../../../types';
import { getUserFullName, isChatBasicGroup, isChatChannel } from '../../../global/helpers';
import { selectChat, selectChatFullInfo } from '../../../global/selectors';
import useFlag from '../../../hooks/useFlag';
import useHistoryBack from '../../../hooks/useHistoryBack';
import useOldLang from '../../../hooks/useOldLang';
import Icon from '../../common/icons/Icon';
import PrivateChatInfo from '../../common/PrivateChatInfo';
import Checkbox from '../../ui/Checkbox';
import ConfirmDialog from '../../ui/ConfirmDialog';
import FloatingActionButton from '../../ui/FloatingActionButton';
import InputText from '../../ui/InputText';
import ListItem from '../../ui/ListItem';
import Spinner from '../../ui/Spinner';
type OwnProps = {
chatId: string;
selectedUserId?: string;
isPromotedByCurrentUser?: boolean;
isNewAdmin?: boolean;
isActive: boolean;
onScreenSelect: (screen: ManagementScreens) => void;
onClose: NoneToVoidFunction;
};
type StateProps = {
chat: ApiChat;
usersById: Record<string, ApiUser>;
adminMembersById?: Record<string, ApiChatMember>;
hasFullInfo: boolean;
currentUserId?: string;
isChannel: boolean;
isFormFullyDisabled: boolean;
isForum?: boolean;
defaultRights?: ApiChatAdminRights;
};
const CUSTOM_TITLE_MAX_LENGTH = 16;
const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
isActive,
isNewAdmin,
selectedUserId,
defaultRights,
chat,
usersById,
currentUserId,
adminMembersById,
hasFullInfo,
isChannel,
isForum,
isFormFullyDisabled,
onClose,
onScreenSelect,
}) => {
const { updateChatAdmin } = getActions();
const [permissions, setPermissions] = useState<ApiChatAdminRights>({});
const [isTouched, setIsTouched] = useState(Boolean(isNewAdmin));
const [isLoading, setIsLoading] = useState(false);
const [isDismissConfirmationDialogOpen, openDismissConfirmationDialog, closeDismissConfirmationDialog] = useFlag();
const [customTitle, setCustomTitle] = useState('');
const lang = useOldLang();
useHistoryBack({
isActive,
onBack: onClose,
});
const selectedChatMember = useMemo(() => {
const selectedAdminMember = selectedUserId ? adminMembersById?.[selectedUserId] : undefined;
// If `selectedAdminMember` variable is filled with a value, then we have already saved the administrator,
// so now we need to return to the list of administrators
if (isNewAdmin && (selectedAdminMember || !selectedUserId)) {
return undefined;
}
if (isNewAdmin) {
const user = getGlobal().users.byId[selectedUserId!];
return user ? {
userId: user.id,
adminRights: defaultRights,
customTitle: lang('ChannelAdmin'),
isOwner: false,
promotedByUserId: undefined,
} : undefined;
}
return selectedAdminMember;
}, [adminMembersById, defaultRights, isNewAdmin, lang, selectedUserId]);
useEffect(() => {
if (hasFullInfo && selectedUserId && !selectedChatMember) {
onScreenSelect(ManagementScreens.ChatAdministrators);
}
}, [chat, hasFullInfo, onScreenSelect, selectedChatMember, selectedUserId]);
useEffect(() => {
setPermissions(selectedChatMember?.adminRights || {});
setCustomTitle((selectedChatMember?.customTitle || '').substr(0, CUSTOM_TITLE_MAX_LENGTH));
setIsTouched(Boolean(isNewAdmin));
setIsLoading(false);
}, [defaultRights, isNewAdmin, selectedChatMember]);
const handlePermissionChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
const { name } = e.target;
function getUpdatedPermissionValue(value: true | undefined) {
return value ? undefined : true;
}
setPermissions((p) => ({
...p,
[name]: getUpdatedPermissionValue(p[name as keyof ApiChatAdminRights]),
}));
setIsTouched(true);
}, []);
const handleSavePermissions = useCallback(() => {
if (!selectedUserId) {
return;
}
setIsLoading(true);
updateChatAdmin({
chatId: chat.id,
userId: selectedUserId,
adminRights: permissions,
customTitle,
});
}, [selectedUserId, updateChatAdmin, chat.id, permissions, customTitle]);
const handleDismissAdmin = useCallback(() => {
if (!selectedUserId) {
return;
}
updateChatAdmin({
chatId: chat.id,
userId: selectedUserId,
adminRights: {},
});
closeDismissConfirmationDialog();
}, [chat.id, closeDismissConfirmationDialog, selectedUserId, updateChatAdmin]);
const getControlIsDisabled = useCallback((key: keyof ApiChatAdminRights) => {
if (isChatBasicGroup(chat)) {
return false;
}
if (isFormFullyDisabled || !chat.adminRights) {
return true;
}
if (chat.isCreator) {
return false;
}
return !chat.adminRights[key];
}, [chat, isFormFullyDisabled]);
const memberStatus = useMemo(() => {
if (isNewAdmin || !selectedChatMember) {
return undefined;
}
if (selectedChatMember.isOwner) {
return lang('ChannelCreator');
}
const promotedByUser = selectedChatMember.promotedByUserId
? usersById[selectedChatMember.promotedByUserId]
: undefined;
if (promotedByUser) {
return lang('EditAdminPromotedBy', getUserFullName(promotedByUser));
}
return lang('ChannelAdmin');
}, [isNewAdmin, selectedChatMember, usersById, lang]);
const handleCustomTitleChange = useCallback((e) => {
const { value } = e.target;
setCustomTitle(value);
setIsTouched(true);
}, []);
if (!selectedChatMember) {
return undefined;
}
return (
<div className="Management">
<div className="panel-content custom-scroll">
<div className="section">
<ListItem inactive className="chat-item-clickable">
<PrivateChatInfo
userId={selectedChatMember.userId}
status={memberStatus}
forceShowSelf
/>
</ListItem>
<h3 className="section-heading mt-4" dir="auto">{lang('EditAdminWhatCanDo')}</h3>
<div className="ListItem">
<Checkbox
name="changeInfo"
checked={Boolean(permissions.changeInfo)}
label={lang(isChannel ? 'EditAdminChangeChannelInfo' : 'EditAdminChangeGroupInfo')}
blocking
disabled={getControlIsDisabled('changeInfo')}
onChange={handlePermissionChange}
/>
</div>
{isChannel && (
<div className="ListItem">
<Checkbox
name="postMessages"
checked={Boolean(permissions.postMessages)}
label={lang('EditAdminPostMessages')}
blocking
disabled={getControlIsDisabled('postMessages')}
onChange={handlePermissionChange}
/>
</div>
)}
{isChannel && (
<div className="ListItem">
<Checkbox
name="editMessages"
checked={Boolean(permissions.editMessages)}
label={lang('EditAdminEditMessages')}
blocking
disabled={getControlIsDisabled('editMessages')}
onChange={handlePermissionChange}
/>
</div>
)}
<div className="ListItem">
<Checkbox
name="deleteMessages"
checked={Boolean(permissions.deleteMessages)}
label={lang(isChannel ? 'EditAdminDeleteMessages' : 'EditAdminGroupDeleteMessages')}
blocking
disabled={getControlIsDisabled('deleteMessages')}
onChange={handlePermissionChange}
/>
</div>
<div className="ListItem">
<Checkbox
name="postStories"
checked={Boolean(permissions.postStories)}
label={lang('EditAdminPostStories')}
blocking
disabled={getControlIsDisabled('postStories')}
onChange={handlePermissionChange}
/>
</div>
<div className="ListItem">
<Checkbox
name="editStories"
checked={Boolean(permissions.editStories)}
label={lang('EditAdminEditStories')}
blocking
disabled={getControlIsDisabled('editStories')}
onChange={handlePermissionChange}
/>
</div>
<div className="ListItem">
<Checkbox
name="deleteStories"
checked={Boolean(permissions.deleteStories)}
label={lang('EditAdminDeleteStories')}
blocking
disabled={getControlIsDisabled('deleteStories')}
onChange={handlePermissionChange}
/>
</div>
{!isChannel && (
<div className="ListItem">
<Checkbox
name="banUsers"
checked={Boolean(permissions.banUsers)}
label={lang('EditAdminBanUsers')}
blocking
disabled={getControlIsDisabled('banUsers')}
onChange={handlePermissionChange}
/>
</div>
)}
<div className="ListItem">
<Checkbox
name="inviteUsers"
checked={Boolean(permissions.inviteUsers)}
label={lang('EditAdminAddUsers')}
blocking
disabled={getControlIsDisabled('inviteUsers')}
onChange={handlePermissionChange}
/>
</div>
{!isChannel && (
<div className="ListItem">
<Checkbox
name="pinMessages"
checked={Boolean(permissions.pinMessages)}
label={lang('EditAdminPinMessages')}
blocking
disabled={getControlIsDisabled('pinMessages')}
onChange={handlePermissionChange}
/>
</div>
)}
<div className="ListItem">
<Checkbox
name="addAdmins"
checked={Boolean(permissions.addAdmins)}
label={lang('EditAdminAddAdmins')}
blocking
disabled={getControlIsDisabled('addAdmins')}
onChange={handlePermissionChange}
/>
</div>
<div className="ListItem">
<Checkbox
name="manageCall"
checked={Boolean(permissions.manageCall)}
label={lang('StartVoipChatPermission')}
blocking
disabled={getControlIsDisabled('manageCall')}
onChange={handlePermissionChange}
/>
</div>
{isForum && (
<div className="ListItem">
<Checkbox
name="manageTopics"
checked={Boolean(permissions.manageTopics)}
label={lang('ManageTopicsPermission')}
blocking
disabled={getControlIsDisabled('manageTopics')}
onChange={handlePermissionChange}
/>
</div>
)}
{!isChannel && (
<div className="ListItem">
<Checkbox
name="anonymous"
checked={Boolean(permissions.anonymous)}
label={lang('EditAdminSendAnonymously')}
blocking
disabled={getControlIsDisabled('anonymous')}
onChange={handlePermissionChange}
/>
</div>
)}
{isFormFullyDisabled && (
<p className="section-info mb-4" dir="auto">
{lang('Channel.EditAdmin.CannotEdit')}
</p>
)}
{!isChannel && (
<InputText
id="admin-title"
label={lang('EditAdminRank')}
onChange={handleCustomTitleChange}
value={customTitle}
disabled={isFormFullyDisabled}
maxLength={CUSTOM_TITLE_MAX_LENGTH}
/>
)}
{currentUserId !== selectedUserId && !isFormFullyDisabled && !isNewAdmin && (
<ListItem icon="delete" ripple destructive onClick={openDismissConfirmationDialog}>
{lang('EditAdminRemoveAdmin')}
</ListItem>
)}
</div>
</div>
<FloatingActionButton
isShown={isTouched}
onClick={handleSavePermissions}
ariaLabel={lang('Save')}
disabled={isLoading}
>
{isLoading ? (
<Spinner color="white" />
) : (
<Icon name="check" />
)}
</FloatingActionButton>
{!isNewAdmin && (
<ConfirmDialog
isOpen={isDismissConfirmationDialogOpen}
onClose={closeDismissConfirmationDialog}
text="Are you sure you want to dismiss this admin?"
confirmLabel={lang('Channel.Admin.Dismiss')}
confirmHandler={handleDismissAdmin}
confirmIsDestructive
/>
)}
</div>
);
};
export default memo(withGlobal<OwnProps>(
(global, { chatId, isPromotedByCurrentUser }): Complete<StateProps> => {
const chat = selectChat(global, chatId)!;
const fullInfo = selectChatFullInfo(global, chatId);
const { byId: usersById } = global.users;
const { currentUserId } = global;
const isChannel = isChatChannel(chat);
const isFormFullyDisabled = !(chat.isCreator || isPromotedByCurrentUser);
const isForum = chat.isForum;
return {
chat,
usersById,
currentUserId,
isChannel,
isForum,
isFormFullyDisabled,
defaultRights: chat.adminRights,
hasFullInfo: Boolean(fullInfo),
adminMembersById: fullInfo?.adminMembersById,
};
},
(global, { chatId }) => {
return Boolean(selectChat(global, chatId));
},
)(ManageGroupAdminRights));