feat: sort online users in chat & added types to socket data events

This commit is contained in:
Sergey Yarkov 2022-02-16 22:03:19 +03:00
parent cfd1137c2d
commit 3b15e47cfe
6 changed files with 57 additions and 23 deletions

View File

@ -15,6 +15,7 @@ import PrivateRoute from '@educt/components/PrivateRoute';
import PublicRoute from '@educt/components/PublicRoute'; import PublicRoute from '@educt/components/PublicRoute';
import { useSocketEvent } from './hooks/useSocketEvent'; import { useSocketEvent } from './hooks/useSocketEvent';
import { SocketContext } from './contexts'; import { SocketContext } from './contexts';
import { UserOnlineListType, UserSeesionType } from './types';
const App = () => { const App = () => {
const { const {
@ -26,16 +27,16 @@ const App = () => {
useSocketEvent('connect', () => console.info('[WebSocket]: Connected.')); useSocketEvent('connect', () => console.info('[WebSocket]: Connected.'));
useSocketEvent('disconnect', () => console.info('[WebSocket]: Disconnected.')); useSocketEvent('disconnect', () => console.info('[WebSocket]: Disconnected.'));
useSocketEvent('connect_error', err => console.error(err)); useSocketEvent('connect_error', err => console.error(err));
useSocketEvent('user:session', data => { useSocketEvent<UserSeesionType>('user:session', data => {
if (socket) { if (socket) {
const { sessionId, userId } = data; const { sessionId } = data;
if (sessionId) {
socket.auth = { sessionId }; socket.auth = { sessionId };
window.localStorage.setItem('sessionId', sessionId); window.localStorage.setItem('sessionId', sessionId);
// TODO fix types }
socket.userId = userId;
} }
}); });
useSocketEvent('user:online', online => onlineStore.loadOnline(online)); useSocketEvent<UserOnlineListType>('user:online', online => onlineStore.loadOnline(online));
return ( return (
<Router history={history}> <Router history={history}>

View File

@ -17,10 +17,13 @@ const Wrapper: React.FC<StackProps> = ({ children }) => {
const Users: React.FC = observer(() => { const Users: React.FC = observer(() => {
const { data: users } = useFetchUsers(); const { data: users } = useFetchUsers();
const { onlineStore } = useRootStore(); const {
userStore: { me },
onlineStore,
} = useRootStore();
const { search } = useChatContext(); const { search } = useChatContext();
if (users === null) if (users === null || me === null)
return ( return (
<Wrapper> <Wrapper>
<Skeleton h='90px' borderRadius={'lg'} /> <Skeleton h='90px' borderRadius={'lg'} />
@ -28,20 +31,31 @@ const Users: React.FC = observer(() => {
</Wrapper> </Wrapper>
); );
const filtered = (!search ? users : users.filter(u => u.fullname.toLocaleLowerCase().includes(search))).sort(user => const filtered = (!search ? users : users.filter(u => u.fullname.toLocaleLowerCase().includes(search)))
!onlineStore.isOnline(user.id) ? 1 : -1 .concat()
); .sort(u => (onlineStore.isOnline(u.id) ? -1 : 1));
const isEmpty = filtered.length === 0; const isEmpty = filtered.length === 0;
return ( return (
<Wrapper> <Wrapper>
{/* Favorites */}
<User id={me.id}>
<User.Avatar isOnline fullname={me.fullname} />
<User.Info fullname={me.fullname} lastMessage={''} />
</User>
{/* User list */}
{!isEmpty ? ( {!isEmpty ? (
filtered.map(user => ( filtered.map(
user =>
user.id !== me.id && (
<User key={user.id} id={user.id}> <User key={user.id} id={user.id}>
<User.Avatar isOnline={onlineStore.isOnline(user.id) || false} fullname={user.fullname} /> <User.Avatar isOnline={onlineStore.isOnline(user.id) || false} fullname={user.fullname} />
<User.Info fullname={user.fullname} lastMessage={''} /> <User.Info fullname={user.fullname} lastMessage={''} />
</User> </User>
)) )
)
) : ( ) : (
<Box textAlign={'center'}> <Box textAlign={'center'}>
<Text userSelect={'none'} color='gray.500'> <Text userSelect={'none'} color='gray.500'>

View File

@ -17,7 +17,7 @@ import {
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';
import { MessageType } from '@educt/types'; import { HistoryMessageType, MessageType } from '@educt/types';
import { UserServiceInstance } from '@educt/services'; import { UserServiceInstance } from '@educt/services';
import { IUser } from '@educt/interfaces'; import { IUser } from '@educt/interfaces';
import LoadingList from '@educt/components/LoadingList'; import LoadingList from '@educt/components/LoadingList';
@ -120,11 +120,10 @@ const Window: React.FC = props => {
} }
}, [chatId]); }, [chatId]);
// TODO add types for this event
/** /**
* Event on accept new messages * Event on accept new messages
*/ */
useSocketEvent('chat:message', data => { useSocketEvent<HistoryMessageType>('chat:message', data => {
const user = onlineStore.getUser(data.from); const user = onlineStore.getUser(data.from);
if (user) { if (user) {
@ -134,6 +133,18 @@ const Window: React.FC = props => {
*/ */
if (data.to === data.from) return; if (data.to === data.from) return;
/**
* Notify user about new message
*/
// toast({
// title: `${user.userName}`,
// description: data.content,
// variant: 'left-accent',
// position: 'bottom-right',
// status: 'info',
// duration: 5000,
// });
const message: MessageType = { const message: MessageType = {
content: data.content, content: data.content,
userName: user.userName, userName: user.userName,

View File

@ -2,10 +2,11 @@ import { createContext } from 'react';
import { ChangeEmailPageContextType, CoursesPageContextType, UsersPageContextType } from '@educt/types'; import { ChangeEmailPageContextType, CoursesPageContextType, UsersPageContextType } from '@educt/types';
import RootStore from '../stores/RootStore'; import RootStore from '../stores/RootStore';
import { Socket } from 'socket.io-client'; import { Socket } from 'socket.io-client';
import { ClientToServerEvents, ServerToClientEvents } from '@educt/interfaces';
export const StoreContext = createContext<RootStore | undefined>(undefined); export const StoreContext = createContext<RootStore | undefined>(undefined);
export const SocketContext = createContext<{ socket: Socket | null }>({ export const SocketContext = createContext<{ socket: Socket<ServerToClientEvents, ClientToServerEvents> | null }>({
socket: null, socket: null,
}); });

View File

@ -1,7 +1,7 @@
import { useContext, useEffect } from 'react'; import { useContext, useEffect } from 'react';
import { SocketContext } from '@educt/contexts'; import { SocketContext } from '@educt/contexts';
export const useSocketEvent = (event: string, callback: (...args: unknown[]) => void) => { export const useSocketEvent = <T>(event: string, callback: (data: T) => void) => {
const { socket } = useContext(SocketContext); const { socket } = useContext(SocketContext);
useEffect(() => { useEffect(() => {

View File

@ -76,6 +76,13 @@ export type HistoryMessageType = {
time: string; time: string;
}; };
export type UserSeesionType = {
sessionId: string | undefined;
userId: string | undefined;
};
export type UserOnlineListType = [string, { userId: string; userName: string }][];
/** /**
* Change email page context * Change email page context
*/ */