From 1bcb407144ee215e9f8359b1f7ad56ff561f96e8 Mon Sep 17 00:00:00 2001 From: Mallepally Lokeshwar Reddy Date: Wed, 21 Aug 2024 23:59:42 +0530 Subject: [PATCH 1/6] chore: Add user feed reducer to profile reducer --- src/store/actions/actionTypes.js | 4 +++ src/store/reducers/profileReducer/index.js | 4 ++- .../profileReducer/userFeedReducer.js | 36 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/store/reducers/profileReducer/userFeedReducer.js diff --git a/src/store/actions/actionTypes.js b/src/store/actions/actionTypes.js index 34c8d586..262c720b 100644 --- a/src/store/actions/actionTypes.js +++ b/src/store/actions/actionTypes.js @@ -155,3 +155,7 @@ export const GET_STEPS_DATA_FAIL = "GET_STEPS_DETAILS_FAILED"; export const GET_TUTORIAL_FEED_START = "GET_TUTORIAL_FEED_START"; export const GET_TUTORIAL_FEED_SUCCESS = "GET_TUTORIAL_FEED_SUCCESS"; export const GET_TUTORIAL_FEED_FAILED = "GET_TUTORIAL_FEED_FAILED"; + +export const GET_USER_FEED_START = "GET_USER_FEED_START"; +export const GET_USER_FEED_SUCCESS = "GET_USER_FEED_SUCCESS"; +export const GET_USER_FEED_FAILED = "GET_USER_FEED_FAILED"; diff --git a/src/store/reducers/profileReducer/index.js b/src/store/reducers/profileReducer/index.js index 6d5ece73..38154da6 100644 --- a/src/store/reducers/profileReducer/index.js +++ b/src/store/reducers/profileReducer/index.js @@ -2,9 +2,11 @@ import { combineReducers } from "redux"; import profileEditReducer from "./profileEditReducer"; import dataReducer from "./dataReducer"; import userReducer from "./userReducer"; +import userFeedReducer from "./userFeedReducer"; export default combineReducers({ edit: profileEditReducer, data: dataReducer, - user: userReducer + user: userReducer, + userFeed: userFeedReducer, }); diff --git a/src/store/reducers/profileReducer/userFeedReducer.js b/src/store/reducers/profileReducer/userFeedReducer.js new file mode 100644 index 00000000..2f23ea0d --- /dev/null +++ b/src/store/reducers/profileReducer/userFeedReducer.js @@ -0,0 +1,36 @@ +import * as actions from "../../actions/actionTypes"; + +const initialState = { + loading: false, + error: null, + userFeedArray: [] +}; + +const userFeedReducer = (state = initialState, { type, payload }) => { + switch (type) { + case actions.GET_USER_FEED_START: + return { + ...state, + loading: true + }; + + case actions.GET_USER_FEED_SUCCESS: + return { + ...state, + loading: false, + userFeedArray: payload + }; + + case actions.GET_USER_FEED_FAILED: + return { + ...state, + loading: false, + error: payload + }; + + default: + return state; + } +}; + +export default userFeedReducer; From 18891c443150e0ed0f720d1a852729d83112e186 Mon Sep 17 00:00:00 2001 From: Mallepally Lokeshwar Reddy Date: Thu, 22 Aug 2024 00:02:01 +0530 Subject: [PATCH 2/6] feat: Add user feed functionality to profile page and organize exports --- src/store/actions/index.js | 15 ++++++++- src/store/actions/profileActions.js | 48 ++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/store/actions/index.js b/src/store/actions/index.js index 76520c41..e8e09385 100644 --- a/src/store/actions/index.js +++ b/src/store/actions/index.js @@ -16,6 +16,7 @@ export { verifyPasswordResetCode } from "./authActions"; export { + getOrgBasicData, addOrgUser, clearEditGeneral, clearOrgData, @@ -40,9 +41,21 @@ export { getUserProfileData, updateUserProfile, uploadProfileImage, + isUserFollower, addUserFollower, - removeUserFollower + removeUserFollower, + getUserFeedIdArray, + getUserFeedData, } from "./profileActions"; +export { + getTutorialFeedIdArray, + getTutorialFeedData, + getTutorialData, + getTutorialSteps, + getCommentData, + getCommentReply, + addComment +} from "./tutorialPageActions" export { addNewTutorialStep, clearCreateTutorials, diff --git a/src/store/actions/profileActions.js b/src/store/actions/profileActions.js index c3a96c31..f6ed61f8 100644 --- a/src/store/actions/profileActions.js +++ b/src/store/actions/profileActions.js @@ -281,7 +281,7 @@ export const removeUserFollower = async ( } }; -const getAllOrgsOfCurrentUser = () => async (firebase, firestore) => { +export const getAllOrgsOfCurrentUser = () => async (firebase, firestore) => { try { const auth = firebase.auth().currentUser; if (auth === null) return []; @@ -297,3 +297,49 @@ const getAllOrgsOfCurrentUser = () => async (firebase, firestore) => { console.log(e); } }; + +export const getUserFeedIdArray = userId => async (_, firestore) => { + try { + const userIdArray = []; + const querySnapshot = await firestore.collection("cl_user").get(); + const promises = querySnapshot.docs.map(async (doc) => { + const followStatus = await isUserFollower(userId, doc.id, firestore); + if (!followStatus) { + userIdArray.push(doc.id); + } + }); + + await Promise.all(promises); + return userIdArray; + } catch (e) { + console.log(e); + throw new Error("Failed to get user feed ID array"); + } +}; + + +export const getUserFeedData = userIdArray => async (firebase, firestore, dispatch) => { + try { + dispatch({ type: actions.GET_USER_FEED_START }); + + if (userIdArray.length === 0) { + dispatch({ type: actions.GET_USER_FEED_SUCCESS, payload: [] }); + return; + } + + const users = await firestore + .collection("cl_user") + .where("uid", "in", userIdArray) + .get(); + + if (users.empty) { + dispatch({ type: actions.GET_USER_FEED_SUCCESS, payload: [] }); + } else { + const userFeed = users.docs.map(doc => doc.data()); + dispatch({ type: actions.GET_USER_FEED_SUCCESS, payload: userFeed }); + } + } catch (e) { + dispatch({ type: actions.GET_USER_FEED_FAILED, payload: e }); + console.error("Failed to get user feed data", e); + } +}; From 52d7be8e6a92dcee49a3e59f67bebf2a80aeabca Mon Sep 17 00:00:00 2001 From: Mallepally Lokeshwar Reddy Date: Thu, 22 Aug 2024 17:58:33 +0530 Subject: [PATCH 3/6] chore: Refactor UserCard component and update user feed functionality --- src/components/CardTabs/Users/index.jsx | 111 +++++++++++++++++++++--- src/components/HomePage/index.jsx | 62 +------------ 2 files changed, 101 insertions(+), 72 deletions(-) diff --git a/src/components/CardTabs/Users/index.jsx b/src/components/CardTabs/Users/index.jsx index d6b8e7f5..d782956a 100644 --- a/src/components/CardTabs/Users/index.jsx +++ b/src/components/CardTabs/Users/index.jsx @@ -1,10 +1,13 @@ import Card from "@mui/material/Card"; import CardContent from "@mui/material/CardContent"; import Typography from "@mui/material/Typography"; -import React from "react"; +import React, { useState, useEffect } from "react"; import { makeStyles } from "@mui/styles"; import UserElement from "./UserElement"; - +import { getUserFeedData, getUserFeedIdArray } from "../../../store/actions"; +import { useFirebase, useFirestore } from "react-redux-firebase"; +import { useDispatch, useSelector } from "react-redux"; +import OrgUser from "../../../assets/images/org-user.svg"; const useStyles = makeStyles(theme => ({ root: { display: "flex", @@ -33,8 +36,74 @@ const useStyles = makeStyles(theme => ({ } })); -const UserCard = props => { +const UserCard = ({ title, userId }) => { const classes = useStyles(); + const firebase = useFirebase(); + const firestore = useFirestore(); + const dispatch = useDispatch(); + const [usersToFollow, setUsersToFollow] = useState([]); + + const users = useSelector( + ({ + profile: { + userFeed: { userFeedArray } + } + }) => userFeedArray + ); + + const [contributors, setContributors] = useState([ + { + name: "Janvi Thakkar", + img: [OrgUser], + desg: "Software Engineer", + onClick: {} + }, + { + name: "Janvi Thakkar", + img: [OrgUser], + desg: "Software Engineer", + onClick: {} + }, + { + name: "Janvi Thakkar", + img: [OrgUser], + desg: "Software Engineer", + onClick: {} + }, + { + name: "Janvi Thakkar", + img: [OrgUser], + desg: "Software Engineer", + onClick: {} + } + ]); + + useEffect(() => { + const getUserFeed = async () => { + const userIdArray = await getUserFeedIdArray(userId)( + firebase, + firestore, + dispatch + ); + getUserFeedData(userIdArray)(firebase, firestore, dispatch); + }; + + getUserFeed(); + }, []); + + useEffect(() => { + const updatedUsersToFollow = users + .filter(user => user.uid !== userId) + .map(user => ({ + uid: user.uid, + name: user.displayName, + img: user.photoURL ? [user.photoURL] : [OrgUser], + desg: user.handle, + onClick: {} + })); + setUsersToFollow(updatedUsersToFollow); + }, [users]); + return (
@@ -45,18 +114,32 @@ const UserCard = props => { gutterBottom data-testId="UsersCardTitle" > - {props.title} + {title} - {props.users.map(function (user, index) { - return ( - - ); - })} + + {title === "Who to Follow" && + usersToFollow.map(function (user, index) { + return ( + + ); + })} + + {title === "Contributors" && + contributors.map(function (user, index) { + return ( + + ); + })}
diff --git a/src/components/HomePage/index.jsx b/src/components/HomePage/index.jsx index e8190e6b..041ffeb8 100644 --- a/src/components/HomePage/index.jsx +++ b/src/components/HomePage/index.jsx @@ -110,60 +110,6 @@ function HomePage({ background = "white", textColor = "black" }) { "React" ]); - const [usersToFollow, setUsersToFollow] = useState([ - { - name: "Janvi Thakkar", - img: [OrgUser], - desg: "Software Engineer", - onClick: {} - }, - { - name: "Janvi Thakkar", - img: [OrgUser], - desg: "Software Engineer", - onClick: {} - }, - { - name: "Janvi Thakkar", - img: [OrgUser], - desg: "Software Engineer", - onClick: {} - }, - { - name: "Janvi Thakkar", - img: [OrgUser], - desg: "Software Engineer", - onClick: {} - } - ]); - - const [contributors, setContributors] = useState([ - { - name: "Janvi Thakkar", - img: [OrgUser], - desg: "Software Engineer", - onClick: {} - }, - { - name: "Janvi Thakkar", - img: [OrgUser], - desg: "Software Engineer", - onClick: {} - }, - { - name: "Janvi Thakkar", - img: [OrgUser], - desg: "Software Engineer", - onClick: {} - }, - { - name: "Janvi Thakkar", - img: [OrgUser], - desg: "Software Engineer", - onClick: {} - } - ]); - const profileData = useSelector(({ firebase: { profile } }) => profile); useEffect(() => { const getFeed = async () => { @@ -294,10 +240,10 @@ function HomePage({ background = "white", textColor = "black" }) { - + - + @@ -341,7 +287,7 @@ function HomePage({ background = "white", textColor = "black" }) { data-testId="homepageUsersToFollow" > - + - + From ab897a8aec31843f7bcfdce9dc974bef045ef954 Mon Sep 17 00:00:00 2001 From: Mallepally Lokeshwar Reddy Date: Thu, 22 Aug 2024 17:59:28 +0530 Subject: [PATCH 4/6] refactor: Implement dynamic data rendering for "Who to Follow" sidebar --- src/components/CardTabs/Users/UserElement.jsx | 59 +++++++++++++++---- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/src/components/CardTabs/Users/UserElement.jsx b/src/components/CardTabs/Users/UserElement.jsx index 2a6c2a9c..56db8b88 100644 --- a/src/components/CardTabs/Users/UserElement.jsx +++ b/src/components/CardTabs/Users/UserElement.jsx @@ -1,11 +1,49 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import Box from "@mui/material/Box"; import AddUser from "../../../assets/images/add-user.svg"; import CheckUser from "../../../assets/images/square-check-regular.svg"; +import { useFirestore } from "react-redux-firebase"; +import { useSelector } from "react-redux"; +import { + isUserFollower, + addUserFollower, + removeUserFollower +} from "../../../store/actions"; const UserElement = ({ user, index, useStyles }) => { const classes = useStyles(); - const [icon, setIcon] = useState(true); + const firestore = useFirestore(); + + const profileData = user; + const currentProfileData = useSelector( + ({ firebase: { profile } }) => profile + ); + const followerId = currentProfileData.uid; + const followingId = profileData.uid; + const [followed, setFollowed] = useState(false); + + const handleFollowToggle = async () => { + if (!followed) { + await addUserFollower(currentProfileData, profileData, firestore); + } else { + await removeUserFollower(currentProfileData, profileData, firestore); + } + setFollowed(!followed); + }; + + useEffect(() => { + const checkIfFollowing = async () => { + const isFollowing = await isUserFollower( + followerId, + followingId, + firestore + ); + setFollowed(isFollowing); + }; + + checkIfFollowing(); + }, [followerId, followingId, firestore]); + return ( { { - setIcon(false); - }} + onClick={handleFollowToggle} data-testId={index == 0 ? "UserAdd" : ""} - sx={ - icon && { - cursor: "pointer" - } - } + sx={{ + cursor: followed ? "default" : "pointer" + }} > - + {followed ); From aa42bced858c9fc57cca28ebc75abeb7e5192588 Mon Sep 17 00:00:00 2001 From: Mallepally Lokeshwar Reddy Date: Sat, 24 Aug 2024 16:13:54 +0530 Subject: [PATCH 5/6] chore: Update profile data fetching in multiple components --- src/App.jsx | 19 +-------------- src/components/Organization/pages/General.jsx | 11 +++++++-- .../Tutorials/NewTutorial/index.jsx | 9 +++++++- src/routes.jsx | 23 ++++++++++++++++--- 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 4a9c86f6..84cbc5f4 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,25 +1,8 @@ -import React, { useEffect } from "react"; +import React from "react"; import Routes from "./routes"; import "./App.less"; -import { useFirebase, useFirestore } from "react-redux-firebase"; -import { useDispatch, useSelector } from "react-redux"; -import { getProfileData } from "./store/actions"; const App = () => { - const firebase = useFirebase(); - const firestore = useFirestore(); - const dispatch = useDispatch(); - const organizations = useSelector( - ({ - firebase: { - profile: { organizations } - } - }) => organizations - ); - - useEffect(() => { - getProfileData(organizations)(firebase, firestore, dispatch); - }, [organizations, firebase, dispatch]); return ; }; diff --git a/src/components/Organization/pages/General.jsx b/src/components/Organization/pages/General.jsx index a1c80ed9..cfeadea7 100644 --- a/src/components/Organization/pages/General.jsx +++ b/src/components/Organization/pages/General.jsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { Grid, Typography, @@ -21,7 +21,8 @@ import { NoImage } from "../../../helpers/images"; import { uploadOrgProfileImage, clearEditGeneral, - editGeneralData + editGeneralData, + getProfileData } from "../../../store/actions"; import { useFirebase, useFirestore } from "react-redux-firebase"; import ChangeProfile from "../../Profile/ChangeProfile/ChangeProfile"; @@ -217,6 +218,12 @@ function General() { CurrentOrg ]); + useEffect(() => { + if (!profileOrganizations) { + getProfileData()(firebase, firestore, dispatch); + } + }, [firestore, firebase, dispatch, profileOrganizations]); + console.log(OrgData); return ( diff --git a/src/components/Tutorials/NewTutorial/index.jsx b/src/components/Tutorials/NewTutorial/index.jsx index ac826889..bf38ffe7 100644 --- a/src/components/Tutorials/NewTutorial/index.jsx +++ b/src/components/Tutorials/NewTutorial/index.jsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import { AppstoreAddOutlined } from "@ant-design/icons"; import { useDispatch, useSelector } from "react-redux"; -import { createTutorial } from "../../../store/actions"; +import { createTutorial, getProfileData } from "../../../store/actions"; import { useFirebase, useFirestore } from "react-redux-firebase"; import { useHistory } from "react-router-dom"; import Button from "@mui/material/Button"; @@ -79,6 +79,13 @@ const NewTutorial = ({ viewModal, onSidebarClick, viewCallback, active }) => { } }) => organizations ); + // console.log("organizations", organizations); + + useEffect(() => { + if (!organizations) { + getProfileData()(firebase, firestore, dispatch); + } + }, [firestore, firebase, dispatch, organizations]); const userHandle = useSelector( ({ diff --git a/src/routes.jsx b/src/routes.jsx index 39493a49..b0a77896 100644 --- a/src/routes.jsx +++ b/src/routes.jsx @@ -1,6 +1,11 @@ -import React from "react"; -import { useSelector } from "react-redux"; -import { isEmpty, isLoaded } from "react-redux-firebase"; +import React, { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { + isEmpty, + isLoaded, + useFirebase, + useFirestore +} from "react-redux-firebase"; import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; import { UserIsAllowedUserDashboard, @@ -27,12 +32,24 @@ import MainNavbar from "./components/NavBar/new/MainNavbar"; import UserDashboard from "./components/UserDashboard"; import TutorialPage from "./components/TutorialPage"; import Notification from "./components/Notification"; +import { getProfileData } from "./store/actions"; const AuthIsLoaded = ({ children }) => { + const firebase = useFirebase(); + const firestore = useFirestore(); + const dispatch = useDispatch(); + const profile = useSelector(({ firebase: { profile } }) => profile); const data = useSelector(({ profile: { data } }) => data); const general = useSelector(({ org: { general } }) => general); + useEffect(() => { + if (isLoaded(profile) && isLoaded(data) && isLoaded(general)) { + return; // Avoid fetching if data is already loaded + } + getProfileData()(firebase, firestore, dispatch); + }, [profile, firestore, firebase, dispatch]); + //case for not logged in user if ( isLoaded(profile) && From 8b6661459144b58f03abf175693623559258ca8f Mon Sep 17 00:00:00 2001 From: Mallepally Lokeshwar Reddy Date: Sat, 24 Aug 2024 20:02:50 +0530 Subject: [PATCH 6/6] feat: Update user dashboard profile and user settings navigation --- cypress/e2e/components/profile/user-dashboard.cy.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress/e2e/components/profile/user-dashboard.cy.js b/cypress/e2e/components/profile/user-dashboard.cy.js index 18d385d0..32ebbb04 100644 --- a/cypress/e2e/components/profile/user-dashboard.cy.js +++ b/cypress/e2e/components/profile/user-dashboard.cy.js @@ -34,7 +34,7 @@ describe("User Dashboard Test | CodeLabz", () => { it("Check Profile", function () { cy.visit(`${this.base_url}user-dashboard/profile`); cy.get( - '[data-testid="profile"] > .makeStyles-navLink-81 > .MuiButtonBase-root' + '[data-testid="profile"]' ) .should("exist") .click(); @@ -68,7 +68,7 @@ describe("User Dashboard Test | CodeLabz", () => { cy.visit(`${this.base_url}user-dashboard/profile`); cy.get( - '[data-testid="userSettings"] > .makeStyles-navLink-81 > .MuiButtonBase-root' + '[data-testid="userSettings"]' ) .should("exist") .click();