Sfoglia il codice sorgente

done message notificatioN

unknown 3 anni fa
parent
commit
b003dccd74

File diff suppressed because it is too large
+ 1 - 1
.eslintcache


+ 10 - 10
src/App.tsx

@@ -68,16 +68,16 @@ function App() {
         <CLoad/>
       </Suspense>
       <ToastContainer
-        position="top-right"
-        autoClose={5000}
-        hideProgressBar={false}
-        newestOnTop={false}
-        closeOnClick
-        rtl={false}
-        pauseOnFocusLoss
-        draggable
-        pauseOnHover
-      />
+      position="bottom-right"
+      autoClose={5000}
+      hideProgressBar={false}
+      newestOnTop={false}
+      closeOnClick
+      rtl={false}
+      pauseOnFocusLoss
+      draggable
+      pauseOnHover
+      />      
     </MuiPickersUtilsProvider>
   </div>
 )}

+ 10 - 10
src/api-data/index.ts

@@ -36,7 +36,6 @@ const setToken = {
 const authorizeUser = async (number:string,country:string):Promise<string | undefined> => {
   try {
     const { data : {data} } = await axios.post('/auth/register', { number,country });
-    success(`check ${number}`);
     success(`code ${data}`);
     return data
   } catch (e) {
@@ -47,7 +46,6 @@ const authorizeUser = async (number:string,country:string):Promise<string | unde
 const loginUser = async <T>(number:string,code:string):Promise<T | undefined> => {
   try {
     const { data : {data} } = await axios.post('/auth/login', { number,code });
-    success(`AUTHORIZED`);
     return data
   } catch (e) {
     return undefined
@@ -57,7 +55,6 @@ const loginUser = async <T>(number:string,code:string):Promise<T | undefined> =>
 const logoutUser = async <T>():Promise<T | undefined> => {
   try {
     const { data } = await axios.post('/auth/logout');
-    success(`LOGOUT`);
     return data
   } catch (e) {
     return undefined
@@ -67,7 +64,6 @@ const logoutUser = async <T>():Promise<T | undefined> => {
 const onlineUser = async <T>():Promise<T | undefined> => {
   try {
     const { data } = await axios.post('/auth/online');
-    success(`ONLINE`);
     return data
   } catch (e) {
     return undefined
@@ -77,7 +73,6 @@ const onlineUser = async <T>():Promise<T | undefined> => {
 const updateCredentials = async <T>(name: string,lastName: string):Promise<T | undefined> => {
   try {
     const { data : {data} } = await axios.patch('/users/current', { name, lastName });
-    success(`CREDENTIALS UPDATED`);
     return data
   } catch (e) {
     return undefined
@@ -87,7 +82,6 @@ const updateCredentials = async <T>(name: string,lastName: string):Promise<T | u
 const updateUserAvatar = async <T>(formData: object): Promise<T | undefined> => {
   try {
     const { data : {data} } = await axios.patch('/users/avatars', formData);
-    success(`AVATAR UPDATED`);
     return data
   } catch (e) {
     return undefined
@@ -97,7 +91,6 @@ const updateUserAvatar = async <T>(formData: object): Promise<T | undefined> =>
 const currentUser = async <T>(): Promise<T | undefined> => {
   try {
     const { data : {data} } = await axios.get('/users/current');
-    success(`LOGIN`);
     return data
   } catch (e) {
     return undefined
@@ -107,10 +100,8 @@ const currentUser = async <T>(): Promise<T | undefined> => {
 const addContact = async <T>(number:string): Promise<T | undefined> => {
   try {
     const { data : {data} } = await axios.post('/contacts', { number });
-    success('CONTACT ADDED');
     return data
   } catch (e) {
-    error('ALREADY ADDED OR NOT EXIST')
     return undefined
   }
 };
@@ -145,7 +136,15 @@ const getChat = async <T>(id:string): Promise<T | undefined> => {
 const muteChat = async <T>(id:string): Promise<T | undefined> => {
   try {
     const { data: { data } } = await axios.post('/chats/mute', {id});
-    success(`MUTE`);
+    return data
+  } catch (e) {
+    return undefined
+  }
+};
+
+const seenChat = async <T>(id:string): Promise<T | undefined> => {
+  try {
+    const { data: { data } } = await axios.post('/chats/seen', { id });
     return data
   } catch (e) {
     return undefined
@@ -195,6 +194,7 @@ export {
   startChat,
   getChat,
   muteChat,
+  seenChat,
   getChats,
   sentMessageById,
   getMessagesById

+ 14 - 10
src/components/HomePage/LeftBar/ChatsList/index.tsx

@@ -12,11 +12,12 @@ import shortid from 'shortid';
 import { useSelector, useDispatch } from 'react-redux';
 
 import AlertInfo from '../../../reusableComponents/AlertInfo'
-import doubleCheck from '../../../../img/clipart289625.png'
+import DoneAllIcon from '@mui/icons-material/DoneAll';
 import { firstLetter, slicedWord, timeStamp } from '../../../../helpers'
 import { getState } from '../../../../redux/chats/selector'
 import { asyncGetChats } from '../../../../redux/chats/operations'
 import { asyncStartChatById } from '../../../../redux/chat/operations'
+import { actionScroll } from '../../../../redux/control/action'
 
 const StyledBadge = styled(Badge)(({ theme }) => ({
   '& .MuiBadge-badge': {
@@ -163,19 +164,22 @@ const ChatsList = () => {
   const { total, chats , lastMessages,lastOnline } = useSelector(getState)
  
 
-  const handleListItemClick = async (i: number, companionId: string) => {
-      await dispatch(asyncStartChatById(companionId))
-      setSelectedIndex(i);
+  const handleListItemClick = (i: number, companionId: string) => {
+    dispatch(asyncStartChatById(companionId))
+    dispatch(actionScroll(false))
+    setSelectedIndex(i);
   }
 
-  const handleNewMsgS = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>, i: number) => {
+  const handleNewMsgS = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>, i: number,companionId: string) => {
     e.stopPropagation()
+    dispatch(asyncStartChatById(companionId))
+    dispatch(actionScroll(true))
     console.log(i,'index','clicked read new messages')
   }
 
   useEffect(() => {
     const handleReset = () => dispatch(asyncGetChats())
-    const idInterval = setInterval(handleReset, 10000);
+    const idInterval = setInterval(handleReset, 1500);
     return () => clearInterval(idInterval);
   }, [dispatch]);
   
@@ -187,7 +191,7 @@ const ChatsList = () => {
   return total !== '0' ? (
     <List className={classes.list} component="nav"
           aria-label="main mailbox folders">
-      {chats.map(({name, lastName, avatarUrl, updatedAt, color,companionId,mute }, i: number) => 
+      {chats.map(({name, lastName, avatarUrl, updatedAt, color,companionId,mute,seen,total }, i: number) => 
           <ListItemButton
           key={shortid.generate()}
           selected={selectedIndex === i}
@@ -210,13 +214,13 @@ const ChatsList = () => {
              `${firstLetter(name)}${slicedWord(name, 8, 1)} joined Telegram`} />
           <ListItemIcon className={classes.listItem_iconRight}>
                <div className={classes.listItem_iconTimeChecked}>
-                 <img alt='double check' width="16" height='16' src={doubleCheck} />
+                 {total-seen === 0 && <DoneAllIcon style={{ color: '#18bd03' }} fontSize='small' />}
               <Typography className={classes.listItem_icon_time} variant="h6" color="initial">
                 {lastMessages[i] ? timeStamp(lastMessages[i].updatedAt) :
                 timeStamp(updatedAt)}</Typography>
                </div>
-            {lastMessages[i] ? <button onClick={(e) => handleNewMsgS(e, i)}
-              className={mute?classes.listItem_iconRightBtnMute:classes.listItem_iconRightBtn}>0</button> :
+            {lastMessages[i] && total > seen ? <button onClick={(e) => handleNewMsgS(e, i,companionId)}
+              className={mute?classes.listItem_iconRightBtnMute:classes.listItem_iconRightBtn}>{total-seen}</button> :
               <button  className={classes.listItem_iconRightBtnHidden}/>}
           </ListItemIcon>            
         </ListItemButton>)}

+ 6 - 3
src/components/HomePage/RightBar/ChatBar/SendMessage/index.tsx

@@ -9,10 +9,12 @@ import CloseIcon from '@mui/icons-material/Close';
 import { useState } from "react";
 import { useSelector, useDispatch } from "react-redux";
 
-import { asyncSentMessageById } from '../../../../../redux/messages/operations'
-import { getChat } from '../../../../../redux/chat/selector'
 import FilesMenu from "../FilesMenu";
 import NotDone from "../../../../reusableComponents/NotDone";
+import { asyncSentMessageById } from '../../../../../redux/messages/operations'
+import { getChat } from '../../../../../redux/chat/selector'
+import { playNotification } from "../../../../../helpers";
+
 
 const useStyles = makeStyles({   
     container: {
@@ -139,7 +141,7 @@ interface ISendMessage{
 const SendMessage = ({isArrow,handleScrollTo}:ISendMessage) => {
     const classes = useStyles();
     const dispatch = useDispatch()
-    const {companionId} = useSelector(getChat)
+    const { companionId } = useSelector(getChat)
     const [value, setValue] = useState<string>('')
     const [file, setFile] = useState<any>(null)
     const [isOpenMenu, setIsOpenMenu] = useState<boolean>(false)
@@ -152,6 +154,7 @@ const SendMessage = ({isArrow,handleScrollTo}:ISendMessage) => {
         isOpenEmoji && setIsOpenEmoji(false)
         dispatch(asyncSentMessageById(companionId, message))
         setTimeout(handleScrollTo, 3000);
+        playNotification('http://localhost:3000/send.mp3')
     }    
     const handleTextarea = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
         isOpenMenu&&setIsOpenMenu(false)

+ 21 - 12
src/components/HomePage/RightBar/ChatBar/index.tsx

@@ -10,7 +10,10 @@ import AlertInfo from "../../../reusableComponents/AlertInfo";
 import { getMessages } from '../../../../redux/messages/selector'
 import { getNumber } from '../../../../redux/authorization/selector'
 import { getChat } from '../../../../redux/chat/selector'
+import { getScroll } from '../../../../redux/control/selector'
+import { actionScroll } from '../../../../redux/control/action'
 import { asyncGetMessagesById } from '../../../../redux/messages/operations'
+import { seenChat } from "../../../../api-data";
 const debounce = require('lodash.debounce');
 
 const useStyles = makeStyles({   
@@ -39,30 +42,36 @@ const ChatBar = () => {
   const messages = useSelector(getMessages)
   const userNumber = useSelector(getNumber)
   const { companionId } = useSelector(getChat)
-  const [isArrow,setIsArrow] = useState<boolean>(false)
+  const scroll = useSelector(getScroll)
+  const [isArrow, setIsArrow] = useState<boolean>(false)
   const divRef = useRef<any | null>(null)
+  const handleScrollTo = () => {
+     divRef.current&&divRef.current.scrollTo({
+     top: divRef.current.scrollHeight,
+     behavior: 'smooth'
+     })
+   }
   const handleScroll = ({ target }: any) => {
     const different = target.scrollHeight - target.scrollTop
-    setIsArrow(different < 1500?false:true)
-  }
-  const handleScrollTo = () => {
-    divRef.current&&divRef.current.scrollTo({
-    top: divRef.current.scrollTopMax,
-    behavior: 'smooth'
-    })
+    if (different < 900) seenChat(companionId)
+    setIsArrow(different < 1500 ? false : true)
   }
   const debouncedHandleScroll = useCallback(debounce(handleScroll, 300), []);
 
   useEffect(() => {
-    dispatch(asyncGetMessagesById(companionId,handleScrollTo))
-  }, [dispatch, companionId])
+    if (scroll) {
+      dispatch(asyncGetMessagesById(companionId, handleScrollTo))
+      dispatch(actionScroll(false))
+    }
+  }, [dispatch,scroll, companionId])  
   
   useEffect(() => {
+    dispatch(asyncGetMessagesById(companionId, handleScrollTo))
     const handleReset = () => dispatch(asyncGetMessagesById(companionId,null))
     const idInterval = setInterval(handleReset, 3000);
     return () => clearInterval(idInterval);
-  }, [dispatch, companionId]);  
-  
+  }, [dispatch, companionId]);
+
 
   return (
       <div ref={divRef} className={classes.container} onScroll={debouncedHandleScroll}>

+ 2 - 1
src/components/HomePage/RightBar/index.tsx

@@ -39,7 +39,8 @@ const useStyles = makeStyles({
 const RightBar = () => {
   const classes = useStyles()
   const isOpen = useSelector(getIsOpen)
-  const {_id} = useSelector(getChat)
+  const { _id } = useSelector(getChat)
+
     return _id?
       <Grid item lg={9} className={classes.container}>
         <HeaderBar />

+ 3 - 2
src/components/HomePage/index.tsx

@@ -11,9 +11,10 @@ const useStyles = makeStyles({
 })
 
 const HomePage = () => {
-   const classes = useStyles()
+    const classes = useStyles()
     return (
-       <Grid className={classes.container} container spacing={0} >
+        <Grid className={classes.container}
+           container spacing={0} >
           <LeftBar/>
           <RightBar/>
        </Grid>

+ 22 - 1
src/helpers/index.ts

@@ -1,3 +1,5 @@
+import { toast } from 'react-toastify';
+
 const format = (a: string) => a.split(' ').join('').trim()
 
 const firstLetter = (word: string) => word.slice(0, 1).toUpperCase()
@@ -19,5 +21,24 @@ const timeStampFilter = (updatedAt: string) => new Date(updatedAt).toLocaleStrin
     day: 'numeric',
 })
 
+const playNotification = (url:string) => {
+  const audio = new Audio(url);
+  audio.play();
+}
+
+const notification = (name: string, onClick: () => void) => {
+    toast.info(`new ✉️ from ${name}`, {
+      position: "bottom-right",
+      autoClose: 5000,
+      hideProgressBar: false,
+      closeOnClick: false,
+      pauseOnHover: true,
+      draggable: true,
+      progress: undefined,
+      onClick
+    })
+}
+
+
 
-export { format,firstLetter,slicedWord,timeStamp,timeStampFilter }
+export { format,firstLetter,slicedWord,timeStamp,timeStampFilter,playNotification,notification }

+ 1 - 5
src/redux/chat/operations/index.ts

@@ -1,4 +1,3 @@
-import { actionIsLoading } from '../../loading/action';
 import {
   actionSelectChat,
   actionGetChat
@@ -9,12 +8,9 @@ import { TChat } from '../../../typescript/redux/chat/types'
 
 const asyncStartChatById = (id:string) => async (dispatch:any) => {
   try {
-    dispatch(actionIsLoading(true));
     const data = await startChat<TChat>(id)
     data&&dispatch(actionSelectChat(data))
-  } finally {
-    dispatch(actionIsLoading(false));
-  }
+  } catch(e) {}
 };
 
 const asyncGetChatById = (id:string) => async (dispatch:any) => {

+ 2 - 0
src/redux/chat/reducer/index.ts

@@ -14,6 +14,8 @@ const initialState: IChatState = {
      color: '',
      online: '',
      mute: false,
+     seen: 0,
+     total: 0,
      number:'',
      _id: '',
      companionId: '',

+ 2 - 9
src/redux/contacts/operations/index.ts

@@ -1,4 +1,3 @@
-import { actionIsLoading } from '../../loading/action';
 import {
   actionGetContactsSuccess,
   actionGetContactsReject
@@ -8,23 +7,17 @@ import { IContactsState } from '../../../typescript/redux/contacts/interfaces'
 
 const asyncGetContacts = () => async (dispatch:any) => {
   try {
-    dispatch(actionIsLoading(true));
     const data = await getContacts<IContactsState>()
     data&&dispatch(actionGetContactsSuccess(data))
   } catch (e) {
     dispatch(actionGetContactsReject())
-  } finally {
-    dispatch(actionIsLoading(false));
-  }
+  } 
 };
 
 const asyncAddContact = (number:string) => async (dispatch:any) => {
   try {
-    dispatch(actionIsLoading(true));
     await addContact(number)
-  } finally {
-    dispatch(actionIsLoading(false));
-  }
+  } catch(e) {}
 };
 
 

+ 6 - 2
src/redux/control/action/index.ts

@@ -1,9 +1,13 @@
 import { createAction } from '@reduxjs/toolkit';
-import { TIsOpen } from '../../../typescript/redux/control/types'
+import { TIsOpen,TScroll } from '../../../typescript/redux/control/types'
 
 const actionIsOpen= createAction('control/isOpen', (value:TIsOpen) => ({
   payload: value,
 }));
 
+const actionScroll= createAction('control/scroll', (value:TScroll) => ({
+  payload: value,
+}));
+
 
-export { actionIsOpen };
+export { actionIsOpen,actionScroll };

+ 6 - 2
src/redux/control/reducer/index.ts

@@ -1,15 +1,19 @@
 import { createReducer } from '@reduxjs/toolkit';
-import { actionIsOpen } from '../action';
-import { IControlState,IPayloadIsOpen } from '../../../typescript/redux/control/interfaces'
+import { actionIsOpen,actionScroll } from '../action';
+import { IControlState,IPayloadIsOpen,IPayloadScroll } from '../../../typescript/redux/control/interfaces'
 
 const initialState:IControlState = {
   isOpen: '',
+  scroll: false,
 }
 
 const reducerControl = createReducer(initialState, {
   [actionIsOpen.type]: (state, { payload:isOpen }:IPayloadIsOpen) => {
     return {...state,isOpen}
   },
+  [actionScroll.type]: (state, { payload:scroll }:IPayloadScroll) => {
+    return {...state,scroll}
+  },  
 });
 
 export default reducerControl;

+ 2 - 1
src/redux/control/selector/index.ts

@@ -1,6 +1,7 @@
 import { IState } from '../../../typescript/redux/interfaces'
 
 const getIsOpen = (state: IState) => state.control.isOpen;
+const getScroll = (state: IState) => state.control.scroll;
 const getState = (state:IState) => state.control;
 
-export { getIsOpen,getState };
+export { getIsOpen,getScroll,getState };

+ 1 - 5
src/redux/messages/operations/index.ts

@@ -1,4 +1,3 @@
-import { actionIsLoading } from '../../loading/action';
 import {
   actionGetMessagesSuccess,
   actionGetMessagesReject
@@ -19,11 +18,8 @@ const asyncGetMessagesById= (id:string,cb:any) => async (dispatch:any) => {
 
 const asyncSentMessageById= (id:string,message:any) => async (dispatch:any) => {
   try {
-    dispatch(actionIsLoading(true));
     await sentMessageById(id, message)
-  } finally {
-    dispatch(actionIsLoading(false));
-  }
+  } catch(e) {}
 };
 
 export { asyncGetMessagesById,asyncSentMessageById };

+ 2 - 0
src/typescript/redux/chat/types.ts

@@ -5,6 +5,8 @@ export type TChat = {
   color: string,
   online: string,
   mute: boolean,
+  seen: number,
+  total: number,
   number:string,
   _id: string,
   companionId: string,

+ 2 - 0
src/typescript/redux/chats/types.ts

@@ -5,6 +5,8 @@ export type TChat = {
   color: string,
   online: string,
   mute: boolean,
+  seen: number,
+  total: number,
   number:string,
   _id: string,
   companionId: string,

+ 6 - 1
src/typescript/redux/control/interfaces.ts

@@ -1,12 +1,17 @@
-import { TIsOpen } from './types'
+import { TIsOpen,TScroll } from './types'
 
 export interface IControlState {
   isOpen: TIsOpen,
+  scroll: TScroll
 }
 
 export interface IPayloadIsOpen {
   payload:TIsOpen
 }
 
+export interface IPayloadScroll {
+  payload:TScroll
+}
+
 
 

+ 3 - 1
src/typescript/redux/control/types.ts

@@ -1 +1,3 @@
-export type TIsOpen = ('' | 'credentials' | 'search' | 'menu')
+export type TIsOpen = ('' | 'credentials' | 'search' | 'menu')
+
+export type TScroll = boolean