From 5602c29b158e38d10f3723172a46c223ea461b63 Mon Sep 17 00:00:00 2001 From: Sergey Yarkov Date: Fri, 11 Feb 2022 11:55:56 +0300 Subject: [PATCH] feat: fethincg user in messenger & improved perfomance --- src/components/Chat/Sidebar/Sidebar.tsx | 2 - src/components/Chat/Window/Message/Avatar.tsx | 6 +- .../Chat/Window/Message/Message.tsx | 23 ++- src/components/Chat/Window/Window.tsx | 174 +++++++++++------- src/hooks/queries/index.ts | 2 + 5 files changed, 135 insertions(+), 72 deletions(-) diff --git a/src/components/Chat/Sidebar/Sidebar.tsx b/src/components/Chat/Sidebar/Sidebar.tsx index e346039..202cb36 100644 --- a/src/components/Chat/Sidebar/Sidebar.tsx +++ b/src/components/Chat/Sidebar/Sidebar.tsx @@ -20,8 +20,6 @@ const Sidebar: React.FC & ISidebarComposition = props => { {props.children} ); - - return null; }; Sidebar.Heading = Heading; diff --git a/src/components/Chat/Window/Message/Avatar.tsx b/src/components/Chat/Window/Message/Avatar.tsx index 525c1a2..6887fe1 100644 --- a/src/components/Chat/Window/Message/Avatar.tsx +++ b/src/components/Chat/Window/Message/Avatar.tsx @@ -1,16 +1,16 @@ -import React from 'react'; +import React, { memo } from 'react'; import { WrapItem, Avatar as ChakraAvatar } from '@chakra-ui/react'; export interface IAvatarProps { fullname: string; } -const Avatar: React.FC = ({ fullname: name }) => { +const Avatar: React.FC = memo(({ fullname: name }) => { return ( ); -}; +}); export { Avatar }; diff --git a/src/components/Chat/Window/Message/Message.tsx b/src/components/Chat/Window/Message/Message.tsx index cd4d5de..3d9aa13 100644 --- a/src/components/Chat/Window/Message/Message.tsx +++ b/src/components/Chat/Window/Message/Message.tsx @@ -1,5 +1,5 @@ -import React, { useEffect, useRef } from 'react'; -import { Flex, Stack } from '@chakra-ui/react'; +import React, { memo, useEffect, useRef } from 'react'; +import { Flex, Box, Stack, Text as ChakraText } from '@chakra-ui/react'; import { Avatar, IAvatarProps } from './Avatar'; import { Text, ITextProps } from './Text'; import { MessageContext } from './context'; @@ -14,7 +14,7 @@ interface IMyMessageProps { isMyMessage: boolean; } -const MessageList: React.FC<{ messages: MessageType[] }> = ({ messages, children }) => { +const MessageList: React.FC<{ messages: MessageType[] }> = memo(({ messages }) => { const listRef = useRef(null); useEffect(() => { @@ -25,10 +25,23 @@ const MessageList: React.FC<{ messages: MessageType[] }> = ({ messages, children return ( - {children} + {messages.length !== 0 ? ( + messages.map((message, i) => ( + + + + + )) + ) : ( + + + Your message history will be displayed here. + + + )} ); -}; +}); const Message: React.FC & IMessageComposition = ({ children, isMyMessage }) => { return ( diff --git a/src/components/Chat/Window/Window.tsx b/src/components/Chat/Window/Window.tsx index c1c237e..26ffb6d 100644 --- a/src/components/Chat/Window/Window.tsx +++ b/src/components/Chat/Window/Window.tsx @@ -1,21 +1,42 @@ -import React, { ChangeEvent, useContext, useEffect, useState } from 'react'; +import React, { ChangeEvent } from 'react'; +import axios from 'axios'; import { GridItem, GridItemProps, Flex, Box, Icon, Text, Input, Heading, IconButton, Divider } from '@chakra-ui/react'; import { MdOutlineChat, MdSend } from 'react-icons/md'; -import { Message, MessageList } from './Message'; -import { useRootStore } from '@educt/hooks/useRootStore'; +import { MessageList } from './Message'; import { SocketContext } from '@educt/contexts'; -import { useSocketEvent } from '@educt/hooks/useSocketEvent'; import { MessageType } from '@educt/types'; +import { UserServiceInstance } from '@educt/services'; +import { IUser } from '@educt/interfaces'; +import LoadingList from '@educt/components/LoadingList'; -const Window: React.FC = props => { +/** + * Hooks + */ +import { useContext, useEffect, useState } from 'react'; +import { useToast } from '@chakra-ui/react'; +import { useSocketEvent } from '@educt/hooks/useSocketEvent'; +import { useRootStore } from '@educt/hooks/useRootStore'; +import { useHistory } from 'react-router-dom'; + +const Wrapper: React.FC = props => ( + + {props.children} + +); + +const Window: React.FC = props => { const { userStore: { me }, onlineStore, } = useRootStore(); const chatId = new URLSearchParams(location.search).get('chat_id'); + const [isLoading, setIsLoading] = useState(false); + const [selectedUser, setSelectedUser] = useState(null); const [inputValue, setInputValue] = useState(''); const [messages, setMessages] = useState([]); const { socket } = useContext(SocketContext); + const history = useHistory(); + const toast = useToast(); const onSubmit = (e: React.FormEvent) => { e.preventDefault(); @@ -44,16 +65,47 @@ const Window: React.FC = props => { const onChangeInputValue = (e: ChangeEvent) => setInputValue(e.target.value); + /** + * Load user data by `chat_id` + */ + useEffect(() => { + if (chatId) { + (async () => { + try { + setIsLoading(true); + const { data: user } = await UserServiceInstance.fetchUserById(chatId); + setSelectedUser(user); + } catch (error) { + if (axios.isAxiosError(error)) { + if (error.response?.status === 404) { + toast({ title: 'Chat not found.', status: 'warning' }); + history.replace({ search: '' }); + } + } + + console.error(error); + } finally { + setIsLoading(false); + } + })(); + } + }, [chatId]); + + // TODO load message history + /** + * Loading messages history + */ useEffect(() => { - // TODO load message history console.log('chat id changed!'); setMessages([]); }, [chatId]); // TODO add types for this event + /** + * Event on accept new messages + */ useSocketEvent('message:private', data => { const user = onlineStore.getUser(data.from); - console.log('message!'); if (user) { if (chatId === data.from) { @@ -74,68 +126,66 @@ const Window: React.FC = props => { } }); - return ( - - {chatId ? ( - - - - {chatId} - - + if (isLoading) { + return ( + + + + ); + } - - - - {messages.length !== 0 ? ( - messages.map((message, i) => ( - - - - - )) - ) : ( - - - Your message history will be displayed here. - - - )} - -
onSubmit(e)}> - - - } - aria-label='Send message' - variant={'ghost'} - _hover={{ bg: 'inherit' }} - _active={{ bg: 'inherit' }} - _focus={{ outline: 'none' }} - /> - -
-
- ) : ( + /** + * Unselected user + */ + if (!chatId || selectedUser === null) { + return ( + Select a chat to start messaging - )} -
+ + ); + } + + return ( + + + + + {selectedUser.fullname} + + + + +
onSubmit(e)}> + + + } + aria-label='Send message' + variant={'ghost'} + _hover={{ bg: 'inherit' }} + _active={{ bg: 'inherit' }} + _focus={{ outline: 'none' }} + /> + +
+
+
); }; diff --git a/src/hooks/queries/index.ts b/src/hooks/queries/index.ts index 27ee318..95d8a7e 100644 --- a/src/hooks/queries/index.ts +++ b/src/hooks/queries/index.ts @@ -25,6 +25,7 @@ import { useDetachStudents } from './course/useDetachStudents'; /** * User */ +import { useFetchUser } from './user/useFetchUser'; import { useCreateUser } from './user/useCreateUser'; import { useDeleteUser } from './user/useDeleteUser'; import { useUpdateUser } from './user/useUpdateUser'; @@ -57,6 +58,7 @@ export { useCreateLesson, useDeleteLesson, useUpdateLesson, + useFetchUser, useCreateUser, useDeleteUser, useUpdateUser,