Browse Source

27.03.2023 04:45

Volddemar4ik 1 năm trước cách đây
mục cha
commit
2d5ee2f60d

+ 173 - 4
js/Project/project/src/components/feed/aboutMe.js

@@ -1,9 +1,106 @@
+// import { Avatar, Typography, Box, Stack, CardHeader } from '@mui/material'
+// import { useSelector, connect } from 'react-redux'
+// import { useHistory } from 'react-router-dom'
+
+// import { url } from "../../App"
+
+// function AboutMe({ aboutMe = {} }) {
+//     let history = useHistory()
+
+//     const myPostsCount = useSelector(state => state?.promise?.MyPostsCount?.payload)
+
+//     function toMyAccount() {
+//         history.push(`/user/${aboutMe?._id}`)
+//     }
+
+//     return (
+//         <Box sx={{
+//             maxWidth: 400
+//         }}>
+//             <CardHeader
+//                 avatar={
+//                     <Avatar
+//                         sx={{ width: 50, height: 50 }}
+//                         alt={aboutMe?.login}
+//                         src={url + aboutMe?.avatar?.url}
+//                     />
+//                 }
+
+//                 subheader={
+//                     <Typography
+//                         sx={{
+//                             cursor: 'pointer'
+//                         }}
+//                         variant='h6'
+//                         onClick={toMyAccount}>
+//                         {aboutMe?.login}
+//                     </Typography>
+//                 }
+//             />
+//             <Stack
+//                 sx={{
+//                     paddingTop: '5px',
+//                     paddingLeft: '20px',
+//                     flexWrap: 'wrap'
+//                 }}
+//                 direction="row"
+//                 spacing={1}
+//                 padding={2}>
+//                 <Typography>
+//                     {myPostsCount || '0'} публикаций
+//                 </Typography>
+//                 <Typography>
+//                     {aboutMe?.followers?.length || '0'} подписчиков
+//                 </Typography>
+//                 <Typography>
+//                     {aboutMe?.following?.length || '0'} подписок
+//                 </Typography>
+//             </Stack>
+
+//             {/* вот тут можно добавить список всех юзеров, которые есть в базе: аватар+логин+кнопка подписаться/отписаться */}
+
+//         </Box>
+
+//     )
+// }
+
+// export const CAboutMe = connect(state => ({ aboutMe: state?.promise?.AboutMe?.payload }))(AboutMe)
+
+
+
 import { Avatar, Typography, Box, Stack, CardHeader } from '@mui/material'
 import { useSelector, connect } from 'react-redux'
 import { useHistory } from 'react-router-dom'
+import React, { useState } from 'react';
+
+import Backdrop from '@mui/material/Backdrop';
+import Modal from '@mui/material/Modal';
+import Fade from '@mui/material/Fade';
+
+import { RecommendedCard } from './recommended';
+
+import './style.scss'
 
 import { url } from "../../App"
 
