mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 18:29:15 +03:00
feat: rendering notifications
This commit is contained in:
parent
981a0ef642
commit
be418a18aa
15
src/App.tsx
15
src/App.tsx
@ -20,6 +20,7 @@ import { useToast } from '@chakra-ui/react';
|
|||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const {
|
const {
|
||||||
|
userStore,
|
||||||
uiStore: { history },
|
uiStore: { history },
|
||||||
onlineStore,
|
onlineStore,
|
||||||
} = useRootStore();
|
} = useRootStore();
|
||||||
@ -56,6 +57,20 @@ const App = () => {
|
|||||||
status: 'info',
|
status: 'info',
|
||||||
duration: 5000,
|
duration: 5000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!window.location.pathname.includes('messages')) {
|
||||||
|
/**
|
||||||
|
* Update user store
|
||||||
|
*/
|
||||||
|
if (data.notificationId) {
|
||||||
|
userStore.addNotification({
|
||||||
|
id: data.notificationId,
|
||||||
|
content: 'You received a new private message',
|
||||||
|
time: new Date().toISOString(),
|
||||||
|
type: 'MESSAGE',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
useSocketEvent<UserOnlineListType>('user:online', online => onlineStore.loadOnline(online));
|
useSocketEvent<UserOnlineListType>('user:online', online => onlineStore.loadOnline(online));
|
||||||
|
|||||||
@ -39,9 +39,9 @@ const Heading: React.FC<BoxProps> = props => {
|
|||||||
<Button isActive variant={'ghost'} size='sm'>
|
<Button isActive variant={'ghost'} size='sm'>
|
||||||
All
|
All
|
||||||
</Button>
|
</Button>
|
||||||
<Button colorScheme={'blue'} variant='ghost' size='sm'>
|
{/* <Button colorScheme={'blue'} variant='ghost' size='sm'>
|
||||||
Unread
|
Unread
|
||||||
</Button>
|
</Button> */}
|
||||||
</HStack>
|
</HStack>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from '@chakra-ui/react';
|
import { Box, Text } from '@chakra-ui/react';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
|
import { HistoryMessageType } from '@educt/types';
|
||||||
|
|
||||||
export interface IInfoProps {
|
export interface IInfoProps {
|
||||||
fullname: string;
|
fullname: string;
|
||||||
lastMessage: string;
|
lastMessage?: HistoryMessageType | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Info: React.FC<IInfoProps> = ({ fullname, lastMessage }) => {
|
const Info: React.FC<IInfoProps> = ({ fullname, lastMessage }) => {
|
||||||
@ -17,9 +18,12 @@ const Info: React.FC<IInfoProps> = ({ fullname, lastMessage }) => {
|
|||||||
<Box ml='4'>
|
<Box ml='4'>
|
||||||
<Text fontWeight={'semibold'}>
|
<Text fontWeight={'semibold'}>
|
||||||
{isMe ? 'Favorites' : fullname} <br />
|
{isMe ? 'Favorites' : fullname} <br />
|
||||||
<Text as={'span'} fontWeight='normal' fontSize={'sm'} color='gray.500'>
|
{lastMessage && (
|
||||||
{lastMessage}
|
<Text as={'span'} fontWeight='normal' fontSize={'sm'} color='gray.500'>
|
||||||
</Text>
|
{lastMessage.from === me?.id && `You: `}
|
||||||
|
{lastMessage.content}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { Box, Stack, StackProps, Text } from '@chakra-ui/react';
|
import { Stack, StackProps } from '@chakra-ui/react';
|
||||||
import { User } from './User';
|
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';
|
import { useFetchConversations } from '@educt/hooks/queries';
|
||||||
|
|
||||||
const Wrapper: React.FC<StackProps> = ({ children }) => {
|
const Wrapper: React.FC<StackProps> = ({ children }) => {
|
||||||
return (
|
return (
|
||||||
@ -16,14 +16,14 @@ const Wrapper: React.FC<StackProps> = ({ children }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Users: React.FC = observer(() => {
|
const Users: React.FC = observer(() => {
|
||||||
const { data: users } = useFetchUsers();
|
|
||||||
const {
|
const {
|
||||||
userStore: { me },
|
userStore: { me },
|
||||||
onlineStore,
|
onlineStore,
|
||||||
} = useRootStore();
|
} = useRootStore();
|
||||||
|
const { data: conversations } = useFetchConversations();
|
||||||
const { search } = useChatContext();
|
const { search } = useChatContext();
|
||||||
|
|
||||||
if (users === null || me === null)
|
if (conversations === null || me === null)
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Skeleton h='90px' borderRadius={'lg'} />
|
<Skeleton h='90px' borderRadius={'lg'} />
|
||||||
@ -31,9 +31,11 @@ const Users: React.FC = observer(() => {
|
|||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
|
|
||||||
const filtered = (!search ? users : users.filter(u => u.fullname.toLocaleLowerCase().includes(search)))
|
const filtered = (
|
||||||
|
!search ? conversations : conversations.filter(u => u.fullname.toLocaleLowerCase().includes(search))
|
||||||
|
)
|
||||||
.concat()
|
.concat()
|
||||||
.sort(u => (onlineStore.isOnline(u.id) ? -1 : 1));
|
.sort((c1, c2) => new Date(c2.lastMessage.time).valueOf() - new Date(c1.lastMessage.time).valueOf());
|
||||||
|
|
||||||
const isEmpty = filtered.length === 0;
|
const isEmpty = filtered.length === 0;
|
||||||
|
|
||||||
@ -42,27 +44,23 @@ const Users: React.FC = observer(() => {
|
|||||||
{/* Favorites */}
|
{/* Favorites */}
|
||||||
<User id={me.id}>
|
<User id={me.id}>
|
||||||
<User.Avatar isOnline fullname={me.fullname} />
|
<User.Avatar isOnline fullname={me.fullname} />
|
||||||
<User.Info fullname={me.fullname} lastMessage={''} />
|
<User.Info fullname={me.fullname} />
|
||||||
</User>
|
</User>
|
||||||
|
|
||||||
{/* User list */}
|
{/* User list */}
|
||||||
{!isEmpty ? (
|
{!isEmpty &&
|
||||||
filtered.map(
|
filtered.map(
|
||||||
user =>
|
c =>
|
||||||
user.id !== me.id && (
|
c.userId !== me.id && (
|
||||||
<User key={user.id} id={user.id}>
|
<User key={c.userId} id={c.userId}>
|
||||||
<User.Avatar isOnline={onlineStore.isOnline(user.id) || false} fullname={user.fullname} />
|
<User.Avatar isOnline={onlineStore.isOnline(c.userId) || false} fullname={c.fullname} />
|
||||||
<User.Info fullname={user.fullname} lastMessage={''} />
|
<User.Info fullname={c.fullname} lastMessage={c.lastMessage} />
|
||||||
|
<User.Time
|
||||||
|
time={new Date(c.lastMessage.time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||||
|
/>
|
||||||
</User>
|
</User>
|
||||||
)
|
)
|
||||||
)
|
)}
|
||||||
) : (
|
|
||||||
<Box textAlign={'center'}>
|
|
||||||
<Text userSelect={'none'} color='gray.500'>
|
|
||||||
Cannot find any users
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,17 @@
|
|||||||
import React from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Menu, MenuButton, MenuList, MenuItem, MenuGroup, IconButton, MenuDivider, Flex, Text } from '@chakra-ui/react';
|
import {
|
||||||
|
Menu,
|
||||||
|
MenuButton,
|
||||||
|
MenuList,
|
||||||
|
MenuItem,
|
||||||
|
MenuGroup,
|
||||||
|
IconButton,
|
||||||
|
MenuDivider,
|
||||||
|
Flex,
|
||||||
|
Text,
|
||||||
|
useDisclosure,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import { Skeleton } from '@chakra-ui/skeleton';
|
import { Skeleton } from '@chakra-ui/skeleton';
|
||||||
import { MdNotifications, MdOutlineMessage } from 'react-icons/md';
|
import { MdNotifications, MdOutlineMessage } from 'react-icons/md';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
@ -10,26 +21,69 @@ import { observer } from 'mobx-react';
|
|||||||
*/
|
*/
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
|
import { SocketContext } from '@educt/contexts';
|
||||||
|
import { runInAction } from 'mobx';
|
||||||
|
|
||||||
const Notifications: React.FC = observer(() => {
|
const Notifications: React.FC = observer(() => {
|
||||||
const {
|
const {
|
||||||
userStore: { me },
|
userStore: { me, isLoading },
|
||||||
} = useRootStore();
|
} = useRootStore();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const { socket } = useContext(SocketContext);
|
||||||
|
const { onOpen, onClose, isOpen } = useDisclosure();
|
||||||
|
const [count, setCount] = useState<number>(0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load count of notifications
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (me) {
|
||||||
|
setCount(me.notifications.length);
|
||||||
|
}
|
||||||
|
}, [me, me?.notifications.length]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark notifications as read
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen && count > 0 && me) {
|
||||||
|
const messages = me.notifications.filter(n => n.type === 'MESSAGE');
|
||||||
|
socket?.emit('notification:read', {
|
||||||
|
userId: me.id,
|
||||||
|
ids: messages.map(n => n.id),
|
||||||
|
});
|
||||||
|
setCount(0);
|
||||||
|
}
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete notifications with type `message` when on message page
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (window.location.pathname.includes('messages') && me) {
|
||||||
|
socket?.emit('notification:read', {
|
||||||
|
userId: me.id,
|
||||||
|
ids: me.notifications.filter(n => n.type === 'MESSAGE').map(n => n.id),
|
||||||
|
});
|
||||||
|
|
||||||
|
setCount(0);
|
||||||
|
runInAction(() => {
|
||||||
|
me.notifications = me.notifications.filter(n => n.type !== 'MESSAGE');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [me]);
|
||||||
|
|
||||||
if (me === null) {
|
if (me === null) {
|
||||||
return <Skeleton width='50px' height='40px' mr={3} ml={3} borderRadius='md' />;
|
return <Skeleton width='50px' height='40px' mr={3} ml={3} borderRadius='md' />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const count = me.notifications.length;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter types of notifications
|
* Filter types of notifications
|
||||||
*/
|
*/
|
||||||
const messages = me.notifications.filter(notification => notification.type === 'MESSAGE');
|
const messages = me.notifications.filter(notification => notification.type === 'MESSAGE');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu>
|
<Menu isOpen={isOpen} onClose={onClose} onOpen={onOpen} isLazy>
|
||||||
<MenuButton
|
<MenuButton
|
||||||
as={IconButton}
|
as={IconButton}
|
||||||
mx='2'
|
mx='2'
|
||||||
@ -37,7 +91,7 @@ const Notifications: React.FC = observer(() => {
|
|||||||
icon={<MdNotifications />}
|
icon={<MdNotifications />}
|
||||||
_after={{
|
_after={{
|
||||||
content: `"${count && count > 0 && count}"`,
|
content: `"${count && count > 0 && count}"`,
|
||||||
display: `${count && count > 0 ? 'block' : 'none'}`,
|
display: `${!isLoading ? (count && count > 0 ? 'block' : 'none') : 'none'}`,
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '5px',
|
top: '5px',
|
||||||
right: '5px',
|
right: '5px',
|
||||||
@ -52,6 +106,7 @@ const Notifications: React.FC = observer(() => {
|
|||||||
<MenuList minWidth='300px'>
|
<MenuList minWidth='300px'>
|
||||||
<MenuGroup title={count && count > 0 ? `${count} new notifications` : 'Notifications'}>
|
<MenuGroup title={count && count > 0 ? `${count} new notifications` : 'Notifications'}>
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
|
|
||||||
{/* MESSAGES */}
|
{/* MESSAGES */}
|
||||||
{messages.length !== 0 && (
|
{messages.length !== 0 && (
|
||||||
<MenuItem onClick={() => history.push('/messages')} py='3' icon={<MdOutlineMessage />}>
|
<MenuItem onClick={() => history.push('/messages')} py='3' icon={<MdOutlineMessage />}>
|
||||||
|
|||||||
@ -2,11 +2,10 @@ 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<ServerToClientEvents, ClientToServerEvents> | null }>({
|
export const SocketContext = createContext<{ socket: Socket | null }>({
|
||||||
socket: null,
|
socket: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
25
src/hooks/queries/chat/useFetchConversations.ts
Normal file
25
src/hooks/queries/chat/useFetchConversations.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import useAsync from '@educt/hooks/useAsync';
|
||||||
|
import { ChatServiceInstance } from '@educt/services/ChatService';
|
||||||
|
import { ConversationType } from '@educt/types';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
const useFetchConversations = () => {
|
||||||
|
const fetch = async () => {
|
||||||
|
try {
|
||||||
|
const result = await ChatServiceInstance.fetchConversations();
|
||||||
|
return result.data;
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const { execute: fetchConversations, ...state } = useAsync<ConversationType[], Parameters<typeof fetch>>(fetch);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchConversations();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { ...state };
|
||||||
|
};
|
||||||
|
|
||||||
|
export { useFetchConversations };
|
||||||
@ -46,6 +46,11 @@ import { useUpdateCategory } from './category/useUpdateCategory';
|
|||||||
*/
|
*/
|
||||||
import { useFetchStat } from './stat/useFetchStat';
|
import { useFetchStat } from './stat/useFetchStat';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chat
|
||||||
|
*/
|
||||||
|
import { useFetchConversations } from './chat/useFetchConversations';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
useLogin,
|
useLogin,
|
||||||
useLogout,
|
useLogout,
|
||||||
@ -71,4 +76,5 @@ export {
|
|||||||
useUpdateCategory,
|
useUpdateCategory,
|
||||||
useFetchCategories,
|
useFetchCategories,
|
||||||
useFetchStat,
|
useFetchStat,
|
||||||
|
useFetchConversations,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -143,9 +143,6 @@ export interface IMe extends IUser {
|
|||||||
courses?: UserCourseType[];
|
courses?: UserCourseType[];
|
||||||
likes: Array<Pick<ICourse, 'id'>>;
|
likes: Array<Pick<ICourse, 'id'>>;
|
||||||
notifications: Array<NotificationType>;
|
notifications: Array<NotificationType>;
|
||||||
isAdmin: boolean;
|
|
||||||
isTeacher: boolean;
|
|
||||||
isStudent: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUserContacts {
|
export interface IUserContacts {
|
||||||
|
|||||||
24
src/services/ChatService.ts
Normal file
24
src/services/ChatService.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { IApiRespose } from '@educt/interfaces';
|
||||||
|
import { ConversationType } from '@educt/types';
|
||||||
|
import { AxiosInstance } from 'axios';
|
||||||
|
import { ApiServiceInstance } from '.';
|
||||||
|
|
||||||
|
class ChatService {
|
||||||
|
public api: AxiosInstance;
|
||||||
|
|
||||||
|
constructor(api: AxiosInstance) {
|
||||||
|
this.api = api;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all conversations
|
||||||
|
*
|
||||||
|
* @returns Array of conversations
|
||||||
|
*/
|
||||||
|
public async fetchConversations(): Promise<IApiRespose<ConversationType[]>> {
|
||||||
|
const result = await this.api.get('/v1/chat/conversations');
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ChatServiceInstance = new ChatService(ApiServiceInstance.api);
|
||||||
@ -7,6 +7,7 @@ import * as helpers from '@educt/helpers';
|
|||||||
import {
|
import {
|
||||||
CreateUserParamsType,
|
CreateUserParamsType,
|
||||||
FetchUsersParamsType,
|
FetchUsersParamsType,
|
||||||
|
NotificationType,
|
||||||
UpdateUserContactsParamsType,
|
UpdateUserContactsParamsType,
|
||||||
UpdateUserParamsType,
|
UpdateUserParamsType,
|
||||||
} from '@educt/types';
|
} from '@educt/types';
|
||||||
@ -23,10 +24,12 @@ import { UserServiceInstance } from '@educt/services';
|
|||||||
*/
|
*/
|
||||||
import RootStore from './RootStore';
|
import RootStore from './RootStore';
|
||||||
|
|
||||||
|
type MeMetadata = { isAdmin: boolean; isTeacher: boolean; isStudent: boolean };
|
||||||
|
|
||||||
export default class UserStore {
|
export default class UserStore {
|
||||||
public root: RootStore;
|
public root: RootStore;
|
||||||
|
|
||||||
public me: IMe | null = null;
|
public me: (IMe & MeMetadata) | null = null;
|
||||||
|
|
||||||
public users: IUser[] | null = null;
|
public users: IUser[] | null = null;
|
||||||
|
|
||||||
@ -60,50 +63,24 @@ export default class UserStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async loadCurrentUserData() {
|
public async loadCurrentUserData() {
|
||||||
const result = await UserServiceInstance.fetchMe();
|
try {
|
||||||
const {
|
this.setLoading(true);
|
||||||
data: {
|
const result = await UserServiceInstance.fetchMe();
|
||||||
id,
|
const { data } = result;
|
||||||
first_name,
|
|
||||||
last_name,
|
|
||||||
fullname,
|
|
||||||
about,
|
|
||||||
last_login,
|
|
||||||
email,
|
|
||||||
roles,
|
|
||||||
likes,
|
|
||||||
notifications,
|
|
||||||
contacts,
|
|
||||||
courses,
|
|
||||||
created_at,
|
|
||||||
updated_at,
|
|
||||||
},
|
|
||||||
} = result;
|
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
// TODO Fix this
|
this.me = {
|
||||||
this.me = {
|
...data,
|
||||||
id,
|
isAdmin: helpers.userContainRoles(data.roles, [UserRoleEnum.ADMIN]),
|
||||||
first_name,
|
isTeacher: helpers.userContainRoles(data.roles, [UserRoleEnum.TEACHER]),
|
||||||
last_name,
|
isStudent: helpers.userContainRoles(data.roles, [UserRoleEnum.STUDENT]),
|
||||||
email,
|
};
|
||||||
roles,
|
});
|
||||||
fullname,
|
|
||||||
about,
|
|
||||||
last_login,
|
|
||||||
likes,
|
|
||||||
notifications,
|
|
||||||
contacts,
|
|
||||||
courses,
|
|
||||||
isAdmin: helpers.userContainRoles(roles, [UserRoleEnum.ADMIN]),
|
|
||||||
isTeacher: helpers.userContainRoles(roles, [UserRoleEnum.TEACHER]),
|
|
||||||
isStudent: helpers.userContainRoles(roles, [UserRoleEnum.STUDENT]),
|
|
||||||
created_at,
|
|
||||||
updated_at,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
} finally {
|
||||||
|
this.setLoading(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createUser(data: CreateUserParamsType, paramsContext?: FetchUsersParamsType) {
|
public async createUser(data: CreateUserParamsType, paramsContext?: FetchUsersParamsType) {
|
||||||
@ -217,4 +194,10 @@ export default class UserStore {
|
|||||||
this.me = null;
|
this.me = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addNotification(notification: NotificationType) {
|
||||||
|
if (this.me) {
|
||||||
|
this.me.notifications.unshift(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
src/types/index.d.ts
vendored
7
src/types/index.d.ts
vendored
@ -74,6 +74,13 @@ export type HistoryMessageType = {
|
|||||||
to: string;
|
to: string;
|
||||||
content: string;
|
content: string;
|
||||||
time: string;
|
time: string;
|
||||||
|
notificationId?: string | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ConversationType = {
|
||||||
|
userId: string;
|
||||||
|
fullname: string;
|
||||||
|
lastMessage: HistoryMessageType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NotificationType = {
|
export type NotificationType = {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user