diff --git a/app/soapbox/components/sidebar-navigation.tsx b/app/soapbox/components/sidebar-navigation.tsx index 583006070..e7a65326d 100644 --- a/app/soapbox/components/sidebar-navigation.tsx +++ b/app/soapbox/components/sidebar-navigation.tsx @@ -113,9 +113,9 @@ const SidebarNavigation = () => { return ( } + text={} /> ); } diff --git a/app/soapbox/components/thumb_navigation.tsx b/app/soapbox/components/thumb_navigation.tsx index e46f7c7c3..1935526ed 100644 --- a/app/soapbox/components/thumb_navigation.tsx +++ b/app/soapbox/components/thumb_navigation.tsx @@ -17,8 +17,8 @@ const ThumbNavigation: React.FC = (): JSX.Element => { if (features.chats) { return ( } + src={require('@tabler/icons/mail.svg')} + text={} to='/chats' exact count={chatsCount} diff --git a/app/soapbox/components/ui/stack/stack.tsx b/app/soapbox/components/ui/stack/stack.tsx index 65ec97cbb..e75dfaae3 100644 --- a/app/soapbox/components/ui/stack/stack.tsx +++ b/app/soapbox/components/ui/stack/stack.tsx @@ -1,7 +1,7 @@ import classNames from 'clsx'; import React from 'react'; -type SIZES = 0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 10 +type SIZES = 0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 10 const spaces = { 0: 'space-y-0', @@ -12,6 +12,7 @@ const spaces = { 3: 'space-y-3', 4: 'space-y-4', 5: 'space-y-5', + 6: 'space-y-6', 10: 'space-y-10', }; diff --git a/app/soapbox/features/chats/components/chat-box.tsx b/app/soapbox/features/chats/components/chat-box.tsx index 3c78e7603..f701586e9 100644 --- a/app/soapbox/features/chats/components/chat-box.tsx +++ b/app/soapbox/features/chats/components/chat-box.tsx @@ -1,4 +1,5 @@ import { useMutation } from '@tanstack/react-query'; +import classNames from 'clsx'; import React, { MutableRefObject, useState } from 'react'; import { useIntl, defineMessages } from 'react-intl'; @@ -6,7 +7,7 @@ import { uploadMedia } from 'soapbox/actions/media'; import { HStack, IconButton, Stack, Text, Textarea } from 'soapbox/components/ui'; import UploadProgress from 'soapbox/components/upload-progress'; import UploadButton from 'soapbox/features/compose/components/upload_button'; -import { useAppSelector, useAppDispatch, useOwnAccount } from 'soapbox/hooks'; +import { useAppDispatch, useOwnAccount } from 'soapbox/hooks'; import { IChat, useChat } from 'soapbox/queries/chats'; import { queryClient } from 'soapbox/queries/client'; import { truncateFilename } from 'soapbox/utils/media'; @@ -23,14 +24,15 @@ const fileKeyGen = (): number => Math.floor((Math.random() * 0x10000)); interface IChatBox { chat: IChat, autosize?: boolean, - inputRef?: MutableRefObject + inputRef?: MutableRefObject, + className?: string, } /** * Chat UI with just the messages and textarea. * Reused between floating desktop chats and fullscreen/mobile chats. */ -const ChatBox: React.FC = ({ chat, autosize, inputRef }) => { +const ChatBox: React.FC = ({ chat, autosize, inputRef, className }) => { const intl = useIntl(); const dispatch = useAppDispatch(); const account = useOwnAccount(); @@ -48,7 +50,7 @@ const ChatBox: React.FC = ({ chat, autosize, inputRef }) => { const submitMessage = useMutation(({ chatId, content }: any) => createChatMessage(chatId, content), { retry: false, - onMutate: async(newMessage: any) => { + onMutate: async (newMessage: any) => { // Cancel any outgoing refetches (so they don't overwrite our optimistic update) await queryClient.cancelQueries(['chats', 'messages', chat.id]); @@ -206,7 +208,7 @@ const ChatBox: React.FC = ({ chat, autosize, inputRef }) => { }; return ( - +
diff --git a/app/soapbox/features/chats/components/chat-list.tsx b/app/soapbox/features/chats/components/chat-list.tsx index 9bf437999..c5daf7a55 100644 --- a/app/soapbox/features/chats/components/chat-list.tsx +++ b/app/soapbox/features/chats/components/chat-list.tsx @@ -15,10 +15,11 @@ import Blankslate from './chat-pane/blankslate'; interface IChatList { onClickChat: (chat: any) => void, useWindowScroll?: boolean, + fade?: boolean, searchValue?: string } -const ChatList: React.FC = ({ onClickChat, useWindowScroll = false, searchValue }) => { +const ChatList: React.FC = ({ onClickChat, useWindowScroll = false, searchValue, fade }) => { const dispatch = useDispatch(); const chatListRef = useRef(null); @@ -75,18 +76,22 @@ const ChatList: React.FC = ({ onClickChat, useWindowScroll = false, s )} -
-
+ {fade && ( + <> +
+
+ + )}
); }; diff --git a/app/soapbox/features/chats/components/chat-message-list.tsx b/app/soapbox/features/chats/components/chat-message-list.tsx index 9bbedc8c9..26d44740a 100644 --- a/app/soapbox/features/chats/components/chat-message-list.tsx +++ b/app/soapbox/features/chats/components/chat-message-list.tsx @@ -283,7 +283,7 @@ const ChatMessageList: React.FC = ({ chat, autosize }) => { title={getFormattedTimestamp(chatMessage)} className={ classNames({ - 'text-ellipsis break-words relative rounded-md p-2': true, + 'text-ellipsis break-words relative rounded-md p-2 max-w-full': true, 'bg-primary-500 text-white mr-2': isMyMessage, 'bg-gray-200 dark:bg-gray-800 text-gray-900 dark:text-gray-100 order-2 ml-2': !isMyMessage, }) diff --git a/app/soapbox/features/chats/components/chat-pane/chat-pane.tsx b/app/soapbox/features/chats/components/chat-pane/chat-pane.tsx index 1591b2f3f..e75efd432 100644 --- a/app/soapbox/features/chats/components/chat-pane/chat-pane.tsx +++ b/app/soapbox/features/chats/components/chat-pane/chat-pane.tsx @@ -84,7 +84,7 @@ const ChatPane = () => { ) : ( no results diff --git a/app/soapbox/features/chats/components/chat.tsx b/app/soapbox/features/chats/components/chat.tsx index 8a3ac0e4c..6ec72c380 100644 --- a/app/soapbox/features/chats/components/chat.tsx +++ b/app/soapbox/features/chats/components/chat.tsx @@ -21,12 +21,12 @@ const Chat: React.FC = ({ chat, onClick }) => { data-testid='chat' > - - + + - -
- {chat.account?.display_name || `@${chat.account.username}`} + +
+ {chat.account?.display_name || `@${chat.account.username}`} {chat.account?.verified && }
@@ -37,7 +37,7 @@ const Chat: React.FC = ({ chat, onClick }) => { weight='medium' theme='muted' truncate - className='max-w-[200px]' + className='w-full' data-testid='chat-last-message' dangerouslySetInnerHTML={{ __html: chat.last_message?.content }} /> @@ -54,7 +54,12 @@ const Chat: React.FC = ({ chat, onClick }) => { /> )} - + )} diff --git a/app/soapbox/features/chats/index.tsx b/app/soapbox/features/chats/index.tsx index 5635cbdc8..507167461 100644 --- a/app/soapbox/features/chats/index.tsx +++ b/app/soapbox/features/chats/index.tsx @@ -1,18 +1,19 @@ -import React from 'react'; +import React, { useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { useDispatch } from 'react-redux'; import { useHistory } from 'react-router-dom'; import { launchChat } from 'soapbox/actions/chats'; import AccountSearch from 'soapbox/components/account_search'; -import AudioToggle from 'soapbox/features/chats/components/audio-toggle'; -import { Column } from '../../components/ui'; +import { Card, CardTitle, Stack } from '../../components/ui'; +import Chat from './components/chat'; +import ChatBox from './components/chat-box'; import ChatList from './components/chat-list'; const messages = defineMessages({ - title: { id: 'column.chats', defaultMessage: 'Chats' }, + title: { id: 'column.chats', defaultMessage: 'Messages' }, searchPlaceholder: { id: 'chats.search_placeholder', defaultMessage: 'Start a chat with…' }, }); @@ -21,30 +22,44 @@ const ChatIndex: React.FC = () => { const dispatch = useDispatch(); const history = useHistory(); + const [chat, setChat] = useState(null); + const handleSuggestion = (accountId: string) => { dispatch(launchChat(accountId, history, true)); }; - const handleClickChat = (chat: { id: string }) => { - history.push(`/chats/${chat.id}`); + const handleClickChat = (chat: any) => { + // history.push(`/chats/${chat.id}`); + setChat(chat); }; return ( - -
- + +
+ + + + + + + + + + + {chat && ( + + {}} /> +
+ {}} /> +
+
+ )} +
- - - - - +
); }; diff --git a/app/soapbox/features/ui/index.tsx b/app/soapbox/features/ui/index.tsx index b136ebdb8..1328999fd 100644 --- a/app/soapbox/features/ui/index.tsx +++ b/app/soapbox/features/ui/index.tsx @@ -29,6 +29,7 @@ import ThumbNavigation from 'soapbox/components/thumb_navigation'; import { Layout } from 'soapbox/components/ui'; import { useAppSelector, useOwnAccount, useSoapboxConfig, useFeatures } from 'soapbox/hooks'; import AdminPage from 'soapbox/pages/admin_page'; +import ChatsPage from 'soapbox/pages/chats-page'; import DefaultPage from 'soapbox/pages/default_page'; // import GroupsPage from 'soapbox/pages/groups_page'; // import GroupPage from 'soapbox/pages/group_page'; @@ -265,8 +266,8 @@ const SwitchingColumnsArea: React.FC = ({ children }) => { {features.suggestions && } {features.profileDirectory && } - {features.chats && } - {features.chats && } + {features.chats && } + {features.chats && } diff --git a/app/soapbox/locales/en.json b/app/soapbox/locales/en.json index 88d96cf7d..58d37d736 100644 --- a/app/soapbox/locales/en.json +++ b/app/soapbox/locales/en.json @@ -194,7 +194,7 @@ "column.birthdays": "Birthdays", "column.blocks": "Blocked users", "column.bookmarks": "Bookmarks", - "column.chats": "Chats", + "column.chats": "Messages", "column.community": "Local timeline", "column.crypto_donate": "Donate Cryptocurrency", "column.developers": "Developers", diff --git a/app/soapbox/pages/chats-page.tsx b/app/soapbox/pages/chats-page.tsx new file mode 100644 index 000000000..b822f3d85 --- /dev/null +++ b/app/soapbox/pages/chats-page.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +/** Custom layout for chats on desktop. */ +const ChatsPage: React.FC = ({ children }) => { + return ( +
+ {children} +
+ ); +}; + +export default ChatsPage; diff --git a/app/soapbox/queries/chats.ts b/app/soapbox/queries/chats.ts index 82dd400d5..738e0ca21 100644 --- a/app/soapbox/queries/chats.ts +++ b/app/soapbox/queries/chats.ts @@ -3,8 +3,10 @@ import { useEffect, useState } from 'react'; import { fetchRelationships } from 'soapbox/actions/accounts'; import snackbar from 'soapbox/actions/snackbar'; +import compareId from 'soapbox/compare_id'; import { useChatContext } from 'soapbox/contexts/chat-context'; import { useApi, useAppDispatch } from 'soapbox/hooks'; +import { normalizeChatMessage } from 'soapbox/normalizers'; import { queryClient } from './client'; @@ -40,11 +42,7 @@ export interface IChatMessage { pending?: boolean } -const reverseOrder = (a: IChat, b: IChat): number => { - if (Number(a.id) < Number(b.id)) return -1; - if (Number(a.id) > Number(b.id)) return 1; - return 0; -}; +const reverseOrder = (a: IChat, b: IChat): number => compareId(a.id, b.id); const useChatMessages = (chatId: string) => { const api = useApi(); @@ -57,7 +55,7 @@ const useChatMessages = (chatId: string) => { }); const hasMore = !!headers.link; - const result = data.sort(reverseOrder); + const result = data.sort(reverseOrder).map(normalizeChatMessage); const nextMaxId = result[0]?.id; return {