feat: render all users in sidebar chat

This commit is contained in:
Sergey Yarkov 2022-02-14 21:43:12 +03:00
parent ce5411bd50
commit cfd1137c2d
10 changed files with 79 additions and 26 deletions

View File

@ -5,6 +5,7 @@ import { User } from './User';
import { useChatContext } from '../../context'; import { useChatContext } from '../../context';
import { useRootStore } from '@educt/hooks/useRootStore'; import { useRootStore } from '@educt/hooks/useRootStore';
import { Skeleton } from '@chakra-ui/react'; import { Skeleton } from '@chakra-ui/react';
import { useFetchUsers } from '@educt/hooks/queries';
const Wrapper: React.FC<StackProps> = ({ children }) => { const Wrapper: React.FC<StackProps> = ({ children }) => {
return ( return (
@ -15,9 +16,9 @@ const Wrapper: React.FC<StackProps> = ({ children }) => {
}; };
const Users: React.FC = observer(() => { const Users: React.FC = observer(() => {
const { data: users } = useFetchUsers();
const { onlineStore } = useRootStore(); const { onlineStore } = useRootStore();
const { search } = useChatContext(); const { search } = useChatContext();
const { users } = onlineStore;
if (users === null) if (users === null)
return ( return (
@ -27,17 +28,18 @@ const Users: React.FC = observer(() => {
</Wrapper> </Wrapper>
); );
const filtered = !search ? users : users.filter(u => u.userName.toLocaleLowerCase().includes(search)); const filtered = (!search ? users : users.filter(u => u.fullname.toLocaleLowerCase().includes(search))).sort(user =>
!onlineStore.isOnline(user.id) ? 1 : -1
);
const isEmpty = filtered.length === 0; const isEmpty = filtered.length === 0;
return ( return (
<Wrapper> <Wrapper>
{!isEmpty ? ( {!isEmpty ? (
filtered.map(u => ( filtered.map(user => (
<User key={u.userId} id={u.userId}> <User key={user.id} id={user.id}>
<User.Avatar isOnline={onlineStore.isOnline(u.userId) || false} fullname={u.userName} /> <User.Avatar isOnline={onlineStore.isOnline(user.id) || false} fullname={user.fullname} />
<User.Info fullname={u.userName} lastMessage={''} /> <User.Info fullname={user.fullname} lastMessage={''} />
<User.Time time={'2h'} />
</User> </User>
)) ))
) : ( ) : (

View File

@ -1,6 +1,19 @@
import React, { ChangeEvent } from 'react'; import React, { ChangeEvent } from 'react';
import axios from 'axios'; import axios from 'axios';
import { GridItem, GridItemProps, Flex, Box, Icon, Text, Input, Heading, IconButton, Divider } from '@chakra-ui/react'; import { Link as ReactRouterLink } from 'react-router-dom';
import {
GridItem,
GridItemProps,
Flex,
Box,
Icon,
Text,
Input,
Heading,
IconButton,
Divider,
Link,
} from '@chakra-ui/react';
import { MdOutlineChat, MdSend } from 'react-icons/md'; import { MdOutlineChat, MdSend } from 'react-icons/md';
import { MessageList } from './Message'; import { MessageList } from './Message';
import { SocketContext } from '@educt/contexts'; import { SocketContext } from '@educt/contexts';
@ -80,13 +93,12 @@ const Window: React.FC = props => {
data: { history }, data: { history },
} = await UserServiceInstance.fetchChatHistory(chatId); } = await UserServiceInstance.fetchChatHistory(chatId);
// TODO add type for message
const messages = history.map(message => { const messages = history.map(message => {
return { return {
isMyMessage: message.from === me.id, isMyMessage: message.from === me?.id,
content: message.content, content: message.content,
time: new Date(message.time).toLocaleTimeString(), time: new Date(message.time).toLocaleTimeString(),
userName: message.from === me.id ? me.fullname : user.fullname, userName: message.from === me?.id ? me.fullname : user.fullname,
}; };
}); });
@ -161,11 +173,16 @@ const Window: React.FC = props => {
return ( return (
<Wrapper {...props}> <Wrapper {...props}>
<Flex flexDir={'column'}> <Flex flexDir={'column'}>
<Box my='3'> <Flex my='3' alignItems={'flex-end'} justifyContent={'space-between'}>
<Heading as='h2' fontWeight={'semibold'} fontSize='3xl'> <Heading as='h2' fontWeight={'semibold'} fontSize='3xl'>
{isMe && me ? 'Favorites' : selectedUser.fullname} {isMe && me ? 'Favorites' : selectedUser.fullname}
</Heading> </Heading>
</Box> {!isMe && (
<Link as={ReactRouterLink} to={`/user/${selectedUser.id}`} textDecor='underline'>
See profile
</Link>
)}
</Flex>
<Divider my='2' /> <Divider my='2' />
<MessageList messages={messages} /> <MessageList messages={messages} />
<form onSubmit={e => onSubmit(e)}> <form onSubmit={e => onSubmit(e)}>

View File

@ -62,13 +62,6 @@ const Layout: React.FC<ILayoutProps> = ({ children, roles }) => {
} }
}, [socket]); }, [socket]);
/**
* Emit online status of user
*/
useEffect(() => {
socket?.emit('user:status');
}, []);
return ( return (
<> <>
<Header> <Header>

View File

@ -25,6 +25,7 @@ import { useDetachStudents } from './course/useDetachStudents';
/** /**
* User * User
*/ */
import { useFetchUsers } from './user/useFetchUsers';
import { useFetchUser } from './user/useFetchUser'; import { useFetchUser } from './user/useFetchUser';
import { useCreateUser } from './user/useCreateUser'; import { useCreateUser } from './user/useCreateUser';
import { useDeleteUser } from './user/useDeleteUser'; import { useDeleteUser } from './user/useDeleteUser';
@ -58,6 +59,7 @@ export {
useCreateLesson, useCreateLesson,
useDeleteLesson, useDeleteLesson,
useUpdateLesson, useUpdateLesson,
useFetchUsers,
useFetchUser, useFetchUser,
useCreateUser, useCreateUser,
useDeleteUser, useDeleteUser,

View File

@ -0,0 +1,30 @@
import useAsync from '@educt/hooks/useAsync';
import { IUser } from '@educt/interfaces';
import { UserServiceInstance } from '@educt/services';
import { FetchUsersParamsType } from '@educt/types';
import { useEffect } from 'react';
import { useErrorHandler } from 'react-error-boundary';
const useFetchUsers = (params?: FetchUsersParamsType | undefined) => {
const handleError = useErrorHandler();
const fetch = async (params?: FetchUsersParamsType | undefined) => {
try {
const result = await UserServiceInstance.fetchAll(params);
return result.data;
} catch (error) {
handleError(error);
return Promise.reject(error);
}
};
const { execute: fetchUsers, ...state } = useAsync<IUser[], Parameters<typeof fetch>>(fetch);
useEffect(() => {
fetchUsers(params);
}, []);
return { ...state };
};
export { useFetchUsers };

View File

@ -43,7 +43,7 @@ const AuthPage: React.FC<IPageProps> = ({ title }) => {
</Helmet> </Helmet>
<Box maxW='md' mx='auto' mt='20'> <Box maxW='md' mx='auto' mt='20'>
<Logo mx='auto' h='14' mb={{ base: '10', md: '10' }} /> <Logo mx='auto' h='14' mb={{ base: '10', md: '10' }} />
<Card> <Card p='6'>
<AuthForm /> <AuthForm />
</Card> </Card>
</Box> </Box>

View File

@ -104,7 +104,7 @@ const CoursePage: React.FC<IPageProps> = () => {
<Text fontSize={'sm'} mr='2'> <Text fontSize={'sm'} mr='2'>
Students: Students:
</Text> </Text>
<AvatarGroup size='sm' max={2}> <AvatarGroup size='sm' max={3}>
{course.students.map(student => ( {course.students.map(student => (
<Avatar key={student.id} name={student.fullname} /> <Avatar key={student.id} name={student.fullname} />
))} ))}

View File

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react';
/** /**
* Types * Types
@ -9,8 +10,9 @@ import { IPageProps } from '@educt/interfaces';
* Components * Components
*/ */
import { Page } from '@educt/components/PageElements'; import { Page } from '@educt/components/PageElements';
import { CreateButton } from '@educt/components/Buttons';
import { Box } from '@chakra-ui/layout'; import { Box } from '@chakra-ui/layout';
import { CreateButton } from '@educt/components/Buttons';
import { CoursesTabs } from './components';
import LoadingPage from '@educt/components/LoadingPage'; import LoadingPage from '@educt/components/LoadingPage';
import CourseList from './components/CourseList'; import CourseList from './components/CourseList';
import CategoryList from './components/CategoryList'; import CategoryList from './components/CategoryList';
@ -22,7 +24,6 @@ import CourseItem from './components/CourseList/CourseItem';
*/ */
import { useRootStore } from '@educt/hooks/useRootStore'; import { useRootStore } from '@educt/hooks/useRootStore';
import { useHistory } from 'react-router'; import { useHistory } from 'react-router';
import { CoursesTabs } from './components';
/** /**
* Courses page * Courses page
@ -53,4 +54,4 @@ const CoursesPage: React.FC<IPageProps> = () => {
); );
}; };
export default CoursesPage; export default observer(CoursesPage);

View File

@ -8,6 +8,7 @@ import {
UpdateUserParamsType, UpdateUserParamsType,
UpdateUserContactsParamsType, UpdateUserContactsParamsType,
UpdateUserInfoParamsType, UpdateUserInfoParamsType,
HistoryMessageType,
} from '@educt/types'; } from '@educt/types';
class UserService { class UserService {
@ -170,7 +171,7 @@ class UserService {
* @param chatId Chat id * @param chatId Chat id
* @returns Array of messages * @returns Array of messages
*/ */
public async fetchChatHistory(chatId: string): Promise<IApiRespose<{ history: unknown[] }>> { public async fetchChatHistory(chatId: string): Promise<IApiRespose<{ history: HistoryMessageType[] }>> {
const result = await this.api.get(`v1/me/messages/${chatId}`); const result = await this.api.get(`v1/me/messages/${chatId}`);
return result.data; return result.data;
} }

View File

@ -69,6 +69,13 @@ export type MessageType = {
isMyMessage: boolean; isMyMessage: boolean;
}; };
export type HistoryMessageType = {
from: string;
to: string;
content: string;
time: string;
};
/** /**
* Change email page context * Change email page context
*/ */