Add chat settings to chat main page

chats-router
Justin 2022-09-13 14:11:22 -04:00
rodzic 0ae515ef18
commit 45afb665b9
5 zmienionych plików z 159 dodań i 31 usunięć

Wyświetl plik

@ -1,5 +1,5 @@
[data-reach-menu-popover] {
@apply origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white dark:bg-gray-900 dark:ring-2 dark:ring-primary-700 focus:outline-none;
@apply origin-top-right absolute mt-2 rounded-md shadow-lg bg-white dark:bg-gray-900 dark:ring-2 dark:ring-primary-700 focus:outline-none;
z-index: 1003;
}

Wyświetl plik

@ -5,28 +5,36 @@ import {
MenuItems,
MenuPopover,
MenuLink,
MenuPopoverProps,
MenuListProps,
} from '@reach/menu-button';
import { positionDefault, positionRight } from '@reach/popover';
import classNames from 'clsx';
import React from 'react';
import './menu.css';
interface IMenuList extends Omit<MenuPopoverProps, 'position'> {
interface IMenuList extends Omit<MenuListProps, 'position'> {
/** Position of the dropdown menu. */
position?: 'left' | 'right'
className?: string
}
/** Renders children as a dropdown menu. */
const MenuList: React.FC<IMenuList> = (props) => (
<MenuPopover position={props.position === 'left' ? positionDefault : positionRight}>
<MenuItems
onKeyDown={(event) => event.nativeEvent.stopImmediatePropagation()}
className='py-1 bg-white dark:bg-primary-900 rounded-lg shadow-menu'
{...props}
/>
</MenuPopover>
);
const MenuList: React.FC<IMenuList> = (props) => {
const { position, className, ...filteredProps } = props;
return (
<MenuPopover position={props.position === 'left' ? positionDefault : positionRight}>
<MenuItems
onKeyDown={(event) => event.nativeEvent.stopImmediatePropagation()}
className={
classNames(className, 'py-1 bg-white dark:bg-primary-900 rounded-lg shadow-menu')
}
{...filteredProps}
/>
</MenuPopover>
);
};
/** Divides menu items. */
const MenuDivider = () => <hr />;

Wyświetl plik

@ -165,11 +165,11 @@ const Header: React.FC<IHeader> = ({ account }) => {
if (account.relationship?.endorsed) {
dispatch(unpinAccount(account.id))
.then(() => dispatch(snackbar.success(intl.formatMessage(messages.userUnendorsed, { acct: account.acct }))))
.catch(() => {});
.catch(() => { });
} else {
dispatch(pinAccount(account.id))
.then(() => dispatch(snackbar.success(intl.formatMessage(messages.userEndorsed, { acct: account.acct }))))
.catch(() => {});
.catch(() => { });
}
};
@ -218,7 +218,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
dispatch(verifyUser(account.id))
.then(() => dispatch(snackbar.success(message)))
.catch(() => {});
.catch(() => { });
};
const onUnverifyUser = () => {
@ -226,7 +226,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
dispatch(unverifyUser(account.id))
.then(() => dispatch(snackbar.success(message)))
.catch(() => {});
.catch(() => { });
};
const onSetDonor = () => {
@ -234,7 +234,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
dispatch(setDonor(account.id))
.then(() => dispatch(snackbar.success(message)))
.catch(() => {});
.catch(() => { });
};
const onRemoveDonor = () => {
@ -242,7 +242,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
dispatch(removeDonor(account.id))
.then(() => dispatch(snackbar.success(message)))
.catch(() => {});
.catch(() => { });
};
const onPromoteToAdmin = () => {
@ -250,7 +250,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
dispatch(promoteToAdmin(account.id))
.then(() => dispatch(snackbar.success(message)))
.catch(() => {});
.catch(() => { });
};
const onPromoteToModerator = () => {
@ -259,7 +259,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
dispatch(promoteToModerator(account.id))
.then(() => dispatch(snackbar.success(message)))
.catch(() => {});
.catch(() => { });
};
const onDemoteToUser = () => {
@ -267,7 +267,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
dispatch(demoteToUser(account.id))
.then(() => dispatch(snackbar.success(message)))
.catch(() => {});
.catch(() => { });
};
const onSuggestUser = () => {
@ -275,7 +275,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
dispatch(suggestUsers([account.id]))
.then(() => dispatch(snackbar.success(message)))
.catch(() => {});
.catch(() => { });
};
const onUnsuggestUser = () => {
@ -283,7 +283,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
dispatch(unsuggestUsers([account.id]))
.then(() => dispatch(snackbar.success(message)))
.catch(() => {});
.catch(() => { });
};
const onRemoveFromFollowers = () => {
@ -778,7 +778,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
children={null}
/>
<MenuList>
<MenuList className='w-56'>
{menu.map((menuItem, idx) => {
if (typeof menuItem?.text === 'undefined') {
return <MenuDivider key={idx} />;

Wyświetl plik

@ -2,29 +2,34 @@ import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useHistory } from 'react-router-dom';
import { blockAccount } from 'soapbox/actions/accounts';
import { launchChat } from 'soapbox/actions/chats';
import { openModal } from 'soapbox/actions/modals';
import { initReport } from 'soapbox/actions/reports';
import AccountSearch from 'soapbox/components/account_search';
import { Card, CardTitle, Stack } from 'soapbox/components/ui';
import List, { ListItem } from 'soapbox/components/list';
import { Avatar, Card, CardTitle, Divider, HStack, Icon, IconButton, Menu, MenuButton, MenuItem, MenuList, Stack, Text, Toggle } from 'soapbox/components/ui';
import VerificationBadge from 'soapbox/components/verification_badge';
import { useChatContext } from 'soapbox/contexts/chat-context';
import { useAppDispatch } from 'soapbox/hooks';
import { useChat, useChatSilences } from 'soapbox/queries/chats';
import Chat from './chat';
import ChatList from './chat-list';
import ChatListItem from './chat-list-item';
const messages = defineMessages({
title: { id: 'column.chats', defaultMessage: 'Messages' },
searchPlaceholder: { id: 'chats.search_placeholder', defaultMessage: 'Start a chat with…' },
});
const ChatPage = () => {
const intl = useIntl();
const dispatch = useAppDispatch();
const history = useHistory();
const { isSilenced, handleSilence } = useChatSilences();
const { chat, setChat } = useChatContext();
const { deleteChat } = useChat(chat?.id as string);
const handleSuggestion = (accountId: string) => {
dispatch(launchChat(accountId, history, true));
@ -35,6 +40,32 @@ const ChatPage = () => {
setChat(chat);
};
const handleBlockUser = () => {
dispatch(openModal('CONFIRM', {
heading: `Block @${chat?.account.acct}`,
message: 'Blocking will prevent this profile from direct messaging you and viewing your content. You can unblock later.',
confirm: 'Block',
confirmationTheme: 'primary',
onConfirm: () => dispatch(blockAccount(chat?.account.id as string)),
}));
};
const handleLeaveChat = () => {
dispatch(openModal('CONFIRM', {
heading: 'Leave Chat',
message: 'Are you sure you want to leave this chat? This conversation will be removed from your inbox.',
confirm: 'Leave Chat',
confirmationTheme: 'primary',
onConfirm: () => {
deleteChat.mutate();
},
}));
};
const handleReportChat = () => {
dispatch(initReport(chat?.account as any));
};
return (
<Card className='p-0 h-[calc(100vh-176px)] overflow-hidden' variant='rounded'>
<div className='grid grid-cols-9 overflow-hidden h-full dark:divide-x-2 dark:divide-solid dark:divide-gray-800'>
@ -57,7 +88,96 @@ const ChatPage = () => {
<Stack className='col-span-6 h-full overflow-hidden'>
{chat && (
<Stack className='h-full overflow-hidden'>
<ChatListItem chat={chat} onClick={() => { }} />
<HStack alignItems='center' justifyContent='between' space={2} className='px-4 py-2 w-full'>
<HStack alignItems='center' space={2} className='overflow-hidden'>
<Avatar src={chat.account?.avatar} size={40} className='flex-none' />
<Stack alignItems='start' className='overflow-hidden'>
<div className='flex items-center space-x-1 flex-grow w-full'>
<Text weight='bold' size='sm' align='left' truncate>{chat.account?.display_name || `@${chat.account.username}`}</Text>
{!chat.account?.verified && <VerificationBadge />}
</div>
<Text
align='left'
size='sm'
weight='medium'
theme='muted'
truncate
className='w-full'
>
{chat.account.acct}
</Text>
</Stack>
</HStack>
<Menu>
<MenuButton
as={IconButton}
src={require('@tabler/icons/info-circle.svg')}
iconClassName='w-5 h-5 text-gray-600'
children={null}
/>
<MenuList className='w-80 py-6'>
<Stack space={4} className='w-5/6 mx-auto'>
<Stack alignItems='center' space={2}>
<Avatar src={chat.account.avatar_static} size={75} />
<Stack>
<Text size='lg' weight='semibold' align='center'>{chat.account.display_name}</Text>
<Text theme='primary' align='center'>@{chat.account.acct}</Text>
</Stack>
</Stack>
<Divider />
<List>
<ListItem label='Silence notifications'>
<Toggle checked={isSilenced} onChange={handleSilence} />
</ListItem>
</List>
<Divider />
<Stack space={2}>
<MenuItem
as='button'
onSelect={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>Block @{chat.account.acct}</span>
</div>
</MenuItem>
<MenuItem
as='button'
onSelect={handleReportChat}
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/flag.svg')} className='w-5 h-5' />
<span>Report @{chat.account.acct}</span>
</div>
</MenuItem>
<MenuItem
as='button'
onSelect={handleLeaveChat}
className='!px-0 hover:!bg-transparent'
>
<div className='w-full flex items-center space-x-2 font-bold text-sm text-danger-600 dark:text-danger-500'>
<Icon src={require('@tabler/icons/logout.svg')} className='w-5 h-5' />
<span>Leave chat</span>
</div>
</MenuItem>
</Stack>
</Stack>
</MenuList>
</Menu>
</HStack>
<div className='h-full overflow-hidden'>
<Chat className='h-full overflow-hidden' chat={chat} />
</div>
@ -69,4 +189,4 @@ const ChatPage = () => {
);
};
export default ChatPage;
export default ChatPage;

Wyświetl plik

@ -110,7 +110,7 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
{menu.map((menuItem, idx) => {
if (menuItem.toggle) {
return (
<div key={idx} className='flex flex-row items-center justify-between px-4 py-1 text-sm text-gray-700 dark:text-gray-400'>
<div key={idx} className='flex flex-row items-center justify-between space-x-4 px-4 py-1 text-sm text-gray-700 dark:text-gray-400'>
<span>{menuItem.text}</span>
{menuItem.toggle}