2 次代碼提交 11e2f51e70 ... 76ae0a7963

作者 SHA1 備註 提交日期
  Ivar 76ae0a7963 minor fixes 2 年之前
  Ivar 7584a7a9ec minor fixes, part of adaptive 2 年之前

+ 1 - 1
public/index.html

@@ -7,7 +7,7 @@
     <meta name="theme-color" content="#000000" />
     <meta
       name="description"
-      content="Web site created using create-react-app"
+      content="TopChat"
     />
     <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
     <!--

+ 53 - 31
src/App.js

@@ -1,8 +1,9 @@
-import React, {useState, useEffect, useRef} from 'react'
+import React, {useState, useEffect, useRef, createContext } from 'react'
 import './App.scss'
 import {Provider, connect} from 'react-redux'
 import {Router, Route, Link, Redirect, Switch} from 'react-router-dom'
 import createHistory from "history/createBrowserHistory"
+
 import {
   store,
   socket
@@ -14,20 +15,63 @@ import {
   Main,
   AsidePage,
   CChatsPage,
-  MsgPage
+  CMsgPage,
+  PageNoChat
 } from "./pages"
 
 import Grid from '@mui/material/Grid'
+import { useTheme } from '@mui/material/styles'
+import useMediaQuery from '@mui/material/useMediaQuery'
+
 
+export const history = createHistory()
 
 
-const PageNoChat = () => (
-  <div style={{ height: "100vh", width: "100%", backgroundColor: "#eee" }}>
-  </div>
-)
+export const AdaptiveContext = createContext(null)
 
-const AuthSwitch = ({ token }) => {
+const AdaptiveGrid = ({ }) => {
+
+  // const [,route, histId] = history.location.pathname.split('/')
+
+  const theme = useTheme()
+  const matches = useMediaQuery(theme.breakpoints.up('sm'))
+
+  const [aside, setAside] = useState(true)
+
+  return (
+    <>
+     <AdaptiveContext.Provider value={ {setAside} }>
+
+      { (matches || aside) &&
+        <Grid item xs={12} sm={4}>
+          <AsidePage>
+            <Switch> 
+              <Route path="/main/:_id" component={CChatsPage} /> 
+              <Route path="/find" component={PageNoChat} />
+              <Route path="*" component={CChatsPage} /> 
+            </Switch>
+          </AsidePage>
+        </Grid>
+      }
 
+      { (matches || !aside) && 
+        <Grid item xs={12} sm={8}>
+          <Switch> 
+            <Route path="/main" component={PageNoChat} exact/>
+            <Route path="/main/:_id" component={CMsgPage} exact/>
+            <Route path="*" component={PageNoChat} /> 
+          </Switch>
+        </Grid>
+      }
+       
+     </AdaptiveContext.Provider>    
+    </>
+  )
+}
+
+
+const AuthSwitch = ({ token }) => {
+  
   if (token) {
     console.log('подключение сокета')
     socket.emit('jwt', token)
@@ -36,44 +80,23 @@ const AuthSwitch = ({ token }) => {
   return (
     <>      
       {token ? 
-      <>
         <Main>
 
-          <Grid item xs={12} sm={4}>
-              <AsidePage>
-                <Switch> 
-                  <Route path="/main/:_id" component={CChatsPage} /> 
-                  <Route path="/find" component={PageNoChat} />
-                  <Route path="*" component={CChatsPage} /> 
-                </Switch>
-              </AsidePage>
-          </Grid>
-
-          <Grid item xs={12} sm={8}>
-            <Switch> 
-              <Route path="/main" component={PageNoChat} exact/>
-              <Route path="/main/:_id" component={MsgPage} exact/>
-              <Route path="*" component={PageNoChat} /> 
-            </Switch>
-          </Grid>
+          <AdaptiveGrid />
 
         </Main>
-      </>
-      : 
-      <>      
+      :    
         <Switch>        
           <Route path="/reg" component={Register} />
           <Route path="/login" component={Login} />
           <Route path="*" component={Login} />
         </Switch>
-      </>
       }
     </>
   )
 }
 const CAuthSwitch = connect(state => ({ token: state.auth.token || null }))(AuthSwitch)
 
-export const history = createHistory()
 
 function App() {
 
@@ -84,7 +107,6 @@ function App() {
       <Provider store={store}>
           <div className="App">
                                 
-
            <CAuthSwitch />
           
           </div>

+ 13 - 27
src/components/Avatar.jsx

@@ -18,25 +18,23 @@ const big = {
 }
 
 
-export const UserAvatar = ({  profile, bigSize=false }) => {
-  //  console.log(profile)
-   
-  function getUrl() {
-    if (profile.localUrl) {
-      return profile.localUrl
-    } else if (profile.avatar?.url) {
-      return backURL + profile.avatar?.url
-    } else {
-      return false
-    }
+function getUrl(obj) {
+  if (obj.localUrl) {
+    return obj.localUrl
+  } else if (obj.avatar?.url) {
+    return backURL + obj.avatar?.url
+  } else {
+    return false
   }
+}
 
+export const UserAvatar = ({  profile, bigSize=false }) => {
   return (
     <>
     {
-        getUrl() ?
+        getUrl(profile) ?
         <Avatar  sx={ bigSize ? big : small } 
-                alt={profile.nick || profile.login } src={getUrl()} /> :
+                alt={profile.nick || profile.login } src={getUrl(profile)} /> :
         <Avatar  sx={ bigSize ? big : small }
                 {...stringColor.stringAvatar(profile.nick || profile.login)} /> 
     }        
@@ -46,25 +44,13 @@ export const UserAvatar = ({  profile, bigSize=false }) => {
 export const CMyAvatar = connect( state => ({ profile: state.promise.myProfile?.payload || {} }))(UserAvatar)
 
 
-
 export const ChatAvatar = ({ chat, bigSize=false }) => {
- 
-  function getUrl() {
-    if (chat.localUrl) {
-      return chat.localUrl
-    } else if (chat.avatar?.url) {
-      return backURL + chat.avatar?.url
-    } else {
-      return false
-    }
-  }
-
   return (
     <>
     {
-        getUrl() ?
+        getUrl(chat) ?
         <Avatar  sx={ bigSize ? big : small } 
-                alt={chat.title } src={getUrl()} /> :
+                alt={chat.title } src={getUrl(chat)} /> :
         <Avatar  sx={ bigSize ? big : small }
                 {...stringColor.stringAvatar(chat.title)} /> 
     }        

+ 10 - 21
src/components/ChatList.jsx

@@ -1,10 +1,10 @@
-import React, {useState, useEffect, useRef} from 'react';
+import React, {useState, useEffect, useContext} from 'react';
 import List from '@mui/material/List';
-import Box from '@mui/material/Box';
+import { AdaptiveContext } from '../App'
 
 import {Link} from 'react-router-dom'
 
-import { FloatBtn, ChatAvatar, CChatModal } from "../components"
+import { ChatAvatar } from "../components"
 import { connect }  from 'react-redux'
 
 
@@ -28,10 +28,14 @@ const Chat = ({ chat, currChat, myId, lv }) => {
                         (chat.messages[chat.messages.length - 1]?.media && 'медиа') ) || '...' )
   }, [chat, currChat, lv])
 
+  const contextObj = useContext(AdaptiveContext)
   return (
     <Link 
       style={{ textDecoration: 'none' }}
       to={`/main/${chat._id}`}
+      onClick={() => {
+        contextObj.setAside(false)
+      }}
       >
      
       <div
@@ -80,34 +84,19 @@ const CChat = connect( state => ({ myId: state.promise.myProfile?.payload?._id |
 
 
 
-const FloatBtnModal = ({ chat={}, OPEN }) => {
-  return (
-    <Box onClick={OPEN}
-      sx={{  position: 'fixed', top: '90%', left: '25%', zIndex: 10}} >
-      <FloatBtn />
-    </Box>
-  )
-}
-
 const ChatList = ({ chats=[], currChatId }) => {
 
-
   return (
       <List        
-        sx={{ maxWidth: '100%', bgcolor: 'background.paper', position: 'relative', zIndex: 2, }}
+        sx={{ maxWidth: '100%', bgcolor: 'background.paper' }}
         >
           <div>
-
-              <CChatModal key={'creation'} create={true} render={FloatBtnModal} />
-
+             
               {chats.map(chat =>          
                     <CChat key={chat._id} chat={chat} currChat={currChatId === chat._id} lv={chat.lastVizited} /> 
               )}  
           </div>  
-      </List>
-
-    
-      
+      </List>      
   )
 }
 export const CChatList = connect( state => ({ chats: Object.values(state.chats).filter(el => !!el._id) }))(ChatList)

+ 39 - 18
src/components/ChatMngHeader.jsx

@@ -1,10 +1,16 @@
-import React, {useState, useEffect, useRef} from 'react'
+import React, {useState, useEffect, useContext} from 'react'
+import { useTheme } from '@mui/material/styles';
+import useMediaQuery from '@mui/material/useMediaQuery';
 import IconButton from '@mui/material/IconButton';
 import ArrowBackIcon from '@mui/icons-material/ArrowBack';
 import MoreVertIcon from '@mui/icons-material/MoreVert';
 import Menu from '@mui/material/Menu';
 import MenuItem from '@mui/material/MenuItem';
 
+import {Link} from 'react-router-dom'
+
+import { AdaptiveContext } from '../App'
+
 import { ChatAvatar, CChatModal } from "../components"
 
 import { printEnding } from "../helpers"
@@ -21,31 +27,34 @@ const chatMngBody = {
    const blockMobile = {
       flexShrink: 0,
       flexGrow: 0,
-      flexBasis: "60px",
+      flexBasis: "50px",
       display: "flex",
       justifyContent: "flex-start",
       aligneItems: "center",
-      display: "none"
    }
    const blockLeft = {
-      flexShrink: 1,
       flexGrow: 1,
+      width: "calc(100% - 100px)",
       display: "flex",
       justifyContent: "flex-start",
       aligneItems: "center",
       cursor: "pointer"
    }
       const blockAv = {
+         flexShrink: 0,
+         flexGrow: 0,
+         flexBasis: "40px",
          margin: "auto 0"
       }
       const blockInfo = {
          display: "flex",
          flexDirection: "column",
          justifyContent: "space-between",
+         overflow: "hidden",
          whiteSpace: "nowrap",
-         textOverFlow: "ellipsis",
+         textOverflow: "ellipsis",
          marginLeft: "15px",
-         userSelect: "none"
+         userSelect: "none",
       }
          const blockName = {
             fontSize: "16px",
@@ -58,7 +67,7 @@ const chatMngBody = {
    const blockRight = {
       flexShrink: 0,
       flexGrow: 0,
-      flexBasis: "10%",
+      flexBasis: "50px",
       display: "flex",
       justifyContent: "flex-end",
       aligneItems: "center"
@@ -91,8 +100,6 @@ const chatMinInfo = ({ chat, OPEN }) => {
 }
 
 
-
-
 const ChatMenu = ({ chatId, onLeave }) => {
    const [anchorEl, setAnchorEl] = useState(null)
    const open = !!anchorEl
@@ -138,19 +145,33 @@ const CChatMenu = connect(null, {onLeave: removeUserChat})(ChatMenu)
 const ChatMngHeader = ({ chats, chatId }) => {
 
    const chat = chats[chatId]
-   // console.log(chat)
+
+   const theme = useTheme()
+   const matches = useMediaQuery(theme.breakpoints.up('sm'))
+
+   const contextObj = useContext(AdaptiveContext)
    return (
       <div style={chatMngBody}>
 
-         <div style={blockMobile}>
-            <IconButton style={{ color: '#fff' }} >
-               <ArrowBackIcon />
-            </IconButton>
-         </div>
-
+         { matches ||
+            <div style={ blockMobile } >
+               <Link
+                  style={{ textDecoration: 'none' }}
+                  to={`/main`}
+                  onClick={() => {
+                     contextObj.setAside(true)
+                  }}
+               >
+                  <IconButton 
+                     style={{ color: '#fff' }} 
+                     >
+                     <ArrowBackIcon />
+                  </IconButton>
+               </Link>
+            </div>
+         }
 
-         <CChatModal key={chatId} create={false} chat={chat} render={chatMinInfo} /> 
-         
+         <CChatModal key={chatId} create={false} chat={chat} render={chatMinInfo} />         
 
          <div style={blockRight}>
             <CChatMenu chatId={chatId} />

+ 3 - 2
src/components/Msg.jsx

@@ -141,7 +141,8 @@ const Msg = ({ msg, myProfile, onEdit }) => {
    if (media) for (const file of media) {
       if (file.type) {
 
-         const objName = file.type.split('/')[0]
+         const [objName, ...rest] = file.type.split('/') || []
+
          if (allMedia.hasOwnProperty(objName)) {
 
             allMedia[objName].push(file)
@@ -198,7 +199,7 @@ const Msg = ({ msg, myProfile, onEdit }) => {
                {  (myId === owner._id) && 
 
                   <ListItemButton onClick={() => {
-                     onEdit(msg);
+                     onEdit({...msg});
                      handleClose()
                   }} >
                      Редактировать

+ 12 - 3
src/components/ProfileModal.jsx

@@ -1,4 +1,6 @@
 import React, {useEffect, useState} from 'react';
+import { useTheme } from '@mui/material/styles';
+import useMediaQuery from '@mui/material/useMediaQuery';
 import Box from '@mui/material/Box';
 import Modal from '@mui/material/Modal';
 import ListItem from '@mui/material/ListItem';
@@ -23,7 +25,7 @@ const styleModalParrent = {
   top: '50%',
   left: '50%',
   transform: 'translate(-50%, -50%)',
-  width: '50%',
+  width: '70%',
   maxWidth: '600px',
   bgcolor: 'background.paper',
   border: '1px solid #999',
@@ -74,7 +76,7 @@ const PassModal = ({ onСonfirm, regError}) => {
          open={open}
          onClose={handleClose}
        >
-         <Box sx={{ ...styleModalParrent,   width: '40%', maxWidth: '400px', }}>
+         <Box sx={{ ...styleModalParrent,   width: '50%', maxWidth: '400px', }}>
             <Box sx={{ display: 'flex', justifyContent: 'end' }}>
                <IconButton aria-label="delete" onClick={handleClose}>
                   <CloseIcon />
@@ -223,6 +225,9 @@ const ProfileModal = ({minLog='2', myProfile, onСonfirm, logError}) => {
      }
   },[logError])
 
+
+  const theme = useTheme()
+  const matches = useMediaQuery(theme.breakpoints.up('sm'))
   return (
     <div>
          <ListItem button onClick={handleOpen} >
@@ -261,7 +266,11 @@ const ProfileModal = ({minLog='2', myProfile, onСonfirm, logError}) => {
                                     localUrl: img && URL.createObjectURL(img)}} bigSize={true} />
 
                      <input {...getInputProps()} type="file" name="media" id='mediaUser' />
-                     <Box sx={{ p: '20px', ml: 1 }} >Изменить аватар</Box>
+
+                     { matches &&
+                        <Box sx={{ p: '20px', ml: 1 }} >Изменить аватар</Box>
+                     }
+
                   </Box>
                </section>
 

+ 18 - 2
src/pages/AsidePage.jsx

@@ -1,20 +1,34 @@
 import React, { useState } from 'react'
+import Box from '@mui/material/Box';
 
 import {
    MenuDrawer, 
    SearchBlock, 
    Header, 
+   FloatBtn, 
+   CChatModal
 } from "../components"
 
-
 const asidePageContainer = {
    height: "100vh",
-   maxWidth: "100%"
+   maxWidth: "100%",
+   position: 'relative',
+   zIndex: 2,
 }
 const asidePageHeader = {
    height: "60px"
 }
 
+const FloatBtnModal = ({ OPEN }) => {
+   return (
+     <Box onClick={OPEN}
+       sx={{  position: 'absolute', top: '90%', left: '65%', zIndex: 10 }} >
+       <FloatBtn />
+     </Box>
+   )
+ }
+ 
+
 export const AsidePage = ({children}) => {
 
    const [input, setInput] = useState('')
@@ -31,6 +45,8 @@ export const AsidePage = ({children}) => {
 
          {children}
 
+         <CChatModal key={'creation'} create={true} render={FloatBtnModal} />
+
       </div>
    )
 }

+ 15 - 5
src/pages/MsgPage.jsx

@@ -6,6 +6,9 @@ import {
    CMsgList,
    CSendingField,   
 } from "../components"
+import {
+   PageNoChat  
+} from "."
 
 import {
    actionFullMsgsByChat,
@@ -127,11 +130,18 @@ const MsgBlock = ({ chatId, getMsgs, setLastVizited, msgsCount=20 }) => {
 const CMsgBlock = connect( null, {getMsgs: actionFullMsgsByChat, setLastVizited: actionChatOne })(MsgBlock)
 
 
-export const MsgPage = ({ match:{params:{_id}} }) => {
+const MsgPage = ({ match:{params:{_id}}, chats }) => {
 
-   return (
-      <>
+   if (_id in chats) {
+      return (
          <CMsgBlock chatId={_id} key={_id} />
-      </>
-   )
+      )
+   } else {
+      return (
+         <PageNoChat />
+      )
+   }
+
 }
+export const CMsgPage = connect( state => ({ chats: state.chats || {} }) )(MsgPage)
+

+ 5 - 0
src/pages/PageNoChat.jsx

@@ -0,0 +1,5 @@
+
+export const PageNoChat = () => (
+   <div style={{ height: "100vh", width: "100%", backgroundColor: "#eee" }}>
+   </div>
+ )

+ 4 - 4
src/pages/index.js

@@ -4,13 +4,13 @@ import {Register} from './Register'
 import {Main} from './Main'
 import {AsidePage} from './AsidePage'
 import {CChatsPage} from './ChatsPage'
-import {MsgPage} from './MsgPage'
-
+import {CMsgPage} from './MsgPage'
+import {PageNoChat} from './PageNoChat'
 
 export {Login} 
 export {Register} 
 export {Main} 
 export {AsidePage} 
 export {CChatsPage} 
-export {MsgPage} 
-
+export {CMsgPage} 
+export {PageNoChat}

+ 5 - 5
src/reducers/store.js

@@ -3,7 +3,7 @@ import thunk from 'redux-thunk'
 import { promiseReducer } from './promiseReducer'
 import { authReducer } from './authReducer'
 import { chatsReducer } from './chatsReducer'
-import { localStoredReducer } from './localStoredReducer'
+// import { localStoredReducer } from './localStoredReducer'
 
 import { actionAboutMe } from './findUserActions'
 
@@ -15,22 +15,22 @@ import {
 } from '../actions'
 
 
-let initialState = localStorage.getItem('state')
+// let initialState = localStorage.getItem('state')
 
 export const store =  createStore ( combineReducers({ 
                                        auth: authReducer,
                                        chats: chatsReducer, 
                                        promise: promiseReducer, 
-                                                }, 
+                                                } 
                                                 ),
-                                    initialState ? {chats: JSON.parse(initialState)} : {},
+                                    // initialState ? {chats: JSON.parse(initialState)} : {},
                                     applyMiddleware(thunk),
                         )
 
 store.dispatch(actionAboutMe())
 
 // store.subscribe(() => console.log(store.getState()))
-window.onbeforeunload = () => window.localStorage.setItem('state', JSON.stringify(store.getState().chats) )
+// window.onbeforeunload = () => window.localStorage.setItem('state', JSON.stringify(store.getState().chats) )
 
 ///////////////////////////////////////////////////////////////////