mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
feat: render all users in sidebar chat
This commit is contained in:
parent
ce5411bd50
commit
cfd1137c2d
@ -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>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -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)}>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
30
src/hooks/queries/user/useFetchUsers.ts
Normal file
30
src/hooks/queries/user/useFetchUsers.ts
Normal 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 };
|
||||||
@ -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>
|
||||||
|
|||||||
@ -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} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
7
src/types/index.d.ts
vendored
7
src/types/index.d.ts
vendored
@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user