mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 18:29:15 +03:00
feat: online user status
This commit is contained in:
parent
6f855af3db
commit
f92ef01353
@ -1,10 +1,11 @@
|
|||||||
import React, { useContext, useEffect } from 'react';
|
import React from 'react';
|
||||||
import { Router, Switch } from 'react-router-dom';
|
import { Router, Switch } from 'react-router-dom';
|
||||||
import { routes } from './routes.config';
|
import { routes } from './routes.config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
|
import { useContext } from 'react';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,6 +19,7 @@ import { SocketContext } from './contexts';
|
|||||||
const App = () => {
|
const App = () => {
|
||||||
const {
|
const {
|
||||||
uiStore: { history },
|
uiStore: { history },
|
||||||
|
onlineStore,
|
||||||
} = useRootStore();
|
} = useRootStore();
|
||||||
const { socket } = useContext(SocketContext);
|
const { socket } = useContext(SocketContext);
|
||||||
|
|
||||||
@ -33,6 +35,7 @@ const App = () => {
|
|||||||
socket.userId = userId;
|
socket.userId = userId;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
useSocketEvent('user:online', online => onlineStore.loadOnline(online));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import React, { Suspense, useContext } from 'react';
|
import React, { Suspense } from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { Redirect } from 'react-router';
|
import { Redirect } from 'react-router';
|
||||||
import { useErrorHandler } from 'react-error-boundary';
|
|
||||||
import { userContainRoles } from '@educt/helpers';
|
import { userContainRoles } from '@educt/helpers';
|
||||||
import { Flex, Box } from '@chakra-ui/react';
|
import { Flex, Box } from '@chakra-ui/react';
|
||||||
|
|
||||||
@ -20,7 +19,8 @@ import NavDesktop from '@educt/components/Nav/Desktop';
|
|||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useContext } from 'react';
|
||||||
|
import { useErrorHandler } from 'react-error-boundary';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -10,7 +10,7 @@ const Avatar: React.FC<IAvatarProps> = props => {
|
|||||||
return (
|
return (
|
||||||
<Flex flexDir={'column'} alignItems={'center'}>
|
<Flex flexDir={'column'} alignItems={'center'}>
|
||||||
<ChakraAvatar border={`4px solid ${borderColor}`} borderColor={borderColor} name={name} size='2xl'>
|
<ChakraAvatar border={`4px solid ${borderColor}`} borderColor={borderColor} name={name} size='2xl'>
|
||||||
{isOnline && <ChakraAvatarBadge boxSize='0.75em' bg={onlineColor} />}
|
{isOnline !== undefined && <ChakraAvatarBadge boxSize='0.75em' bg={onlineColor} />}
|
||||||
</ChakraAvatar>
|
</ChakraAvatar>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -5,7 +5,12 @@ export const useSocket = (url: string) => {
|
|||||||
const [socket, setSocket] = useState<Socket | null>(null);
|
const [socket, setSocket] = useState<Socket | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const socketClient = io(url, { autoConnect: false, withCredentials: true });
|
const socketClient = io(url, {
|
||||||
|
autoConnect: false,
|
||||||
|
withCredentials: true,
|
||||||
|
transports: ['websocket'],
|
||||||
|
upgrade: false,
|
||||||
|
});
|
||||||
|
|
||||||
setSocket(socketClient);
|
setSocket(socketClient);
|
||||||
|
|
||||||
|
|||||||
@ -25,9 +25,7 @@ import { observer } from 'mobx-react';
|
|||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { useFetchCourses, useFetchStat } from '@educt/hooks/queries';
|
import { useFetchCourses, useFetchStat } from '@educt/hooks/queries';
|
||||||
import { useSocketEvent } from '@educt/hooks/useSocketEvent';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main page
|
* Main page
|
||||||
@ -35,23 +33,10 @@ import { useSocketEvent } from '@educt/hooks/useSocketEvent';
|
|||||||
const MainPage: React.FC<IPageProps> = () => {
|
const MainPage: React.FC<IPageProps> = () => {
|
||||||
const {
|
const {
|
||||||
userStore: { me },
|
userStore: { me },
|
||||||
|
onlineStore,
|
||||||
} = useRootStore();
|
} = useRootStore();
|
||||||
const { data: courses } = useFetchCourses({ limit: 3 });
|
const { data: courses } = useFetchCourses({ limit: 3 });
|
||||||
const { data: stat } = useFetchStat();
|
const { data: stat } = useFetchStat();
|
||||||
const [online, setOnline] = useState<number>(0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update online state on socket event
|
|
||||||
*/
|
|
||||||
useSocketEvent('user:online', online => setOnline(online));
|
|
||||||
|
|
||||||
// useSocketEvent('user:connected', user => console.table(user));
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (stat) {
|
|
||||||
setOnline(stat.online);
|
|
||||||
}
|
|
||||||
}, [stat]);
|
|
||||||
|
|
||||||
if (me === null || courses === null || stat === null) return <LoadingPage />;
|
if (me === null || courses === null || stat === null) return <LoadingPage />;
|
||||||
|
|
||||||
@ -63,7 +48,7 @@ const MainPage: React.FC<IPageProps> = () => {
|
|||||||
<Stat>
|
<Stat>
|
||||||
<StatLabel>Online</StatLabel>
|
<StatLabel>Online</StatLabel>
|
||||||
<StatContent>
|
<StatContent>
|
||||||
<StatNumber>{online}</StatNumber>
|
<StatNumber>{onlineStore.count}</StatNumber>
|
||||||
<StatIcon icon={MdOutlineGroup} />
|
<StatIcon icon={MdOutlineGroup} />
|
||||||
</StatContent>
|
</StatContent>
|
||||||
</Stat>
|
</Stat>
|
||||||
|
|||||||
@ -17,6 +17,7 @@ const UserPage: React.FC<IPageProps> = () => {
|
|||||||
const params = useParams<{ id: string }>();
|
const params = useParams<{ id: string }>();
|
||||||
const {
|
const {
|
||||||
userStore: { me },
|
userStore: { me },
|
||||||
|
onlineStore,
|
||||||
} = useRootStore();
|
} = useRootStore();
|
||||||
const { data: user, error, isLoading } = useFetchUser(params.id);
|
const { data: user, error, isLoading } = useFetchUser(params.id);
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ const UserPage: React.FC<IPageProps> = () => {
|
|||||||
<Page.Content maxW={'900px'}>
|
<Page.Content maxW={'900px'}>
|
||||||
<UserProfile>
|
<UserProfile>
|
||||||
<UserProfile.Base>
|
<UserProfile.Base>
|
||||||
<UserProfile.Avatar name={user.fullname} isOnline>
|
<UserProfile.Avatar name={user.fullname} isOnline={onlineStore.isOnline(user.id)}>
|
||||||
<UserProfile.ChatButton userid={user.id} />
|
<UserProfile.ChatButton userid={user.id} />
|
||||||
</UserProfile.Avatar>
|
</UserProfile.Avatar>
|
||||||
<UserProfile.Info>
|
<UserProfile.Info>
|
||||||
|
|||||||
27
src/stores/OnlineStore.ts
Normal file
27
src/stores/OnlineStore.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { makeAutoObservable } from 'mobx';
|
||||||
|
import RootStore from './RootStore';
|
||||||
|
|
||||||
|
export default class OnlineStore {
|
||||||
|
public root: RootStore;
|
||||||
|
|
||||||
|
public online: Set<string>;
|
||||||
|
|
||||||
|
constructor(root: RootStore) {
|
||||||
|
this.root = root;
|
||||||
|
this.online = new Set();
|
||||||
|
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public loadOnline(online: string[]) {
|
||||||
|
this.online = new Set(online);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isOnline(id: string) {
|
||||||
|
return this.online.has(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get count() {
|
||||||
|
return this.online.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,6 +9,7 @@ import UIStore from './UIStore';
|
|||||||
import CategoryStore from './CategoryStore';
|
import CategoryStore from './CategoryStore';
|
||||||
import CourseStore from './CourseStore';
|
import CourseStore from './CourseStore';
|
||||||
import PageStore from './PageStore';
|
import PageStore from './PageStore';
|
||||||
|
import OnlineStore from './OnlineStore';
|
||||||
|
|
||||||
export default class RootStore {
|
export default class RootStore {
|
||||||
public pageStore: PageStore;
|
public pageStore: PageStore;
|
||||||
@ -23,6 +24,8 @@ export default class RootStore {
|
|||||||
|
|
||||||
public userStore: UserStore;
|
public userStore: UserStore;
|
||||||
|
|
||||||
|
public onlineStore: OnlineStore;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
/**
|
/**
|
||||||
* Stores
|
* Stores
|
||||||
@ -33,6 +36,7 @@ export default class RootStore {
|
|||||||
this.pageStore = new PageStore(this);
|
this.pageStore = new PageStore(this);
|
||||||
this.uiStore = new UIStore(this);
|
this.uiStore = new UIStore(this);
|
||||||
this.userStore = new UserStore(this);
|
this.userStore = new UserStore(this);
|
||||||
|
this.onlineStore = new OnlineStore(this);
|
||||||
|
|
||||||
makeAutoObservable(this);
|
makeAutoObservable(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import {
|
|||||||
CreateUserParamsType,
|
CreateUserParamsType,
|
||||||
FetchUsersParamsType,
|
FetchUsersParamsType,
|
||||||
UpdateUserContactsParamsType,
|
UpdateUserContactsParamsType,
|
||||||
UpdateUserInfoParamsType,
|
|
||||||
UpdateUserParamsType,
|
UpdateUserParamsType,
|
||||||
} from '@educt/types';
|
} from '@educt/types';
|
||||||
import { IUser, IMe, IPaginationMeta } from '@educt/interfaces';
|
import { IUser, IMe, IPaginationMeta } from '@educt/interfaces';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user