Finish i18n for DMs

chats-router
Justin 2022-09-14 10:35:32 -04:00
rodzic a60d6770af
commit 6240ea5a23
10 zmienionych plików z 140 dodań i 101 usunięć

Wyświetl plik

@ -17,7 +17,7 @@ const ChatListItem: React.FC<IChatListItemInterface> = ({ chat, onClick }) => {
key={chat.id}
type='button'
onClick={() => onClick(chat)}
className='px-4 py-2 w-full flex flex-col hover:bg-gray-100 dark:hover:bg-gray-800'
className='px-4 py-2 w-full flex flex-col hover:bg-gray-100 dark:hover:bg-gray-800 focus:shadow-inset-ring'
data-testid='chat'
>
<HStack alignItems='center' justifyContent='between' space={2} className='w-full'>

Wyświetl plik

@ -1,16 +1,15 @@
import classNames from 'clsx';
import React, { useRef, useState } from 'react';
import { useDispatch } from 'react-redux';
import { Virtuoso } from 'react-virtuoso';
import { fetchChats } from 'soapbox/actions/chats';
import PullToRefresh from 'soapbox/components/pull-to-refresh';
import { Stack } from 'soapbox/components/ui';
import PlaceholderChat from 'soapbox/features/placeholder/components/placeholder-chat';
import { useAppDispatch } from 'soapbox/hooks';
import { useChats } from 'soapbox/queries/chats';
import ChatListItem from './chat-list-item';
import Blankslate from './chat-pane/blankslate';
interface IChatList {
onClickChat: (chat: any) => void,
@ -20,7 +19,7 @@ interface IChatList {
}
const ChatList: React.FC<IChatList> = ({ onClickChat, useWindowScroll = false, searchValue, fade }) => {
const dispatch = useDispatch();
const dispatch = useAppDispatch();
const chatListRef = useRef(null);
@ -37,23 +36,15 @@ const ChatList: React.FC<IChatList> = ({ onClickChat, useWindowScroll = false, s
}
};
const handleRefresh = () => {
return dispatch(fetchChats()) as any;
};
const handleRefresh = () => dispatch(fetchChats()) as any;
const renderEmpty = () => {
if (isFetching) {
return (
<Stack space={2}>
<PlaceholderChat />
<PlaceholderChat />
<PlaceholderChat />
</Stack>
);
} else {
return <Blankslate />;
}
};
const renderEmpty = () => (
<Stack space={2}>
<PlaceholderChat />
<PlaceholderChat />
<PlaceholderChat />
</Stack>
);
return (
<div className='relative h-full'>

Wyświetl plik

@ -1,5 +1,6 @@
import classNames from 'clsx';
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { openModal } from 'soapbox/actions/modals';
import { initReport } from 'soapbox/actions/reports';
@ -8,21 +9,31 @@ import { useChatContext } from 'soapbox/contexts/chat-context';
import { useAppDispatch } from 'soapbox/hooks';
import { useChat } from 'soapbox/queries/chats';
const messages = defineMessages({
leaveChatHeading: { id: 'chat_message_list_intro.leave_chat.heading', defaultMessage: 'Leave Chat' },
leaveChatMessage: { id: 'chat_message_list_intro.leave_chat.message', defaultMessage: 'Are you sure you want to leave this chat? This conversation will be removed from your inbox.' },
leaveChatConfirm: { id: 'chat_message_list_intro.leave_chat.confirm', defaultMessage: 'Leave Chat' },
intro: { id: 'chat_message_list_intro.intro', defaultMessage: 'wants to start a chat with you' },
accept: { id: 'chat_message_list_intro.actions.accept', defaultMessage: 'Accept' },
leaveChat: { id: 'chat_message_list_intro.actions.leave_chat', defaultMessage: 'Leave chat' },
report: { id: 'chat_message_list_intro.actions.report', defaultMessage: 'Report' },
messageLifespan: { id: 'chat_message_list_intro.actions.message_lifespan', defaultMessage: 'Messages older than 15 days are deleted.' },
});
const ChatMessageListIntro = () => {
const dispatch = useAppDispatch();
const intl = useIntl();
const { chat, needsAcceptance } = useChatContext();
const { acceptChat, deleteChat } = useChat(chat?.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',
heading: intl.formatMessage(messages.leaveChatHeading),
message: intl.formatMessage(messages.leaveChatMessage),
confirm: intl.formatMessage(messages.leaveChatConfirm),
confirmationTheme: 'primary',
onConfirm: () => {
deleteChat.mutate();
},
onConfirm: () => deleteChat.mutate(),
}));
};
@ -55,7 +66,7 @@ const ChatMessageListIntro = () => {
<>
<Text tag='span' weight='semibold'>@{chat.account.acct}</Text>
{' '}
<Text tag='span'>wants to start a chat with you</Text>
<Text tag='span'>{intl.formatMessage(messages.intro)}</Text>
</>
) : (
<Text tag='span' weight='semibold'>@{chat.account.acct}</Text>
@ -68,13 +79,10 @@ const ChatMessageListIntro = () => {
<Button
theme='primary'
block
onClick={() => {
acceptChat.mutate();
// inputRef?.current?.focus();
}}
onClick={() => acceptChat.mutate()}
disabled={acceptChat.isLoading}
>
Accept
{intl.formatMessage(messages.accept)}
</Button>
<HStack alignItems='center' space={2} className='w-full'>
@ -83,7 +91,7 @@ const ChatMessageListIntro = () => {
block
onClick={handleLeaveChat}
>
Leave chat
{intl.formatMessage(messages.leaveChat)}
</Button>
<Button
@ -91,7 +99,7 @@ const ChatMessageListIntro = () => {
block
onClick={handleReportChat}
>
Report
{intl.formatMessage(messages.report)}
</Button>
</HStack>
</Stack>
@ -99,7 +107,7 @@ const ChatMessageListIntro = () => {
<HStack justifyContent='center' alignItems='center' space={1} className='flex-shrink-0'>
<Icon src={require('@tabler/icons/clock.svg')} className='text-gray-600 w-4 h-4' />
<Text size='sm' theme='muted'>
Messages older than 15 days are deleted.
{intl.formatMessage(messages.messageLifespan)}
</Text>
</HStack>
)}

Wyświetl plik

@ -30,6 +30,10 @@ const messages = defineMessages({
more: { id: 'chats.actions.more', defaultMessage: 'More' },
delete: { id: 'chats.actions.delete', defaultMessage: 'Delete' },
copy: { id: 'chats.actions.copy', defaultMessage: 'Copy' },
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' },
});
type TimeFormat = 'today' | 'date';
@ -398,7 +402,7 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, autosize }) => {
<Avatar src={chat.account.avatar} size={75} />
<Text align='center'>
<>
<Text tag='span'>You are blocked by</Text>
<Text tag='span'>{intl.formatMessage(messages.blockedBy)}</Text>
{' '}
<Text tag='span' theme='primary'>@{chat.account.acct}</Text>
</>
@ -413,15 +417,17 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, autosize }) => {
<Stack alignItems='center' justifyContent='center' className='h-full flex-grow'>
<Stack space={4}>
<Stack space={1}>
<Text size='lg' weight='bold' align='center'>Whoops!</Text>
<Text size='lg' weight='bold' align='center'>
{intl.formatMessage(messages.networkFailureTitle)}
</Text>
<Text theme='muted' align='center'>
We encountered a network failure.
{intl.formatMessage(messages.networkFailureSubtitle)}
</Text>
</Stack>
<div className='mx-auto'>
<Button theme='primary' onClick={() => refetch()}>
Try again
{intl.formatMessage(messages.networkFailureAction)}
</Button>
</div>
</Stack>

Wyświetl plik

@ -1,16 +1,42 @@
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { Button, Stack, Text } from 'soapbox/components/ui';
const Blankslate = () => (
<Stack justifyContent='center' alignItems='center' space={4} className='px-4 h-full'>
<Stack space={2}>
<Text weight='semibold' size='xl' align='center'>No messages yet</Text>
<Text theme='muted' align='center'>You can start a conversation with anyone that follows you.</Text>
</Stack>
const messages = defineMessages({
title: { id: 'chat_search.empty_results_blankslate.title', defaultMessage: 'No messages yet' },
body: { id: 'chat_search.empty_results_blankslate.body', defaultMessage: 'You can start a conversation with anyone that follows you.' },
action: { id: 'chat_search.empty_results_blankslate.action', defaultMessage: 'Message someone' },
});
{/* <Button theme='primary'>Message someone</Button> */}
</Stack>
);
interface IBlankslate {
onSearch(): void
}
const Blankslate = ({ onSearch }: IBlankslate) => {
const intl = useIntl();
return (
<Stack alignItems='center' justifyContent='center' className='h-full flex-grow'>
<Stack space={4}>
<Stack space={1} className='max-w-[85%] mx-auto'>
<Text size='lg' weight='bold' align='center'>
{intl.formatMessage(messages.title)}
</Text>
<Text theme='muted' align='center'>
{intl.formatMessage(messages.body)}
</Text>
</Stack>
<div className='mx-auto'>
<Button theme='primary' onClick={onSearch}>
{intl.formatMessage(messages.action)}
</Button>
</div>
</Stack>
</Stack>
);
};
export default Blankslate;

Wyświetl plik

@ -1,31 +1,27 @@
import { useMutation } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import sumBy from 'lodash/sumBy';
import React, { useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import snackbar from 'soapbox/actions/snackbar';
import { Avatar, Button, HStack, Icon, Input, Stack, Text } from 'soapbox/components/ui';
import VerificationBadge from 'soapbox/components/verification_badge';
import { Icon, Input, Stack } from 'soapbox/components/ui';
import { useChatContext } from 'soapbox/contexts/chat-context';
import { useAppDispatch, useDebounce } from 'soapbox/hooks';
import { useDebounce } from 'soapbox/hooks';
import { IChat, useChats } from 'soapbox/queries/chats';
import { queryClient } from 'soapbox/queries/client';
import useAccountSearch from 'soapbox/queries/search';
import ChatList from '../chat-list';
import ChatPaneHeader from '../chat-pane-header';
import ChatSearch from '../chat-search/chat-search';
import EmptyResultsBlankslate from '../chat-search/empty-results-blankslate';
import ChatWindow from '../chat-window';
import { Pane } from '../ui';
import Blankslate from './blankslate';
const messages = defineMessages({
searchPlaceholder: { id: 'chats.search_placeholder', defaultMessage: 'Search inbox' },
});
const ChatPane = () => {
const intl = useIntl();
const dispatch = useAppDispatch();
const debounce = useDebounce;
const [value, setValue] = useState<string>();
@ -33,17 +29,10 @@ const ChatPane = () => {
const { chat, setChat, isOpen, isSearching, setSearching, toggleChatPane } = useChatContext();
const { chatsQuery: { data: chats } } = useChats(debouncedValue);
// const chats: IChat[] = [];
// Screens
// 1. Search + Chats
// 2. Search + empty
// 3. User search
const unreadCount = sumBy(chats, (chat) => chat.unread);
const hasSearchValue = Number(value?.length) > 0;
const hasSearchValue = Number(debouncedValue?.length) > 0;
const handleClickChat = (chat: IChat) => {
setChat(chat);
@ -88,28 +77,13 @@ const ChatPane = () => {
fade
/>
) : (
<Text>no results</Text>
<EmptyResultsBlankslate />
)}
</Stack>
);
} else if (chats?.length === 0) {
return (
<Stack alignItems='center' justifyContent='center' className='h-full flex-grow'>
<Stack space={4}>
<Stack space={1} className='max-w-[85%] mx-auto'>
<Text size='lg' weight='bold' align='center'>No messages yet</Text>
<Text theme='muted' align='center'>
You can start a conversation with anyone that follows you.
</Text>
</Stack>
<div className='mx-auto'>
<Button theme='primary' onClick={() => setSearching(true)}>
Message someone
</Button>
</div>
</Stack>
</Stack>
<Blankslate onSearch={() => setSearching(true)} />
);
}
};

Wyświetl plik

@ -1,14 +1,26 @@
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { Stack, Text } from 'soapbox/components/ui';
const Blankslate = () => (
<Stack justifyContent='center' alignItems='center' space={2} className='h-full w-2/3 mx-auto'>
<Text weight='bold' size='lg' align='center'>Search followers</Text>
<Text theme='muted' align='center'>
You can start a conversation with anyone that follows you.
</Text>
</Stack>
);
const messages = defineMessages({
title: { id: 'chat_search.blankslate.title', defaultMessage: 'Search followers' },
body: { id: 'chat_search.blankslate.body', defaultMessage: 'You can start a conversation with anyone that follows you.' },
});
const Blankslate = () => {
const intl = useIntl();
return (
<Stack justifyContent='center' alignItems='center' space={2} className='h-full w-2/3 mx-auto'>
<Text weight='bold' size='lg' align='center'>
{intl.formatMessage(messages.title)}
</Text>
<Text theme='muted' align='center'>
{intl.formatMessage(messages.body)}
</Text>
</Stack>
);
};
export default Blankslate;

Wyświetl plik

@ -1,6 +1,7 @@
import { useMutation } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import React, { useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import snackbar from 'soapbox/actions/snackbar';
import { HStack, Icon, Input, Stack, Text } from 'soapbox/components/ui';
@ -17,9 +18,14 @@ import Blankslate from './blankslate';
import EmptyResultsBlankslate from './empty-results-blankslate';
import Results from './results';
const messages = defineMessages({
title: { id: 'chat_search.title', defaultMessage: 'Messages' },
});
const ChatSearch = () => {
const debounce = useDebounce;
const dispatch = useAppDispatch();
const intl = useIntl();
const { isOpen, setChat, setSearching, toggleChatPane } = useChatContext();
const { getOrCreateChatByAccountId } = useChats();
@ -82,7 +88,9 @@ const ChatSearch = () => {
/>
</button>
<Text size='sm' weight='bold' truncate>Messages</Text>
<Text size='sm' weight='bold' truncate>
{intl.formatMessage(messages.title)}
</Text>
</HStack>
}
isOpen={isOpen}

Wyświetl plik

@ -1,14 +1,27 @@
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { Stack, Text } from 'soapbox/components/ui';
const EmptyResultsBlankslate = () => (
<Stack justifyContent='center' alignItems='center' space={2} className='h-full w-2/3 mx-auto'>
<Text weight='bold' size='lg' align='center'>No matches found</Text>
<Text theme='muted' align='center'>
Try searching for another name.
</Text>
</Stack>
);
const messages = defineMessages({
title: { id: 'chat_search.empty_results_blankslate.title', defaultMessage: 'No matches found' },
body: { id: 'chat_search.empty_results_blankslate.body', defaultMessage: 'Try searching for another name.' },
});
const EmptyResultsBlankslate = () => {
const intl = useIntl();
return (
<Stack justifyContent='center' alignItems='center' space={2} className='h-full w-2/3 mx-auto'>
<Text weight='bold' size='lg' align='center'>
{intl.formatMessage(messages.title)}
</Text>
<Text theme='muted' align='center'>
{intl.formatMessage(messages.body)}
</Text>
</Stack>
);
};
export default EmptyResultsBlankslate;

Wyświetl plik

@ -11,9 +11,10 @@ module.exports = {
xl: '1280px',
},
extend: {
boxShadow: {
boxShadow: ({ theme }) => ({
'3xl': '0 25px 75px -15px rgba(0, 0, 0, 0.25)',
},
'inset-ring': `inset 0 0 0 2px ${theme('colors.accent-blue')}`,
}),
fontSize: {
base: '0.9375rem',
},