From 38325215e6854e95de58ed876bc11ea2b6850d03 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Thu, 16 Dec 2021 15:04:26 +1100 Subject: [PATCH] Unban UI (#2091) * adding basic functionaliy for unbanning a user * merge ban and unban user dialog in to one dialog Co-authored-by: warrickct --- _locales/en/messages.json | 5 +- ts/components/SessionWrapperModal.tsx | 1 + ts/components/basic/SessionSpinner.tsx | 20 +-- .../message-content/MessageContextMenu.tsx | 7 - .../message-item/GroupUpdateMessage.tsx | 2 - ts/components/dialog/BanOrUnbanUserDialog.tsx | 160 ++++++++++++++++++ ts/components/dialog/ModalContainer.tsx | 4 + .../leftpane/ConversationListItem.tsx | 2 + ts/components/menu/ConversationHeaderMenu.tsx | 4 + .../menu/ConversationListItemContextMenu.tsx | 6 + ts/components/menu/Menu.tsx | 51 ++++++ ts/interactions/conversationInteractions.ts | 13 ++ ts/interactions/messageInteractions.ts | 100 ++--------- ts/state/ducks/modalDialog.tsx | 14 +- ts/state/selectors/modal.ts | 6 + ts/types/LocalizerKeys.ts | 2 +- 16 files changed, 286 insertions(+), 111 deletions(-) create mode 100644 ts/components/dialog/BanOrUnbanUserDialog.tsx diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 7f08d23c9..ee0034f22 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -252,9 +252,7 @@ "userUnbanned": "User unbanned successfully", "userUnbanFailed": "Unban failed!", "banUser": "Ban User", - "banUserConfirm": "Are you sure you want to ban the user?", "banUserAndDeleteAll": "Ban and Delete All", - "banUserAndDeleteAllConfirm": "Are you sure you want to ban the user and delete all his messages?", "userBanned": "Banned successfully", "userBanFailed": "Ban failed!", "leaveGroup": "Leave Group", @@ -463,5 +461,6 @@ "callMediaPermissionsDialogContent": "The current implementation of voice/video calls will expose your IP address to the Oxen Foundation servers and the calling/called user.", "menuCall": "Call", "startedACall": "You called $name$", - "answeredACall": "Call with $name$" + "answeredACall": "Call with $name$", + "banAndDeleteAllDialogTitle": "Ban and Delete All Messages Confirmation" } diff --git a/ts/components/SessionWrapperModal.tsx b/ts/components/SessionWrapperModal.tsx index f34453342..c317132e6 100644 --- a/ts/components/SessionWrapperModal.tsx +++ b/ts/components/SessionWrapperModal.tsx @@ -106,6 +106,7 @@ export const SessionWrapperModal = (props: SessionWrapperModalType) => {
{props.children} +
{onConfirm ? ( diff --git a/ts/components/basic/SessionSpinner.tsx b/ts/components/basic/SessionSpinner.tsx index b57bb0110..2bc6b8582 100644 --- a/ts/components/basic/SessionSpinner.tsx +++ b/ts/components/basic/SessionSpinner.tsx @@ -7,16 +7,12 @@ type Props = { export const SessionSpinner = (props: Props) => { const { loading } = props; - return ( - <> - {loading ? ( -
-
-
-
-
-
- ) : null} - - ); + return loading ? ( +
+
+
+
+
+
+ ) : null; }; diff --git a/ts/components/conversation/message/message-content/MessageContextMenu.tsx b/ts/components/conversation/message/message-content/MessageContextMenu.tsx index 7ae21b543..dcc01652e 100644 --- a/ts/components/conversation/message/message-content/MessageContextMenu.tsx +++ b/ts/components/conversation/message/message-content/MessageContextMenu.tsx @@ -150,10 +150,6 @@ export const MessageContextMenu = (props: Props) => { MessageInteraction.banUser(authorPhoneNumber, convoId); }, [authorPhoneNumber, convoId]); - const onBanAndDeleteAll = useCallback(() => { - MessageInteraction.banUser(authorPhoneNumber, convoId, true); - }, [authorPhoneNumber, convoId]); - const onUnban = useCallback(() => { MessageInteraction.unbanUser(authorPhoneNumber, convoId); }, [authorPhoneNumber, convoId]); @@ -203,9 +199,6 @@ export const MessageContextMenu = (props: Props) => { ) : null} {weAreAdmin && isPublic ? {window.i18n('banUser')} : null} - {weAreAdmin && isPublic ? ( - {window.i18n('banUserAndDeleteAll')} - ) : null} {weAreAdmin && isOpenGroupV2 ? ( {window.i18n('unbanUser')} ) : null} diff --git a/ts/components/conversation/message/message-item/GroupUpdateMessage.tsx b/ts/components/conversation/message/message-item/GroupUpdateMessage.tsx index ce6d41d26..86b61020f 100644 --- a/ts/components/conversation/message/message-item/GroupUpdateMessage.tsx +++ b/ts/components/conversation/message/message-item/GroupUpdateMessage.tsx @@ -54,8 +54,6 @@ const ChangeItemLeft = (left: Array): string => { const ChangeItem = (change: PropsForGroupUpdateType): string => { switch (change.type) { case 'name': - console.warn('name: ', change.newName); - return window.i18n('titleIsNow', [change.newName || '']); case 'add': return ChangeItemJoined(change.added); diff --git a/ts/components/dialog/BanOrUnbanUserDialog.tsx b/ts/components/dialog/BanOrUnbanUserDialog.tsx new file mode 100644 index 000000000..da89371da --- /dev/null +++ b/ts/components/dialog/BanOrUnbanUserDialog.tsx @@ -0,0 +1,160 @@ +import React, { useRef, useState } from 'react'; +import { PubKey } from '../../session/types'; +import { ToastUtils } from '../../session/utils'; +import { Flex } from '../basic/Flex'; +import { useDispatch } from 'react-redux'; +import { BanType, updateBanOrUnbanUserModal } from '../../state/ducks/modalDialog'; +import { SpacerSM } from '../basic/Text'; +import { getConversationController } from '../../session/conversations/ConversationController'; +import { ApiV2 } from '../../session/apis/open_group_api/opengroupV2'; +import { SessionWrapperModal } from '../SessionWrapperModal'; +import { SessionSpinner } from '../basic/SessionSpinner'; +import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton'; +import { ConversationModel } from '../../models/conversation'; +import { useFocusMount } from '../../hooks/useFocusMount'; +import { useConversationPropsById } from '../../hooks/useParamSelector'; +// tslint:disable: use-simple-attributes + +async function banOrUnBanUserCall( + convo: ConversationModel, + textValue: string, + banType: BanType, + deleteAll: boolean +) { + // if we don't have valid data entered by the user + const pubkey = PubKey.from(textValue); + if (!pubkey) { + window.log.info(`invalid pubkey for ${banType} user:${textValue}`); + ToastUtils.pushInvalidPubKey(); + return false; + } + try { + // this is a v2 opengroup + const roomInfos = convo.toOpenGroupV2(); + const isChangeApplied = + banType === 'ban' + ? await ApiV2.banUser(pubkey, roomInfos, deleteAll) + : await ApiV2.unbanUser(pubkey, roomInfos); + + if (!isChangeApplied) { + window?.log?.warn(`failed to ${banType} user: ${isChangeApplied}`); + + banType === 'ban' ? ToastUtils.pushUserBanFailure() : ToastUtils.pushUserUnbanSuccess(); + return false; + } + window?.log?.info(`${pubkey.key} user ${banType}ned successfully...`); + banType === 'ban' ? ToastUtils.pushUserBanSuccess() : ToastUtils.pushUserUnbanSuccess(); + return true; + } catch (e) { + window?.log?.error(`Got error while ${banType}ning user:`, e); + + return false; + } +} + +export const BanOrUnBanUserDialog = (props: { + conversationId: string; + banType: BanType; + pubkey?: string; +}) => { + const { conversationId, banType, pubkey } = props; + const { i18n } = window; + const isBan = banType === 'ban'; + const dispatch = useDispatch(); + const convo = getConversationController().get(conversationId); + const inputRef = useRef(null); + + useFocusMount(inputRef, true); + const wasGivenAPubkey = Boolean(pubkey?.length); + const [inputBoxValue, setInputBoxValue] = useState(''); + const [inProgress, setInProgress] = useState(false); + + const sourceConvoProps = useConversationPropsById(pubkey); + + const inputTextToDisplay = + wasGivenAPubkey && sourceConvoProps + ? `${sourceConvoProps.profileName} ${PubKey.shorten(sourceConvoProps.id)}` + : undefined; + + /** + * Ban or Unban a user from an open group + * @param deleteAll Delete all messages for that user in the group (only works with ban) + */ + const banOrUnBanUser = async (deleteAll: boolean = false) => { + const castedPubkey = pubkey?.length ? pubkey : inputBoxValue; + + window?.log?.info(`asked to ${banType} user: ${castedPubkey}, banAndDeleteAll:${deleteAll}`); + setInProgress(true); + const isBanned = await banOrUnBanUserCall(convo, castedPubkey, banType, deleteAll); + if (isBanned) { + // clear input box + setInputBoxValue(''); + if (wasGivenAPubkey) { + dispatch(updateBanOrUnbanUserModal(null)); + } + } + + setInProgress(false); + }; + + const chatName = convo.get('name'); + const title = `${isBan ? window.i18n('banUser') : window.i18n('unbanUser')}: ${chatName}`; + + const onPubkeyBoxChanges = (e: React.ChangeEvent) => { + setInputBoxValue(e.target.value?.trim() || ''); + }; + + /** + * Starts procedure for banning/unbanning user and all their messages using dialog + */ + const startBanAndDeleteAllSequence = async () => { + await banOrUnBanUser(true); + }; + + const buttonText = isBan ? i18n('banUser') : i18n('unbanUser'); + + return ( + { + dispatch(updateBanOrUnbanUserModal(null)); + }} + > + + + + + {isBan && ( + <> + + + + )} + + + + + ); +}; diff --git a/ts/components/dialog/ModalContainer.tsx b/ts/components/dialog/ModalContainer.tsx index 2734062f5..bfaf3bfc1 100644 --- a/ts/components/dialog/ModalContainer.tsx +++ b/ts/components/dialog/ModalContainer.tsx @@ -3,6 +3,7 @@ import { useSelector } from 'react-redux'; import { getAddModeratorsModal, getAdminLeaveClosedGroupDialog, + getBanOrUnbanUserModalState, getChangeNickNameDialog, getConfirmModal, getDeleteAccountModalState, @@ -30,6 +31,7 @@ import { RemoveModeratorsDialog } from './ModeratorsRemoveDialog'; import { UpdateGroupMembersDialog } from './UpdateGroupMembersDialog'; import { UpdateGroupNameDialog } from './UpdateGroupNameDialog'; import { SessionNicknameDialog } from './SessionNicknameDialog'; +import { BanOrUnBanUserDialog } from './BanOrUnbanUserDialog'; export const ModalContainer = () => { const confirmModalState = useSelector(getConfirmModal); @@ -46,9 +48,11 @@ export const ModalContainer = () => { const adminLeaveClosedGroupModalState = useSelector(getAdminLeaveClosedGroupDialog); const sessionPasswordModalState = useSelector(getSessionPasswordDialog); const deleteAccountModalState = useSelector(getDeleteAccountModalState); + const banOrUnbanUserModalState = useSelector(getBanOrUnbanUserModalState); return ( <> + {banOrUnbanUserModalState && } {inviteModalState && } {addModeratorsModalState && } {removeModeratorsModalState && } diff --git a/ts/components/leftpane/ConversationListItem.tsx b/ts/components/leftpane/ConversationListItem.tsx index a66735069..4c2a813b3 100644 --- a/ts/components/leftpane/ConversationListItem.tsx +++ b/ts/components/leftpane/ConversationListItem.tsx @@ -315,6 +315,7 @@ const ConversationListItem = (props: Props) => { avatarPath, isPrivate, currentNotificationSetting, + weAreAdmin, isMessageRequest, } = props; const triggerId = `conversation-item-${conversationId}-ctxmenu`; @@ -389,6 +390,7 @@ const ConversationListItem = (props: Props) => { type={type} currentNotificationSetting={currentNotificationSetting || 'all'} avatarPath={avatarPath || null} + weAreAdmin={weAreAdmin} />
diff --git a/ts/components/menu/ConversationHeaderMenu.tsx b/ts/components/menu/ConversationHeaderMenu.tsx index 6004cbffa..05348ae85 100644 --- a/ts/components/menu/ConversationHeaderMenu.tsx +++ b/ts/components/menu/ConversationHeaderMenu.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { animation, Menu } from 'react-contexify'; import { getAddModeratorsMenuItem, + getBanMenuItem, getBlockMenuItem, getChangeNicknameMenuItem, getClearNicknameMenuItem, @@ -16,6 +17,7 @@ import { getPinConversationMenuItem, getRemoveModeratorsMenuItem, getShowUserDetailsMenuItem, + getUnbanMenuItem, getUpdateGroupNameMenuItem, } from './Menu'; import _ from 'lodash'; @@ -79,6 +81,8 @@ const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => { {getDeleteMessagesMenuItem(conversationId)} {getAddModeratorsMenuItem(weAreAdmin, isPublic, isKickedFromGroup, conversationId)} {getRemoveModeratorsMenuItem(weAreAdmin, isPublic, isKickedFromGroup, conversationId)} + {getBanMenuItem(weAreAdmin, isPublic, isKickedFromGroup, conversationId)} + {getUnbanMenuItem(weAreAdmin, isPublic, isKickedFromGroup, conversationId)} {getUpdateGroupNameMenuItem(weAreAdmin, isKickedFromGroup, left, conversationId)} {getLeaveGroupMenuItem(isKickedFromGroup, left, isGroup, isPublic, conversationId)} {getInviteContactMenuItem(isGroup, isPublic, conversationId)} diff --git a/ts/components/menu/ConversationListItemContextMenu.tsx b/ts/components/menu/ConversationListItemContextMenu.tsx index b00f21e96..934467803 100644 --- a/ts/components/menu/ConversationListItemContextMenu.tsx +++ b/ts/components/menu/ConversationListItemContextMenu.tsx @@ -8,6 +8,7 @@ import { } from '../../models/conversation'; import { + getBanMenuItem, getBlockMenuItem, getChangeNicknameMenuItem, getClearNicknameMenuItem, @@ -20,6 +21,7 @@ import { getNotificationForConvoMenuItem, getPinConversationMenuItem, getShowUserDetailsMenuItem, + getUnbanMenuItem, } from './Menu'; export type PropsContextConversationItem = { @@ -36,6 +38,7 @@ export type PropsContextConversationItem = { theme?: any; currentNotificationSetting: ConversationNotificationSettingType; avatarPath: string | null; + weAreAdmin?: boolean; }; const ConversationListItemContextMenu = (props: PropsContextConversationItem) => { @@ -51,6 +54,7 @@ const ConversationListItemContextMenu = (props: PropsContextConversationItem) => isKickedFromGroup, currentNotificationSetting, isPrivate, + weAreAdmin, } = props; const isGroup = type === 'group'; @@ -75,6 +79,8 @@ const ConversationListItemContextMenu = (props: PropsContextConversationItem) => {getChangeNicknameMenuItem(isMe, isGroup, conversationId)} {getClearNicknameMenuItem(isMe, hasNickname, isGroup, conversationId)} {getDeleteMessagesMenuItem(conversationId)} + {getBanMenuItem(weAreAdmin, isPublic, isKickedFromGroup, conversationId)} + {getUnbanMenuItem(weAreAdmin, isPublic, isKickedFromGroup, conversationId)} {getInviteContactMenuItem(isGroup, isPublic, conversationId)} {getDeleteContactMenuItem(isGroup, isPublic, left, isKickedFromGroup, conversationId)} {getLeaveGroupMenuItem(isKickedFromGroup, left, isGroup, isPublic, conversationId)} diff --git a/ts/components/menu/Menu.tsx b/ts/components/menu/Menu.tsx index 2974c6778..6e55c3b1b 100644 --- a/ts/components/menu/Menu.tsx +++ b/ts/components/menu/Menu.tsx @@ -11,9 +11,11 @@ import { setDisappearingMessagesByConvoId, setNotificationForConvoId, showAddModeratorsByConvoId, + showBanUserByConvoId, showInviteContactByConvoId, showLeaveGroupByConvoId, showRemoveModeratorsByConvoId, + showUnbanUserByConvoId, showUpdateGroupNameByConvoId, unblockConvoById, } from '../../interactions/conversationInteractions'; @@ -81,6 +83,14 @@ function showDeleteContact( return !isGroup || (isGroup && (isGroupLeft || isKickedFromGroup || isPublic)); } +const showUnbanUser = (isAdmin: boolean, isPublic: boolean, isKickedFromGroup: boolean) => { + return !isKickedFromGroup && isAdmin && isPublic; +}; + +const showBanUser = (isAdmin: boolean, isPublic: boolean, isKickedFromGroup: boolean) => { + return !isKickedFromGroup && isAdmin && isPublic; +}; + function showAddModerators( isAdmin: boolean, isPublic: boolean, @@ -327,6 +337,47 @@ export function getAddModeratorsMenuItem( return null; } +export function getUnbanMenuItem( + isAdmin: boolean | undefined, + isPublic: boolean | undefined, + isKickedFromGroup: boolean | undefined, + conversationId: string +): JSX.Element | null { + if (showUnbanUser(Boolean(isAdmin), Boolean(isPublic), Boolean(isKickedFromGroup))) { + return ( + { + showUnbanUserByConvoId(conversationId); + }} + > + {window.i18n('unbanUser')} + + ); + } + // TODO: translations + return null; +} + +export function getBanMenuItem( + isAdmin: boolean | undefined, + isPublic: boolean | undefined, + isKickedFromGroup: boolean | undefined, + conversationId: string +): JSX.Element | null { + if (showBanUser(Boolean(isAdmin), Boolean(isPublic), Boolean(isKickedFromGroup))) { + return ( + { + showBanUserByConvoId(conversationId); + }} + > + {window.i18n('banUser')} + + ); + } + return null; +} + export function getCopyMenuItem( isPublic: boolean | undefined, isGroup: boolean | undefined, diff --git a/ts/interactions/conversationInteractions.ts b/ts/interactions/conversationInteractions.ts index 4cbbccc49..f5aa19a7b 100644 --- a/ts/interactions/conversationInteractions.ts +++ b/ts/interactions/conversationInteractions.ts @@ -14,6 +14,7 @@ import { adminLeaveClosedGroup, changeNickNameModal, updateAddModeratorsModal, + updateBanOrUnbanUserModal, updateConfirmModal, updateGroupMembersModal, updateGroupNameModal, @@ -213,6 +214,18 @@ export function showRemoveModeratorsByConvoId(conversationId: string) { window.inboxStore?.dispatch(updateRemoveModeratorsModal({ conversationId })); } +export function showBanUserByConvoId(conversationId: string, pubkey?: string) { + window.inboxStore?.dispatch( + updateBanOrUnbanUserModal({ banType: 'ban', conversationId, pubkey }) + ); +} + +export function showUnbanUserByConvoId(conversationId: string, pubkey?: string) { + window.inboxStore?.dispatch( + updateBanOrUnbanUserModal({ banType: 'unban', conversationId, pubkey }) + ); +} + export async function markAllReadByConvoId(conversationId: string) { const conversation = getConversationController().get(conversationId); perfStart(`markAllReadByConvoId-${conversationId}`); diff --git a/ts/interactions/messageInteractions.ts b/ts/interactions/messageInteractions.ts index 2ae61afe5..6c6925f62 100644 --- a/ts/interactions/messageInteractions.ts +++ b/ts/interactions/messageInteractions.ts @@ -1,5 +1,4 @@ import _ from 'lodash'; -import { getV2OpenGroupRoom } from '../data/opengroups'; import { ApiV2 } from '../session/apis/open_group_api/opengroupV2'; import { joinOpenGroupV2WithUIEvents } from '../session/apis/open_group_api/opengroupV2/JoinOpenGroupV2'; import { @@ -10,13 +9,9 @@ import { getConversationController } from '../session/conversations'; import { PubKey } from '../session/types'; import { ToastUtils } from '../session/utils'; -import { updateConfirmModal } from '../state/ducks/modalDialog'; +import { updateBanOrUnbanUserModal, updateConfirmModal } from '../state/ducks/modalDialog'; -export function banUser( - userToBan: string, - conversationId: string, - deleteAllMessages: boolean = false -) { +export function banUser(userToBan: string, conversationId: string) { let pubKeyToBan: PubKey; try { pubKeyToBan = PubKey.cast(userToBan); @@ -25,50 +20,16 @@ export function banUser( ToastUtils.pushUserBanFailure(); return; } + if (!isOpenGroupV2(conversationId)) { + window.log.warn(`Conversation ${conversationId} is not an open group`); + ToastUtils.pushUserBanFailure(); - const onClickClose = () => { - window.inboxStore?.dispatch(updateConfirmModal(null)); - }; - - const title = deleteAllMessages ? window.i18n('banUserAndDeleteAll') : window.i18n('banUser'); - const message = deleteAllMessages - ? window.i18n('banUserAndDeleteAllConfirm') - : window.i18n('banUserConfirm'); - - const confirmationModalProps = { - title, - message, - onClickClose, - onClickOk: async () => { - const conversation = getConversationController().get(conversationId); - if (!conversation) { - window.log.info('cannot ban user, the corresponding conversation was not found.'); - return; - } - let success = false; - if (isOpenGroupV2(conversation.id)) { - const roomInfos = await getV2OpenGroupRoom(conversation.id); - if (!roomInfos) { - window.log.warn('banUser room not found'); - } else { - success = await ApiV2.banUser( - pubKeyToBan, - _.pick(roomInfos, 'serverUrl', 'roomId'), - deleteAllMessages - ); - } - } else { - throw new Error('V1 opengroup are not supported'); - } - if (success) { - ToastUtils.pushUserBanSuccess(); - } else { - ToastUtils.pushUserBanFailure(); - } - }, - }; + return; + } - window.inboxStore?.dispatch(updateConfirmModal(confirmationModalProps)); + window.inboxStore?.dispatch( + updateBanOrUnbanUserModal({ banType: 'ban', conversationId, pubkey: pubKeyToBan.key }) + ); } /** @@ -84,45 +45,14 @@ export function unbanUser(userToUnBan: string, conversationId: string) { ToastUtils.pushUserBanFailure(); return; } - if (!isOpenGroupV2(conversationId || '')) { - window?.log?.warn('no way to unban on a opengroupv1'); - ToastUtils.pushUserBanFailure(); + if (!isOpenGroupV2(conversationId)) { + window.log.warn(`Conversation ${conversationId} is not an open group`); + ToastUtils.pushUserUnbanFailure(); + return; } - - const onClickClose = () => window.inboxStore?.dispatch(updateConfirmModal(null)); - - const onClickOk = async () => { - const conversation = getConversationController().get(conversationId); - - if (!conversation) { - // double check here. the convo might have been removed since the dialog was opened - window.log.info('cannot unban user, the corresponding conversation was not found.'); - return; - } - let success = false; - if (isOpenGroupV2(conversation.id)) { - const roomInfos = await getV2OpenGroupRoom(conversation.id); - if (!roomInfos) { - window.log.warn('unbanUser room not found'); - } else { - success = await ApiV2.unbanUser(pubKeyToUnban, _.pick(roomInfos, 'serverUrl', 'roomId')); - } - } - if (success) { - ToastUtils.pushUserUnbanSuccess(); - } else { - ToastUtils.pushUserUnbanFailure(); - } - }; - window.inboxStore?.dispatch( - updateConfirmModal({ - title: window.i18n('unbanUser'), - message: window.i18n('unbanUserConfirm'), - onClickOk, - onClickClose, - }) + updateBanOrUnbanUserModal({ banType: 'unban', conversationId, pubkey: pubKeyToUnban.key }) ); } diff --git a/ts/state/ducks/modalDialog.tsx b/ts/state/ducks/modalDialog.tsx index 1eb17279d..c7b72ab71 100644 --- a/ts/state/ducks/modalDialog.tsx +++ b/ts/state/ducks/modalDialog.tsx @@ -1,9 +1,15 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { SessionConfirmDialogProps } from '../../components/dialog/SessionConfirm'; import { PasswordAction } from '../../components/dialog/SessionPasswordDialog'; +export type BanType = 'ban' | 'unban'; export type ConfirmModalState = SessionConfirmDialogProps | null; export type InviteContactModalState = { conversationId: string } | null; +export type BanOrUnbanUserModalState = { + conversationId: string; + banType: BanType; + pubkey?: string; +} | null; export type AddModeratorsModalState = InviteContactModalState; export type RemoveModeratorsModalState = InviteContactModalState; export type UpdateGroupMembersModalState = InviteContactModalState; @@ -26,8 +32,9 @@ export type UserDetailsModalState = { export type ModalState = { confirmModal: ConfirmModalState; inviteContactModal: InviteContactModalState; - addModeratorsModal: AddModeratorsModalState; + banOrUnbanUserModal: BanOrUnbanUserModalState; removeModeratorsModal: RemoveModeratorsModalState; + addModeratorsModal: AddModeratorsModalState; groupNameModal: UpdateGroupNameModalState; groupMembersModal: UpdateGroupMembersModalState; userDetailsModal: UserDetailsModalState; @@ -45,6 +52,7 @@ export const initialModalState: ModalState = { inviteContactModal: null, addModeratorsModal: null, removeModeratorsModal: null, + banOrUnbanUserModal: null, groupNameModal: null, groupMembersModal: null, userDetailsModal: null, @@ -67,6 +75,9 @@ const ModalSlice = createSlice({ updateInviteContactModal(state, action: PayloadAction) { return { ...state, inviteContactModal: action.payload }; }, + updateBanOrUnbanUserModal(state, action: PayloadAction) { + return { ...state, banOrUnbanUserModal: action.payload }; + }, updateAddModeratorsModal(state, action: PayloadAction) { return { ...state, addModeratorsModal: action.payload }; }, @@ -122,5 +133,6 @@ export const { adminLeaveClosedGroup, sessionPassword, updateDeleteAccountModal, + updateBanOrUnbanUserModal, } = actions; export const modalReducer = reducer; diff --git a/ts/state/selectors/modal.ts b/ts/state/selectors/modal.ts index f14980055..87ebed3bd 100644 --- a/ts/state/selectors/modal.ts +++ b/ts/state/selectors/modal.ts @@ -4,6 +4,7 @@ import { StateType } from '../reducer'; import { AddModeratorsModalState, AdminLeaveClosedGroupModalState, + BanOrUnbanUserModalState, ChangeNickNameModalState, ConfirmModalState, DeleteAccountModalState, @@ -43,6 +44,11 @@ export const getRemoveModeratorsModal = createSelector( (state: ModalState): RemoveModeratorsModalState => state.removeModeratorsModal ); +export const getBanOrUnbanUserModalState = createSelector( + getModal, + (state: ModalState): BanOrUnbanUserModalState => state.banOrUnbanUserModal +); + export const getUpdateGroupNameModal = createSelector( getModal, (state: ModalState): UpdateGroupNameModalState => state.groupNameModal diff --git a/ts/types/LocalizerKeys.ts b/ts/types/LocalizerKeys.ts index 0cd644596..9f3495947 100644 --- a/ts/types/LocalizerKeys.ts +++ b/ts/types/LocalizerKeys.ts @@ -373,7 +373,7 @@ export type LocalizerKeys = | 'password' | 'usersCanShareTheir...' | 'timestampFormat_M' - | 'banUserAndDeleteAllConfirm' + | 'banAndDeleteAllDialogTitle' | 'nicknamePlaceholder' | 'linkPreviewsTitle' | 'continue'