+
+const style = {
+    position: 'absolute',
+    top: '50%',
+    left: '50%',
+    transform: 'translate(-50%, -50%)',
+    width: 400,
+    height: 'auto',
+    bgcolor: 'background.paper',
+    boxShadow: 24,
+    p: 4,
+    outline: 0,
+    borderRadius: 1.5
+};
+
+
+
+
 function AboutMe({ aboutMe = {} }) {
     let history = useHistory()
 
@@ -13,6 +110,18 @@ function AboutMe({ aboutMe = {} }) {
         history.push(`/user/${aboutMe?._id}`)
     }
 
+    // функции управления открытием/закрытием модального окна
+    const [modalName, setModalName] = useState('')
+    const [modalArray, setModalArray] = useState([])
+    const [open, setOpen] = useState(false);
+    const handleOpen = (param) => {
+        setOpen(true)
+        setModalArray([...param.arr])
+        setModalName(param.name)
+    }
+
+    const handleClose = () => setOpen(false);
+
     return (
         <Box sx={{
             maxWidth: 400
@@ -38,22 +147,82 @@ function AboutMe({ aboutMe = {} }) {
                 }
             />
             <Stack
+                sx={{
+                    paddingTop: '5px',
+                    paddingLeft: '20px',
+                    flexWrap: 'wrap'
+                }}
                 direction="row"
                 spacing={1}
                 padding={2}>
-                <Typography>
+                <Typography
+                    sx={{
+                        cursor: 'pointer'
+                    }}
+                    onClick={toMyAccount}
+                >
                     {myPostsCount || '0'} публикаций
                 </Typography>
-                <Typography>
+                <Typography
+                    sx={{
+                        cursor: 'pointer'
+                    }}
+                    onClick={() => handleOpen({ arr: aboutMe?.followers, name: 'подписчики' })}
+                >
                     {aboutMe?.followers?.length || '0'} подписчиков
                 </Typography>
-                <Typography>
+                <Typography
+                    sx={{
+                        cursor: 'pointer'
+                    }}
+                    onClick={() => handleOpen({ arr: aboutMe?.following, name: 'подписки' })}
+                >
                     {aboutMe?.following?.length || '0'} подписок
                 </Typography>
             </Stack>
 
-            {/* вот тут можно добавить список всех юзеров, которые есть в базе: аватар+логин+кнопка подписаться/отписаться */}
+            {modalArray && <Modal
+                aria-labelledby="transition-modal-title"
+                aria-describedby="transition-modal-description"
+                open={open}
+                onClose={handleClose}
+                closeAfterTransition
+                slots={{ backdrop: Backdrop }}
+                slotProps={{
+                    backdrop: {
+                        timeout: 500,
+                    },
+                }}
+            >
+                <Fade in={open}>
+                    <Box sx={style}>
+                        <Box sx={{
+                            padding: '0 24px'
+                        }}>
+                            <Typography
+                                variant="subtitle1"
+                                color='text.secondary'
+                                gutterBottom
+                            >
+                                Ваши {modalName}
+                            </Typography>
+                        </Box>
+
+                        <Box
+                            className='recommendedBox'
 
+                            sx={{
+                                margin: '8px 0',
+                                width: '95%',
+                                paddingLeft: '8px',
+                                height: '450px',
+                                overflowY: 'auto'
+                            }}>
+                            {modalArray.map(item => <RecommendedCard key={item._id} data={item} />)}
+                        </Box>
+                    </Box>
+                </Fade>
+            </Modal>}
         </Box>
 
     )

+ 7 - 2
js/Project/project/src/components/feed/index.js

@@ -1,7 +1,7 @@
 import React, { useEffect, useState } from 'react';
 import { connect, useDispatch, useSelector } from 'react-redux';
 
-import { Box, Paper } from '@mui/material';
+import { Box, Divider, Paper } from '@mui/material';
 import { styled } from '@mui/material/styles';
 import { Container } from '@mui/system';
 import Grid2 from '@mui/material/Unstable_Grid2/Grid2';
@@ -10,6 +10,7 @@ import Grid2 from '@mui/material/Unstable_Grid2/Grid2';
 import { actionAboutMe, actionDownloadFeed } from '../redux/thunks';
 import { CardFeed } from './card_feed';
 import { CAboutMe } from './aboutMe';
+import Recommended from './recommended';
 
 
 const Item = styled(Paper)(() => ({
@@ -55,7 +56,9 @@ function Feed({ feed, loadFeed }) {
             width: '90%',
             mt: 1
         }}>
-            <Box sx={{ flexGrow: 1 }}>
+            <Box sx={{
+                flexGrow: 1
+            }}>
                 <Grid2 container spacing={2}>
                     <Grid2 xs={7}>
                         <Item>
@@ -67,6 +70,8 @@ function Feed({ feed, loadFeed }) {
                             position: 'fixed'
                         }}>
                             <CAboutMe />
+
+                            <Recommended />
                         </Item>
                     </Grid2>
                 </Grid2>

+ 126 - 0
js/Project/project/src/components/feed/recommended.js

@@ -0,0 +1,126 @@
+import { url } from '../../App';
+
+import * as React from 'react';
+import { useSelector } from "react-redux"
+import { useHistory } from 'react-router-dom';
+
+import { CardHeader, Avatar, Typography, Button, Box } from '@mui/material';
+
+import './style.scss'
+
+
+// Компонент карточки-рекомендации юзера 
+export function RecommendedCard({ data }) {
+    const history = useHistory()
+
+    // проверка, является ли пользователь моим подписчиком
+    const myFollowings = useSelector(state => state?.promise?.AboutMe?.payload?.following)
+    const isFollowing = myFollowings && myFollowings.some(item => item._id === data._id)
+
+    // переход на аккаунт
+    function toAccount() {
+        history.push(`user/${data._id}`)
+    }
+
+    return (
+        <CardHeader
+            avatar={
+                <Avatar
+                    src={url + data?.avatar?.url}
+                    aria-label="recipe"
+                    sx={{ width: 36, height: 36 }}
+                />
+            }
+
+            action={
+                <Button
+                    variant="text"
+                    size="small"
+                    onClick={() => console.log('click')}
+                >
+                    {isFollowing ? 'Отписаться' : 'Подписаться'}
+                </Button>
+            }
+
+            title={
+                <Typography
+                    sx={{
+                        cursor: 'pointer',
+                        width: 'fit-content'
+                    }}
+                    variant="subtitle2"
+                    color='text.secondary'
+                    onClick={toAccount}
+                >
+                    {data?.login || 'anon user'}
+                </Typography>
+            }
+        />
+    )
+}
+
+
+function Recommended() {
+
+    const followers = useSelector(state => state?.promise?.FindFollowers?.payload)
+    const followings = useSelector(state => state?.promise?.FindFollowings?.payload)
+    const myFollowings = useSelector(state => state?.promise?.AboutMe?.payload?.following)
+
+    let recommendedList = []
+
+    if (followers && followings && myFollowings) {
+        const joinArr = [
+            ...followers,
+            ...followings.filter((item) =>
+                !followers.some((current) => current._id === item._id)
+            )
+        ].filter((item) =>
+            !myFollowings.some((current) => current._id === item._id)
+        )
+
+        const random = 15
+        // теперь оставляем в массиве только random рандомных элементов элементов
+        for (let i = 0; i < random; i++) {
+            const randomIndex = Math.floor(Math.random() * joinArr.length)
+            recommendedList.push(joinArr[randomIndex])
+
+            // удаляем элементы из массива, чтобы снова их не показывать
+            joinArr.splice(randomIndex, 1)
+        }
+    }
+
+
+    return (
+        <Box sx={{
+            marginTop: '10px'
+        }}>
+            <Box sx={{
+                padding: '0 24px'
+            }}>
+                <Typography
+                    variant="subtitle1"
+                    color='text.secondary'
+                    gutterBottom
+                >
+                    Рекомендации для Вас
+                </Typography>
+            </Box>
+
+            <Box
+                className='recommendedBox'
+
+                sx={{
+                    margin: '8px 0',
+                    width: '95%',
+                    paddingLeft: '8px',
+                    height: '450px',
+                    overflowY: 'auto'
+                }}>
+                {recommendedList && recommendedList.map(item => <RecommendedCard key={item._id} data={item} />)}
+            </Box>
+        </Box>
+    )
+}
+
+
+export default Recommended

+ 21 - 0
js/Project/project/src/components/feed/style.scss

@@ -0,0 +1,21 @@
+.MuiCardHeader-root {
+    .css-sgoict-MuiCardHeader-action {
+        margin: 0;
+        margin-right: 16px;
+    }
+}
+
+.recommendedBox::-webkit-scrollbar {
+    width: 8px;
+}
+
+.recommendedBox::-webkit-scrollbar-track {
+    -webkit-box-shadow: 5px 5px 5px -5px rgba(34, 60, 80, 0.2) inset;
+    background-color: #f9f9fd;
+    border-radius: 8px;
+}
+
+.recommendedBox::-webkit-scrollbar-thumb {
+    border-radius: 8px;
+    background: linear-gradient(180deg, #00c6fb, #005bea);
+}

+ 29 - 2
js/Project/project/src/components/redux/action.js

@@ -164,10 +164,10 @@ export const actionFindUserOne = (_id, promiseName = 'UserFindOne') => actionPro
         _id url
       } 
        followers {
-        _id login nick avatar {_id url}
+        _id login avatar {_id url}
       }
       following {
-         _id
+         _id login avatar {_id url}
       }
     }
   }`, {
@@ -351,6 +351,33 @@ export const actionFindCommentsPostOne = id => actionPromise('PROMISE', 'FindCom
 
 
 
+// поиск всех подписчиков конкретных юзеров(бек фильтрует все дубли)
+export const actionFindFollowers = arr => actionPromise('PROMISE', 'FindFollowers', gql(`query FindFollowers($findFollowers: String){
+  UserFind(query: $findFollowers){
+      _id login
+      avatar{
+        _id url
+    }
+  }
+}`, {
+  findFollowers: JSON.stringify([{ 'followers._id': { $in: arr } }])
+}))
+
+// поиск всех подписок конкретных юзеров(бек фильтрует все дубли)
+export const actionFindFollowing = arr => actionPromise('PROMISE', 'FindFollowings', gql(` query FindFollowing($findFollowing: String){
+  UserFind(query: $findFollowing){
+      _id login
+      avatar{
+        _id url
+    }
+  }
+}`, {
+  findFollowing: JSON.stringify([{ 'following._id': { $in: arr } }])
+}))
+
+
+
+
 
 
 

+ 26 - 3
js/Project/project/src/components/redux/thunks.js

@@ -12,7 +12,9 @@ import {
     actionCreatePost,
     actionFindPostOne,
     actionFindCommentsPostOne,
-    actionAddComment
+    actionAddComment,
+    actionFindFollowers,
+    actionFindFollowing
 } from "./action"
 
 
@@ -95,6 +97,25 @@ export const actionAboutMe = () =>
 
         // запрашиваем формирование ленты моих постов (первый параметр - список id, второй - это сортировка постов от новых)
         dispatch(actionFeedFindOne(followingList, -1, 10, 'MyFeed'))
+
+
+        // =====================================
+        // собираем id подписок и подписчиков
+        // console.log(33, userData)
+        const podpisotaList = []
+
+        for (const key in userData) {
+            if (key === 'following' || key === 'followers') {
+                for (const item of userData[key]) {
+                    if (!podpisotaList.includes(item._id)) {
+                        podpisotaList.push(item._id);
+                    }
+                }
+            }
+        }
+
+        // диспатчим список для рекомендаций
+        await Promise.all([dispatch(actionFindFollowers(podpisotaList)), dispatch(actionFindFollowing(podpisotaList))])
     }
 
 
@@ -176,9 +197,11 @@ export const actionFullFindCommentsPostOne = id =>
 // создание комментария к посту с последующей загрузкой с бека списка всех постов, которые относятся к этой записи
 export const actionFullAddComment = (nameOfPromise, params, id) =>
     async dispatch => {
-        const newComment = await dispatch(actionAddComment(nameOfPromise,params))
+        const newComment = await dispatch(actionAddComment(nameOfPromise, params))
 
         if (typeof newComment._id === 'string') {
             actionFullFindCommentsPostOne(id)
         }
-    }
+    }
+
+