Add blocked/block experience to chats

This commit is contained in:
Chewbacca 2022-10-25 11:40:14 -04:00
parent 01c40eb42d
commit 70957fe540
4 changed files with 96 additions and 28 deletions

View file

@ -1,13 +1,23 @@
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { HStack, IconButton, Stack, Text, Textarea } from 'soapbox/components/ui';
import { unblockAccount } from 'soapbox/actions/accounts';
import { openModal } from 'soapbox/actions/modals';
import { Button, HStack, IconButton, Stack, Text, Textarea } from 'soapbox/components/ui';
import { useChatContext } from 'soapbox/contexts/chat-context';
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
const messages = defineMessages({
placeholder: { id: 'chat.input.placeholder', defaultMessage: 'Type a message' },
send: { id: 'chat.actions.send', defaultMessage: 'Send' },
failedToSend: { id: 'chat.failed_to_send', defaultMessage: 'Message failed to send.' },
retry: { id: 'chat.retry', defaultMessage: 'Retry?' },
blockedBy: { id: 'chat_message_list.blockedBy', defaultMessage: 'You are blocked by' },
blocked: { id: 'chat_message_list.blocked', defaultMessage: 'You blocked this user' },
unblock: { id: 'chat_composer.unblock', defaultMessage: 'Unblock' },
unblockMessage: { id: 'chat_settings.unblock.message', defaultMessage: 'Unblocking will allow you to resume messaging with the user.' },
unblockHeading: { id: 'chat_settings.unblock.heading', defaultMessage: 'Unblock @{acct}' },
unblockConfirm: { id: 'chat_settings.unblock.confirm', defaultMessage: 'Unblock' },
});
interface IChatComposer extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'onKeyDown' | 'onChange' | 'disabled'> {
@ -25,10 +35,55 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
hasErrorSubmittingMessage = false,
disabled = false,
}, ref) => {
const dispatch = useAppDispatch();
const intl = useIntl();
const { chat } = useChatContext();
const isBlocked = useAppSelector((state) => state.getIn(['relationships', chat?.account?.id, 'blocked_by']));
const isBlocking = useAppSelector((state) => state.getIn(['relationships', chat?.account?.id, 'blocking']));
const isSubmitDisabled = disabled || value.length === 0;
const handleUnblockUser = () => {
dispatch(openModal('CONFIRM', {
heading: intl.formatMessage(messages.unblockHeading, { acct: chat?.account.acct }),
message: intl.formatMessage(messages.unblockMessage),
confirm: intl.formatMessage(messages.unblockConfirm),
confirmationTheme: 'primary',
onConfirm: () => dispatch(unblockAccount(chat?.account.id as string)),
}));
};
if (isBlocking) {
return (
<div className='mt-auto p-6 shadow-3xl dark:border-t-2 dark:border-solid dark:border-gray-800'>
<Stack space={3} alignItems='center'>
<Text align='center' theme='muted'>
{intl.formatMessage(messages.blocked)}
</Text>
<Button theme='secondary' onClick={handleUnblockUser}>
{intl.formatMessage(messages.unblock)}
</Button>
</Stack>
</div>
);
}
if (isBlocked) {
return (
<div className='mt-auto p-6 shadow-3xl dark:border-t-2 dark:border-solid dark:border-gray-800'>
<Text align='center' theme='muted'>
<>
<Text tag='span' theme='inherit'>{intl.formatMessage(messages.blockedBy)}</Text>
{' '}
<Text tag='span' theme='inherit'>@{chat?.account?.acct}</Text>
</>
</Text>
</div>
);
}
return (
<div className='mt-auto pt-4 px-4 shadow-3xl'>
<HStack alignItems='center' justifyContent='between' space={4}>

View file

@ -35,7 +35,6 @@ const messages = defineMessages({
copy: { id: 'chats.actions.copy', defaultMessage: 'Copy' },
report: { id: 'chats.actions.report', defaultMessage: 'Report' },
deleteForMe: { id: 'chats.actions.deleteForMe', defaultMessage: 'Delete for me' },
blockedBy: { id: 'chat_message_list.blockedBy', defaultMessage: 'You are blocked by' },
networkFailureTitle: { id: 'chat_message_list.network_failure.title', defaultMessage: 'Whoops!' },
networkFailureSubtitle: { id: 'chat_message_list.network_failure.subtitle', defaultMessage: 'We encountered a network failure.' },
networkFailureAction: { id: 'chat_message_list.network_failure.action', defaultMessage: 'Try again' },
@ -92,7 +91,6 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, autosize }) => {
const formattedChatMessages = chatMessages || [];
const me = useAppSelector((state) => state.me);
const isBlocked = useAppSelector((state) => state.getIn(['relationships', chat.account.id, 'blocked_by']));
const handleDeleteMessage = useMutation((chatMessageId: string) => deleteChatMessage(chatMessageId), {
onSettled: () => {
@ -351,23 +349,6 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, autosize }) => {
}
}, [formattedChatMessages.length]);
if (isBlocked) {
return (
<Stack alignItems='center' justifyContent='center' className='h-full flex-grow'>
<Stack alignItems='center' space={2}>
<Avatar src={chat.account.avatar} size={75} />
<Text align='center'>
<>
<Text tag='span'>{intl.formatMessage(messages.blockedBy)}</Text>
{' '}
<Text tag='span' theme='primary'>@{chat.account.acct}</Text>
</>
</Text>
</Stack>
</Stack>
);
}
if (isError) {
return (
<Stack alignItems='center' justifyContent='center' className='h-full flex-grow'>

View file

@ -1,12 +1,12 @@
import React, { useRef } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { blockAccount } from 'soapbox/actions/accounts';
import { blockAccount, unblockAccount } from 'soapbox/actions/accounts';
import { openModal } from 'soapbox/actions/modals';
import { Avatar, Divider, HStack, Icon, IconButton, Menu, MenuButton, MenuItem, MenuList, Stack, Text } from 'soapbox/components/ui';
import VerificationBadge from 'soapbox/components/verification_badge';
import { useChatContext } from 'soapbox/contexts/chat-context';
import { useAppDispatch, useOwnAccount } from 'soapbox/hooks';
import { useAppDispatch, useAppSelector, useOwnAccount } from 'soapbox/hooks';
import { useChatActions } from 'soapbox/queries/chats';
import Chat from '../../chat';
@ -18,10 +18,14 @@ const messages = defineMessages({
blockMessage: { id: 'chat_settings.block.message', defaultMessage: 'Blocking will prevent this profile from direct messaging you and viewing your content. You can unblock later.' },
blockHeading: { id: 'chat_settings.block.heading', defaultMessage: 'Block @{acct}' },
blockConfirm: { id: 'chat_settings.block.confirm', defaultMessage: 'Block' },
unblockMessage: { id: 'chat_settings.unblock.message', defaultMessage: 'Unblocking will allow you to resume messaging with the user.' },
unblockHeading: { id: 'chat_settings.unblock.heading', defaultMessage: 'Unblock @{acct}' },
unblockConfirm: { id: 'chat_settings.unblock.confirm', defaultMessage: 'Unblock' },
leaveMessage: { id: 'chat_settings.leave.message', defaultMessage: 'Are you sure you want to leave this chat? Messages will be deleted for you and this chat will be removed from your inbox.' },
leaveHeading: { id: 'chat_settings.leave.heading', defaultMessage: 'Leave Chat' },
leaveConfirm: { id: 'chat_settings.leave.confirm', defaultMessage: 'Leave Chat' },
blockUser: { id: 'chat_settings.options.block_user', defaultMessage: 'Block @{acct}' },
unblockUser: { id: 'chat_settings.options.unblock_user', defaultMessage: 'Unblock @{acct}' },
reportUser: { id: 'chat_settings.options.report_user', defaultMessage: 'Report @{acct}' },
leaveChat: { id: 'chat_settings.options.leave_chat', defaultMessage: 'Leave Chat' },
});
@ -36,6 +40,8 @@ const ChatPageMain = () => {
const { chat, setChat } = useChatContext();
const { deleteChat } = useChatActions(chat?.id as string);
const isBlocking = useAppSelector((state) => state.getIn(['relationships', chat?.account?.id, 'blocking']));
const handleBlockUser = () => {
dispatch(openModal('CONFIRM', {
heading: intl.formatMessage(messages.blockHeading, { acct: chat?.account.acct }),
@ -46,6 +52,16 @@ const ChatPageMain = () => {
}));
};
const handleUnblockUser = () => {
dispatch(openModal('CONFIRM', {
heading: intl.formatMessage(messages.unblockHeading, { acct: chat?.account.acct }),
message: intl.formatMessage(messages.unblockMessage),
confirm: intl.formatMessage(messages.unblockConfirm),
confirmationTheme: 'primary',
onConfirm: () => dispatch(unblockAccount(chat?.account.id as string)),
}));
};
const handleLeaveChat = () => {
dispatch(openModal('CONFIRM', {
heading: intl.formatMessage(messages.leaveHeading),
@ -122,12 +138,12 @@ const ChatPageMain = () => {
<Stack space={2}>
<MenuItem
as='button'
onSelect={handleBlockUser}
onSelect={isBlocking ? handleUnblockUser : handleBlockUser}
className='!px-0 hover:!bg-transparent'
>
<div className='w-full flex items-center space-x-2 font-bold text-sm text-primary-500 dark:text-accent-blue'>
<Icon src={require('@tabler/icons/ban.svg')} className='w-5 h-5' />
<span>{intl.formatMessage(messages.blockUser, { acct: chat.account.acct })}</span>
<span>{intl.formatMessage(isBlocking ? messages.unblockUser : messages.blockUser, { acct: chat.account.acct })}</span>
</div>
</MenuItem>

View file

@ -1,11 +1,11 @@
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { blockAccount } from 'soapbox/actions/accounts';
import { blockAccount, unblockAccount } from 'soapbox/actions/accounts';
import { openModal } from 'soapbox/actions/modals';
import { Avatar, Divider, HStack, Icon, Stack, Text } from 'soapbox/components/ui';
import { useChatContext } from 'soapbox/contexts/chat-context';
import { useAppDispatch } from 'soapbox/hooks';
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
import { useChatActions } from 'soapbox/queries/chats';
import ChatPaneHeader from './chat-pane-header';
@ -14,11 +14,15 @@ const messages = defineMessages({
blockMessage: { id: 'chat_settings.block.message', defaultMessage: 'Blocking will prevent this profile from direct messaging you and viewing your content. You can unblock later.' },
blockHeading: { id: 'chat_settings.block.heading', defaultMessage: 'Block @{acct}' },
blockConfirm: { id: 'chat_settings.block.confirm', defaultMessage: 'Block' },
unblockMessage: { id: 'chat_settings.unblock.message', defaultMessage: 'Unblocking will allow you to resume messaging with the user.' },
unblockHeading: { id: 'chat_settings.unblock.heading', defaultMessage: 'Unblock @{acct}' },
unblockConfirm: { id: 'chat_settings.unblock.confirm', defaultMessage: 'Unblock' },
leaveMessage: { id: 'chat_settings.leave.message', defaultMessage: 'Are you sure you want to leave this chat? Messages will be deleted for you and this chat will be removed from your inbox.' },
leaveHeading: { id: 'chat_settings.leave.heading', defaultMessage: 'Leave Chat' },
leaveConfirm: { id: 'chat_settings.leave.confirm', defaultMessage: 'Leave Chat' },
title: { id: 'chat_settings.title', defaultMessage: 'Chat Details' },
blockUser: { id: 'chat_settings.options.block_user', defaultMessage: 'Block @{acct}' },
unblockUser: { id: 'chat_settings.options.unblock_user', defaultMessage: 'Unblock @{acct}' },
leaveChat: { id: 'chat_settings.options.leave_chat', defaultMessage: 'Leave Chat' },
});
@ -29,6 +33,8 @@ const ChatSettings = () => {
const { chat, setEditing, toggleChatPane } = useChatContext();
const { deleteChat } = useChatActions(chat?.id as string);
const isBlocking = useAppSelector((state) => state.getIn(['relationships', chat?.account?.id, 'blocking']));
const closeSettings = () => setEditing(false);
const minimizeChatPane = () => {
@ -46,6 +52,16 @@ const ChatSettings = () => {
}));
};
const handleUnblockUser = () => {
dispatch(openModal('CONFIRM', {
heading: intl.formatMessage(messages.unblockHeading, { acct: chat?.account.acct }),
message: intl.formatMessage(messages.unblockMessage),
confirm: intl.formatMessage(messages.unblockConfirm),
confirmationTheme: 'primary',
onConfirm: () => dispatch(unblockAccount(chat?.account.id as string)),
}));
};
const handleLeaveChat = () => {
dispatch(openModal('CONFIRM', {
heading: intl.formatMessage(messages.leaveHeading),
@ -94,9 +110,9 @@ const ChatSettings = () => {
<Divider />
<Stack space={5}>
<button onClick={handleBlockUser} className='w-full flex items-center space-x-2 font-bold text-sm text-primary-600 dark:text-accent-blue'>
<button onClick={isBlocking ? handleUnblockUser : handleBlockUser} className='w-full flex items-center space-x-2 font-bold text-sm text-primary-600 dark:text-accent-blue'>
<Icon src={require('@tabler/icons/ban.svg')} className='w-5 h-5' />
<span>{intl.formatMessage(messages.blockUser, { acct: chat.account.acct })}</span>
<span>{intl.formatMessage(isBlocking ? messages.unblockUser : messages.blockUser, { acct: chat.account.acct })}</span>
</button>
<button onClick={handleLeaveChat} className='w-full flex items-center space-x-2 font-bold text-sm text-danger-600'>