diff --git a/package.json b/package.json index 4ec2f6d5..bd3766d5 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "react-responsive": "^8.1.0", "react-reveal": "^1.2.2", "react-router-dom": "^5.2.0", + "react-router-hash-link": "^2.4.3", "react-select": "^5.7.0", "react-social-login-buttons": "^3.4.0", "react-syntax-highlighter": "^15.4.3", diff --git a/src/components/Card/CardWithPicture.jsx b/src/components/Card/CardWithPicture.jsx index a7604000..e8cd65c2 100644 --- a/src/components/Card/CardWithPicture.jsx +++ b/src/components/Card/CardWithPicture.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { makeStyles } from "@mui/styles"; import Card from "@mui/material/Card"; import CardHeader from "@mui/material/CardHeader"; @@ -18,6 +18,10 @@ import MoreVertOutlinedIcon from "@mui/icons-material/MoreVertOutlined"; import { ToggleButton, ToggleButtonGroup } from "@mui/material"; import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; +import { Link } from "react-router-dom"; +import { useDispatch, useSelector } from "react-redux"; +import { useFirebase, useFirestore } from "react-redux-firebase"; +import { getUserProfileData } from "../../store/actions"; const useStyles = makeStyles(theme => ({ root: { @@ -67,10 +71,13 @@ const useStyles = makeStyles(theme => ({ } })); -export default function CardWithPicture(props) { +export default function CardWithPicture({ tutorial }) { const classes = useStyles(); const [alignment, setAlignment] = React.useState("left"); const [count, setCount] = useState(1); + const dispatch = useDispatch(); + const firebase = useFirebase(); + const firestore = useFirestore(); const handleIncrement = () => { setCount(count + 1); }; @@ -83,22 +90,40 @@ export default function CardWithPicture(props) { setAlignment(newAlignment); }; + useEffect(() => { + getUserProfileData(tutorial?.created_by)(firebase, firestore, dispatch); + }, [tutorial]); + + const user = useSelector( + ({ + profile: { + user: { data } + } + }) => data + ); + + const getTime = timestamp => { + return timestamp.toDate().toDateString(); + }; + return ( - + + + - S + + {user?.photoURL && user?.photoURL.length > 0 ? ( + + ) : ( + user?.displayName[0] + )} } title={ @@ -110,9 +135,9 @@ export default function CardWithPicture(props) { color="textPrimary" data-testId="UserName" > - {props.name} + {user?.displayName} - {props.organizationName && ( + {tutorial?.owner && ( <> {" for "} - {props.organizationName} + {tutorial?.owner} )} } - subheader={props.date} + subheader={tutorial?.createdAt ? getTime(tutorial?.createdAt) : ""} /> - - - {props.title} - - - {props.contentDescription} - - + + + + {tutorial?.title} + + + {tutorial?.summary} + + + - {props.time} + {"10 min"}
({ root: { margin: "0.5rem", @@ -62,10 +65,13 @@ const useStyles = makeStyles(theme => ({ } })); -export default function CardWithoutPicture(props) { +export default function CardWithoutPicture({ tutorial }) { const classes = useStyles(); const [alignment, setAlignment] = React.useState("left"); const [count, setCount] = useState(1); + const dispatch = useDispatch(); + const firebase = useFirebase(); + const firestore = useFirestore(); const handleIncrement = () => { setCount(count + 1); }; @@ -78,16 +84,32 @@ export default function CardWithoutPicture(props) { setAlignment(newAlignment); }; + useEffect(() => { + getUserProfileData(tutorial?.created_by)(firebase, firestore, dispatch); + }, [tutorial]); + + const user = useSelector( + ({ + profile: { + user: { data } + } + }) => data + ); + + const getTime = timestamp => { + return timestamp.toDate().toDateString(); + }; + return ( - S + + {user?.photoURL && user?.photoURL.length > 0 ? ( + + ) : ( + user?.displayName[0] + )} } title={ @@ -99,9 +121,9 @@ export default function CardWithoutPicture(props) { color="textPrimary" data-testId="UserName" > - {props.name} + {user?.displayName} - {props.organizationName && ( + {tutorial?.owner && ( <> {" for "} - {props.organizationName} + {tutorial?.owner} )} } - subheader={props.date} + subheader={tutorial?.createdAt ? getTime(tutorial?.createdAt) : ""} /> - - - {props.title} - - - {props.contentDescription} - - + + + + {tutorial?.title} + + + {tutorial?.summary} + + + - {props.time} + {"10 min"}
{ - setOpen(!openMenu); - }; - const [upcomingEvents, setUpEvents] = useState([ - { - name: "Google Summer of Code", - img: [OrgUser], - date: "25 March, 2022", - }, - { - name: "Google Summer of Code", - img: [OrgUser], - date: "25 March, 2022", - }, - { - name: "Google Summer of Code", - img: [OrgUser], - date: "25 March, 2022", - }, - { - name: "Google Summer of Code", - img: [OrgUser], - date: "25 March, 2022", - }, - ]); - const [tags, setTags] = useState([ - "HTML", - "JavaScript", - "Css", - "Python", - "React", - "Java", - "HTML", - "JavaScript", - "Css", - "Python", - "React", - "HTML", - "JavaScript", - "Css", - "Python", - "React", - "Java", - "HTML", - "JavaScript", - "Css", - "Python", - "React", - ]); + const windowSize = useWindowSize(); + const [openMenu, setOpen] = useState(false); + const toggleSlider = () => { + setOpen(!openMenu); + }; + const [upcomingEvents, setUpEvents] = useState([ + { + name: "Google Summer of Code", + img: [OrgUser], + date: "25 March, 2022" + }, + { + name: "Google Summer of Code", + img: [OrgUser], + date: "25 March, 2022" + }, + { + name: "Google Summer of Code", + img: [OrgUser], + date: "25 March, 2022" + }, + { + name: "Google Summer of Code", + img: [OrgUser], + date: "25 March, 2022" + } + ]); + const [tags, setTags] = useState([ + "HTML", + "JavaScript", + "Css", + "Python", + "React", + "Java", + "HTML", + "JavaScript", + "Css", + "Python", + "React", + "HTML", + "JavaScript", + "Css", + "Python", + "React", + "Java", + "HTML", + "JavaScript", + "Css", + "Python", + "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 [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 [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 notification = () => {}; - const handleChange = (event, newValue) => { - setValue(newValue); - }; - const handleTabChange = (event, newValue) => { - setSelectedTab(newValue); - }; - const closeModal = () => { - setVisibleModal((prev) => !prev); - }; - return ( - - -
- {windowSize.width > 750 && ( - - - - - - )} -
- - - closeModal(e)} - /> - - - - - - - {userList.persons.map((person) => { - return person.Heading == "CardWithoutPicture" ? ( - - ) : ( - - ); - })} - - - - } - value="1" - style={{ width: "25%" }} - /> - } - value="2" - style={{ width: "25%" }} - /> - } - value="3" - style={{ width: "25%" }} - /> - } - value="4" - style={{ width: "25%" }} - /> - - - - - - - - - - - - - - - - + const tutorialIdArray = ["9VF7JGNPYmTQe7tdhZfQ"]; - - - - - - - - - - - - - - - - - - - - - + useEffect(() => { + getTutorialFeedData(tutorialIdArray)(firebase, firestore, dispatch); + }, []); + const tutorials = useSelector( + ({ + tutorialPage: { + feed: { homepageFeedArray } + } + }) => homepageFeedArray + ); - - - - - - -
-
- ); + console.log("tutorials array", tutorials); + + const notification = () => {}; + const handleChange = (event, newValue) => { + setValue(newValue); + }; + const handleTabChange = (event, newValue) => { + setSelectedTab(newValue); + }; + const closeModal = () => { + setVisibleModal(prev => !prev); + }; + return ( + + + + {windowSize.width > 750 && ( + + + + + + )} + + + + closeModal(e)} + /> + + + + + + + {tutorials.map(tutorial => { + return !tutorial?.featured_image ? ( + + ) : ( + + ); + })} + + + + } + value="1" + style={{ width: "25%" }} + /> + } + value="2" + style={{ width: "25%" }} + /> + } + value="3" + style={{ width: "25%" }} + /> + } + value="4" + style={{ width: "25%" }} + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); } export default HomePage; diff --git a/src/components/TutorialPage/StepList.jsx b/src/components/TutorialPage/StepList.jsx index d51a805b..13316e2d 100644 --- a/src/components/TutorialPage/StepList.jsx +++ b/src/components/TutorialPage/StepList.jsx @@ -11,6 +11,8 @@ import { Button } from "@mui/material"; import { makeStyles } from "@mui/styles"; +import { HashLink } from "react-router-hash-link"; +import { useParams } from "react-router-dom"; const useStyles = makeStyles(theme => ({ icons: { @@ -72,6 +74,7 @@ const StepList = ({ children }) => { const classes = useStyles(); + const { id } = useParams(); /** * * Cases for rendering the menu items @@ -96,7 +99,11 @@ const StepList = ({ data-testId={item?.dataTestId} > {item.id && ( - + { @@ -139,7 +146,7 @@ const StepList = ({ {item.title} - + )} {!item.link && item.onClick && ( { const [replies] = repliesArray.filter(replies => replies.comment_id == id); - console.log(repliesArray); - const handleIncrement = () => { setCount(count + 1); }; diff --git a/src/components/TutorialPage/components/Commnets/CommentBox.jsx b/src/components/TutorialPage/components/Commnets/CommentBox.jsx index 61a7baa0..373b0acc 100644 --- a/src/components/TutorialPage/components/Commnets/CommentBox.jsx +++ b/src/components/TutorialPage/components/Commnets/CommentBox.jsx @@ -1,6 +1,6 @@ import { Card, Grid, Typography, Button } from "@mui/material"; import { makeStyles } from "@mui/styles"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import Textbox from "./Textbox"; import Comment from "./Comment"; import { addComment } from "../../../../store/actions/tutorialPageActions"; @@ -28,11 +28,13 @@ const useStyles = makeStyles(() => ({ } })); -const CommentBox = ({ comments, tutorialId }) => { +const CommentBox = ({ commentsArray, tutorialId }) => { const classes = useStyles(); const firestore = useFirestore(); const firebase = useFirebase(); const dispatch = useDispatch(); + const [comments, setComments] = useState([]); + const [currCommentCount, setCurrCommentCount] = useState(3); const handleSubmit = comment => { const commentData = { content: comment, @@ -44,10 +46,20 @@ const CommentBox = ({ comments, tutorialId }) => { addComment(commentData)(firebase, firestore, dispatch); }; + useEffect(() => { + setComments(commentsArray?.slice(0, currCommentCount)); + }, [currCommentCount, commentsArray]); + + console.log(commentsArray, comments, currCommentCount); + + const increaseCommentCount = () => { + setCurrCommentCount(state => state + 3); + }; + return ( - + - Comments({comments?.length}) + Comments({commentsArray?.length || 0}) @@ -59,9 +71,14 @@ const CommentBox = ({ comments, tutorialId }) => { ); })} - + {comments?.length != commentsArray?.length && ( + + )} diff --git a/src/components/TutorialPage/components/PostDetails.jsx b/src/components/TutorialPage/components/PostDetails.jsx index bbceb822..1c46ac63 100644 --- a/src/components/TutorialPage/components/PostDetails.jsx +++ b/src/components/TutorialPage/components/PostDetails.jsx @@ -16,6 +16,8 @@ import User from "./UserDetails"; import { useDispatch, useSelector } from "react-redux"; import { useFirebase, useFirestore } from "react-redux-firebase"; import { getUserProfileData } from "../../../store/actions"; +import { HashLink } from "react-router-hash-link"; +import { useParams } from "react-router-dom"; const useStyles = makeStyles(() => ({ container: { padding: "20px", @@ -44,7 +46,8 @@ const PostDetails = ({ details }) => { const firebase = useFirebase(); const firestore = useFirestore(); const [alignment, setAlignment] = React.useState("left"); - const [count, setCount] = useState(details.upVote - details.downVote); + const [count, setCount] = useState(details.upVote - details.downVote || 0); + const { id } = useParams(); useEffect(() => { getUserProfileData(details.user)(firebase, firestore, dispatch); @@ -131,9 +134,11 @@ const PostDetails = ({ details }) => {
- - - + + + + + - + diff --git a/src/components/Tutorials/MyTutorials/BaseTutorialsComponent/index.jsx b/src/components/Tutorials/MyTutorials/BaseTutorialsComponent/index.jsx index 2476912c..102101cc 100644 --- a/src/components/Tutorials/MyTutorials/BaseTutorialsComponent/index.jsx +++ b/src/components/Tutorials/MyTutorials/BaseTutorialsComponent/index.jsx @@ -13,59 +13,69 @@ import TutorialCard from "./TutorialCard"; * @constructor */ const BaseTutorialsComponent = ({ owner = "", ownerName = "", users = [] }) => { - let user = useSelector( - ({ - tutorials: { - data: { user }, - }, - }) => user - ); - const org = useSelector( - ({ - tutorials: { - data: { org }, - }, - }) => org - ); - if (!user) user = users; - if (user) { - const index = [...user, ...org]; + let user = useSelector( + ({ + tutorials: { + data: { user } + } + }) => user + ); + const org = useSelector( + ({ + tutorials: { + data: { org } + } + }) => org + ); + if (!user) user = users; + if (user) { + const index = [...user, ...org]; - const index_array = index.filter((e) => e.owner === owner); - - return ( -
- - {index_array.map((tutorial, index) => ( - - - - ))} - {index_array.length === 0 && ( - - )} - -
- ); - } else { - return ( - - - - - - ); - } + const index_array = index.filter(e => e.owner === owner); + console.log(index, index_array); + return ( +
+ + {index_array[0]?.tutorials?.map((tutorial, index) => ( + + + + ))} + {index_array.length === 0 && ( + + + + )} + +
+ ); + } else { + return ( + + + + + + ); + } }; export default BaseTutorialsComponent; diff --git a/src/components/Tutorials/MyTutorials/OrgTutorials/index.jsx b/src/components/Tutorials/MyTutorials/OrgTutorials/index.jsx index f4499a6b..827baf42 100644 --- a/src/components/Tutorials/MyTutorials/OrgTutorials/index.jsx +++ b/src/components/Tutorials/MyTutorials/OrgTutorials/index.jsx @@ -16,7 +16,6 @@ const OrgTabPanel = ({ orgList, user }) => { }); const onSelectTab = index => setSelectedTab(index); - useEffect(() => { setListData([ { @@ -59,15 +58,7 @@ const OrgTabPanel = ({ orgList, user }) => { - - Not Implemented Yet - + diff --git a/src/store/actions/actionTypes.js b/src/store/actions/actionTypes.js index e8f3e990..34c8d586 100644 --- a/src/store/actions/actionTypes.js +++ b/src/store/actions/actionTypes.js @@ -151,3 +151,7 @@ export const ADD_COMMENT_FAILED = "ADD_COMMENT_FAILED"; export const GET_STEPS_DATA_START = "GET_STEPS_DETAILS_START"; export const GET_STEPS_DATA_SUCCESS = "GET_STEPS_DETAILS_SUCCESS"; 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"; diff --git a/src/store/actions/tutorialPageActions.js b/src/store/actions/tutorialPageActions.js index 8940e939..0fa0ec24 100644 --- a/src/store/actions/tutorialPageActions.js +++ b/src/store/actions/tutorialPageActions.js @@ -1,5 +1,36 @@ import * as actions from "./actionTypes"; +export const getTutorialFeedData = + tutorialIdArray => async (firestore, firebase, dispatch) => { + try { + dispatch({ type: actions.GET_TUTORIAL_FEED_START }); + const tutorials = await firebase + .collection("tutorials") + .where("tutorial_id", "in", tutorialIdArray) + .get(); + if (tutorials.empty) { + dispatch({ type: actions.GET_TUTORIAL_FEED_SUCCESS, payload: [] }); + } else { + const feed = tutorials.docs.map(doc => { + const tutorial = doc.data(); + const tutorialData = { + tutorial_id: tutorial?.tutorial_id, + title: tutorial?.title, + summary: tutorial?.summary, + owner: tutorial?.owner, + created_by: tutorial?.created_by, + createdAt: tutorial?.createdAt, + featured_image: tutorial?.featured_image + }; + return tutorialData; + }); + dispatch({ type: actions.GET_TUTORIAL_FEED_SUCCESS, payload: feed }); + } + } catch (e) { + dispatch({ type: actions.GET_TUTORIAL_FEED_FAILED, payload: e }); + } + }; + export const getTutorialData = tutorialID => async (firebase, firestore, dispatch) => { try { @@ -91,6 +122,14 @@ export const addComment = comment => async (firebase, firestore, dispatch) => { firestore.collection("cl_comments").doc(docref.id).update({ comment_id: docref.id }); + if (comment.replyTo == comment.tutorial_id) { + firestore + .collection("tutorials") + .doc(comment.tutorial_id) + .update({ + comments: firebase.firestore.FieldValue.arrayUnion(docref.id) + }); + } }) .then(() => { dispatch({ type: actions.ADD_COMMENT_SUCCESS }); diff --git a/src/store/actions/tutorialsActions.js b/src/store/actions/tutorialsActions.js index b528f46a..d69de913 100644 --- a/src/store/actions/tutorialsActions.js +++ b/src/store/actions/tutorialsActions.js @@ -11,16 +11,16 @@ const tutorials_index = new Elasticlunr( "summary" ); -export const searchFromTutorialsIndex = (query) => { +export const searchFromTutorialsIndex = query => { return tutorials_index.searchFromIndex(query); }; // Gets all the tutorials with this user having edit access export const getUserTutorialsBasicData = - (user_handle) => async (firestore, dispatch) => { + user_handle => async (firestore, dispatch) => { try { dispatch({ type: actions.GET_USER_TUTORIALS_BASIC_START }); - let index; + let index = []; const userTutorialsQuerySnapshot = await firestore .collection("tutorials") .where("editors", "array-contains", user_handle) @@ -29,7 +29,7 @@ export const getUserTutorialsBasicData = if (userTutorialsQuerySnapshot.empty) { index = []; } else { - index = userTutorialsQuerySnapshot.docs.map((doc) => { + index = userTutorialsQuerySnapshot.docs.map(doc => { const new_doc = { owner: user_handle, tutorial_id: doc.id, @@ -37,33 +37,34 @@ export const getUserTutorialsBasicData = summary: doc.get("summary") || "", featured_image: doc.get("featured_image") || "", icon: doc.get("icon") || "", - isPublished: doc.get("isPublished") || false, + isPublished: doc.get("isPublished") || false }; tutorials_index.addDocToIndex(new_doc); return new_doc; }); } + dispatch({ type: actions.GET_USER_TUTORIALS_BASIC_SUCCESS, - payload: index, + payload: { owner: user_handle, tutorials: index } }); } catch (e) { dispatch({ type: actions.GET_USER_TUTORIALS_BASIC_FAIL, - payload: e.message, + payload: e.message }); } }; // Gets the basic data of all the tutorials of the organizations that the user is a part of export const getOrgTutorialsBasicData = - (organizations) => async (firestore, dispatch) => { + organizations => async (firestore, dispatch) => { try { dispatch({ type: actions.GET_ORG_TUTORIALS_BASIC_START }); let index = []; - const getFinalData = async (handle) => { + const getFinalData = async handle => { let temp_array; const orgTutorialsQuerySnapshot = await firestore .collection("tutorials") @@ -73,14 +74,14 @@ export const getOrgTutorialsBasicData = if (orgTutorialsQuerySnapshot.empty) { temp_array = []; } else { - temp_array = orgTutorialsQuerySnapshot.docs.map((doc) => { + temp_array = orgTutorialsQuerySnapshot.docs.map(doc => { const new_doc = { owner: handle, tutorial_id: doc.id, title: doc.get("title") || "", summary: doc.get("summary") || "", featured_image: doc.get("featured_image") || "", - icon: doc.get("icon") || "", + icon: doc.get("icon") || "" }; tutorials_index.addDocToIndex(new_doc); return new_doc; @@ -91,75 +92,68 @@ export const getOrgTutorialsBasicData = }; if (organizations.length > 0) { - const promises = organizations.map( - async (org_handle) => { - const tutorials = await getFinalData(org_handle) - return { - owner: org_handle, - tutorials - } - } - ); + const promises = organizations.map(async org_handle => { + const tutorials = await getFinalData(org_handle); + return { + owner: org_handle, + tutorials + }; + }); index = await Promise.all(promises); } - - dispatch({ type: actions.GET_ORG_TUTORIALS_BASIC_SUCCESS, - payload: index.flat(), + payload: index.flat() }); } catch (e) { dispatch({ type: actions.GET_ORG_TUTORIALS_BASIC_FAIL, - payload: e.message, + payload: e.message }); } }; - -export const clearTutorialsBasicData = () => (dispatch) => +export const clearTutorialsBasicData = () => dispatch => dispatch({ type: actions.CLEAR_TUTORIALS_BASIC_STATE }); export const createTutorial = - (tutorialData) => async (firebase, firestore, dispatch, history) => { + tutorialData => async (firebase, firestore, dispatch, history) => { try { dispatch({ type: actions.CREATE_TUTORIAL_START }); const { title, summary, owner, created_by, is_org } = tutorialData; - const setData = async (type) => { - const document = firestore - .collection("tutorials") - .doc() + const setData = async type => { + const document = firestore.collection("tutorials").doc(); const documentID = document.id; const step_id = `${documentID}_${new Date().getTime()}`; await document.set({ - created_by, - editors: [created_by], - isPublished: false, - owner, - summary, - title, - featured_image: "", - icon: "", - url: "", - background_color: "#ffffff", - text_color: "#000000", - createdAt: firestore.FieldValue.serverTimestamp(), - updatedAt: firestore.FieldValue.serverTimestamp(), - }); - - // Adds first step when a tutorial is created - await addNewTutorialStep({ - owner, - tutorial_id: documentID, - title: "Step One", - time: 5, - id: step_id - })(firebase, firestore, dispatch); + created_by, + editors: [created_by], + isPublished: false, + owner, + summary, + title, + featured_image: "", + icon: "", + url: "", + background_color: "#ffffff", + text_color: "#000000", + createdAt: firestore.FieldValue.serverTimestamp(), + updatedAt: firestore.FieldValue.serverTimestamp() + }); + + // Adds first step when a tutorial is created + await addNewTutorialStep({ + owner, + tutorial_id: documentID, + title: "Step One", + time: 5, + id: step_id + })(firebase, firestore, dispatch); return documentID; }; @@ -173,12 +167,12 @@ export const createTutorial = } dispatch({ type: actions.CREATE_TUTORIAL_SUCCESS }); } catch (e) { - console.error("CREATE_TUTORIAL_FAIL", e) + console.error("CREATE_TUTORIAL_FAIL", e); dispatch({ type: actions.CREATE_TUTORIAL_FAIL, payload: e.message }); } }; -const checkUserOrOrgHandle = (handle) => async (firestore) => { +const checkUserOrOrgHandle = handle => async firestore => { const userHandleExists = await checkUserHandleExists(handle)(firestore); const orgHandleExists = await checkOrgHandleExists(handle)(firestore); @@ -195,38 +189,36 @@ export const getCurrentTutorialData = (owner, tutorial_id) => async (firebase, firestore, dispatch) => { try { dispatch({ type: actions.GET_CURRENT_TUTORIAL_START }); - + const tutorialDoc = await firestore .collection("tutorials") .doc(tutorial_id) - .get() + .get(); const stepsRef = firestore .collection("tutorials") .doc(tutorial_id) - .collection("steps") - + .collection("steps"); + const stepsQuerySnapshot = await stepsRef.get(); - const steps_obj = {} + const steps_obj = {}; stepsQuerySnapshot.forEach(step => { steps_obj[step.id] = step.data(); // console.log(step.id, step.data()) }); const steps = _.orderBy( - Object.keys(steps_obj).map((step) => steps_obj[step]), + Object.keys(steps_obj).map(step => steps_obj[step]), ["id"], ["asc"] - ) + ); dispatch({ type: actions.GET_CURRENT_TUTORIAL_SUCCESS, payload: { ...tutorialDoc.data(), - steps: steps.filter( - (x) => !x.deleted - ), - tutorial_id, - }, + steps: steps.filter(x => !x.deleted), + tutorial_id + } }); } catch (e) { console.log("GET_CURRENT_TUTORIAL_FAIL", e); @@ -237,38 +229,38 @@ export const getCurrentTutorialData = export const addNewTutorialStep = ({ owner, tutorial_id, title, time, id }) => - async (firebase, firestore, dispatch) => { - try { - dispatch({ type: actions.CREATE_TUTORIAL_STEP_START }); - - await firestore - .collection("tutorials") - .doc(tutorial_id) - .collection("steps") - .doc(id) - .set({ - content: `Switch to editor mode to begin ${title} step`, - id, - time, - title, - visibility: true, - deleted: false, - }); + async (firebase, firestore, dispatch) => { + try { + dispatch({ type: actions.CREATE_TUTORIAL_STEP_START }); - await getCurrentTutorialData(owner, tutorial_id)( - firebase, - firestore, - dispatch - ); + await firestore + .collection("tutorials") + .doc(tutorial_id) + .collection("steps") + .doc(id) + .set({ + content: `Switch to editor mode to begin ${title} step`, + id, + time, + title, + visibility: true, + deleted: false + }); - dispatch({ type: actions.CREATE_TUTORIAL_STEP_SUCCESS }); - } catch (e) { - console.log("CREATE_TUTORIAL_STEP_FAIL", e.message); - dispatch({ type: actions.CREATE_TUTORIAL_STEP_FAIL, payload: e.message }); - } - }; + await getCurrentTutorialData(owner, tutorial_id)( + firebase, + firestore, + dispatch + ); + + dispatch({ type: actions.CREATE_TUTORIAL_STEP_SUCCESS }); + } catch (e) { + console.log("CREATE_TUTORIAL_STEP_FAIL", e.message); + dispatch({ type: actions.CREATE_TUTORIAL_STEP_FAIL, payload: e.message }); + } + }; -export const clearCreateTutorials = () => (dispatch) => +export const clearCreateTutorials = () => dispatch => dispatch({ type: actions.CLEAR_CREATE_TUTORIALS_STATE }); export const getCurrentStepContentFromFirestore = @@ -278,120 +270,119 @@ export const getCurrentStepContentFromFirestore = .collection("tutorials") .doc(tutorial_id) .collection("steps") - .doc(step_id). - get(); + .doc(step_id) + .get(); - dispatch({ type: actions.SET_EDITOR_DATA, payload: stepContent.data().content}); + dispatch({ + type: actions.SET_EDITOR_DATA, + payload: stepContent.data().content + }); } catch (e) { console.log(e.message); } }; -export const setCurrentStepContent = +export const setCurrentStepContent = (tutorial_id, step_id, content) => async (firestore, dispatch) => { try { const stepDoc = firestore .collection("tutorials") .doc(tutorial_id) .collection("steps") - .doc(step_id) + .doc(step_id); - await stepDoc - .update({ - ["content"]: content, - updatedAt: firestore.FieldValue.serverTimestamp(), - }); - - dispatch({ type: actions.SET_EDITOR_DATA, payload: content }); + await stepDoc.update({ + ["content"]: content, + updatedAt: firestore.FieldValue.serverTimestamp() + }); + dispatch({ type: actions.SET_EDITOR_DATA, payload: content }); } catch (e) { console.log(e); } - -} + }; export const hideUnHideStep = (owner, tutorial_id, step_id, visibility) => - async (firebase, firestore, dispatch) => { - try { - await firestore - .collection("tutorials") - .doc(tutorial_id) - .collection("steps") - .doc(step_id) - .update({ - [`visibility`]: !visibility, - updatedAt: firestore.FieldValue.serverTimestamp(), - }); + async (firebase, firestore, dispatch) => { + try { + const type = await checkUserOrOrgHandle(owner)(firebase); + await firestore + .collection("tutorials") + .doc(tutorial_id) + .collection("steps") + .doc(step_id) + .update({ + [`visibility`]: !visibility, + updatedAt: firestore.FieldValue.serverTimestamp() + }); - await getCurrentTutorialData(owner, tutorial_id)( - firebase, - firestore, - dispatch - ); - } catch (e) { - console.log(e.message); - } - }; + await getCurrentTutorialData(owner, tutorial_id)( + firebase, + firestore, + dispatch + ); + } catch (e) { + console.log(e.message); + } + }; - export const publishUnpublishTutorial = +export const publishUnpublishTutorial = (owner, tutorial_id, isPublished) => - async (firebase, firestore, dispatch) => { - try { - await firestore - .collection("tutorials") - .doc(tutorial_id) - .update({ - ["isPublished"]: !isPublished, - }) + async (firebase, firestore, dispatch) => { + try { + await firestore + .collection("tutorials") + .doc(tutorial_id) + .update({ + ["isPublished"]: !isPublished + }); - getCurrentTutorialData(owner, tutorial_id)(firebase, firestore, dispatch); - } catch (e) { - console.log(e.message); - } - }; + getCurrentTutorialData(owner, tutorial_id)(firebase, firestore, dispatch); + } catch (e) { + console.log(e.message); + } + }; export const removeStep = (owner, tutorial_id, step_id, current_step_no) => - async (firebase, firestore, dispatch) => { - try { - await firestore - .collection("tutorials") - .doc(tutorial_id) - .collection("steps") - .doc(step_id) - .update({ - "deleted": true, - updatedAt: firestore.FieldValue.serverTimestamp(), - }); - - - const data = await firestore - .collection("tutorials") - .doc(tutorial_id) - .collection("steps") - .doc(step_id) - .get(); - - - await setCurrentStepNo( - current_step_no > 0 ? current_step_no - 1 : current_step_no - )(dispatch); - - await getCurrentTutorialData(owner, tutorial_id)( - firebase, - firestore, - dispatch - ); - } catch (e) { - console.log(e.message); - } - }; + async (firebase, firestore, dispatch) => { + try { + await firestore + .collection("tutorials") + .doc(tutorial_id) + .collection("steps") + .doc(step_id) + .update({ + deleted: true, + updatedAt: firestore.FieldValue.serverTimestamp() + }); + + const data = await firestore + .collection("tutorials") + .doc(tutorial_id) + .collection("steps") + .doc(step_id) + .get(); -export const setCurrentStep = (data) => async (dispatch) => + await setCurrentStepNo( + current_step_no > 0 ? current_step_no - 1 : current_step_no + )(dispatch); + + await getCurrentTutorialData(owner, tutorial_id)( + firebase, + firestore, + dispatch + ); + } catch (e) { + console.log(e.message); + } + }; + +export const setCurrentStep = data => async dispatch => dispatch({ type: actions.SET_EDITOR_DATA, payload: data }); -export const setCurrentStepNo = (data) => async (dispatch) => +export const setCurrentStepNo = data => async dispatch => dispatch({ type: actions.SET_CURRENT_STEP_NO, payload: data }); export const uploadTutorialImages = @@ -407,11 +398,11 @@ export const uploadTutorialImages = return { imageURLs: firebase.firestore.FieldValue.arrayUnion({ name: metadata.name, - url: downloadURL, - }), + url: downloadURL + }) }; }, - documentId: tutorial_id, + documentId: tutorial_id }); await getCurrentTutorialData(owner, tutorial_id)( @@ -421,24 +412,24 @@ export const uploadTutorialImages = ); dispatch({ - type: actions.TUTORIAL_IMAGE_UPLOAD_SUCCESS, + type: actions.TUTORIAL_IMAGE_UPLOAD_SUCCESS }); } catch (e) { dispatch({ type: actions.TUTORIAL_IMAGE_UPLOAD_FAIL, - payload: e.message, + payload: e.message }); } }; -export const clearTutorialImagesReducer = () => (dispatch) => +export const clearTutorialImagesReducer = () => dispatch => dispatch({ type: actions.CLEAR_TUTORIAL_IMAGES_STATE }); export const remoteTutorialImages = (owner, tutorial_id, name, url) => async (firebase, firestore, dispatch) => { try { dispatch({ - type: actions.TUTORIAL_IMAGE_DELETE_START, + type: actions.TUTORIAL_IMAGE_DELETE_START }); const type = await checkUserOrOrgHandle(owner)(firestore); @@ -452,8 +443,8 @@ export const remoteTutorialImages = .update({ imageURLs: firebase.firestore.FieldValue.arrayRemove({ name, - url, - }), + url + }) }); await getCurrentTutorialData(owner, tutorial_id)( @@ -463,83 +454,81 @@ export const remoteTutorialImages = ); dispatch({ - type: actions.TUTORIAL_IMAGE_DELETE_SUCCESS, + type: actions.TUTORIAL_IMAGE_DELETE_SUCCESS }); } catch (e) { dispatch({ type: actions.TUTORIAL_IMAGE_DELETE_FAIL, - payload: e.message, + payload: e.message }); } }; export const updateStepTitle = (owner, tutorial_id, step_id, step_title) => - async (firebase, firestore, dispatch) => { - try { - - const dbPath = `tutorials/${tutorial_id}/steps`; - await firestore - .collection(dbPath) - .doc(step_id) - .update({ - [`title`]: step_title, - updatedAt: firestore.FieldValue.serverTimestamp(), - }); + async (firebase, firestore, dispatch) => { + try { + const dbPath = `tutorials/${tutorial_id}/steps`; + await firestore + .collection(dbPath) + .doc(step_id) + .update({ + [`title`]: step_title, + updatedAt: firestore.FieldValue.serverTimestamp() + }); - await getCurrentTutorialData(owner, tutorial_id)( - firebase, - firestore, - dispatch - ); - } catch (e) { - console.log(e); - } - }; + await getCurrentTutorialData(owner, tutorial_id)( + firebase, + firestore, + dispatch + ); + } catch (e) { + console.log(e); + } + }; export const updateStepTime = (owner, tutorial_id, step_id, step_time) => - async (firebase, firestore, dispatch) => { - try { - - const dbPath = `tutorials/${tutorial_id}/steps`; + async (firebase, firestore, dispatch) => { + try { + const dbPath = `tutorials/${tutorial_id}/steps`; - await firestore - .collection(dbPath) - .doc(step_id) - .update({ - [`time`]: step_time, - updatedAt: firestore.FieldValue.serverTimestamp(), - }); + await firestore + .collection(dbPath) + .doc(step_id) + .update({ + [`time`]: step_time, + updatedAt: firestore.FieldValue.serverTimestamp() + }); - await getCurrentTutorialData(owner, tutorial_id)( - firebase, - firestore, - dispatch - ); - } catch (e) { - console.log(e.message); - } - }; + await getCurrentTutorialData(owner, tutorial_id)( + firebase, + firestore, + dispatch + ); + } catch (e) { + console.log(e.message); + } + }; export const setTutorialTheme = ({ tutorial_id, owner, bgColor, textColor }) => - async (firebase, firestore, dispatch) => { - try { - const dbPath = `tutorials`; - - await firestore.collection(dbPath).doc(tutorial_id).update({ - text_color: textColor, - background_color: bgColor, - updatedAt: firestore.FieldValue.serverTimestamp(), - }); + async (firebase, firestore, dispatch) => { + try { + const dbPath = `tutorials`; - await getCurrentTutorialData(owner, tutorial_id)( - firebase, - firestore, - dispatch - ); - } catch (e) { - console.log(e.message); - } - }; + await firestore.collection(dbPath).doc(tutorial_id).update({ + text_color: textColor, + background_color: bgColor, + updatedAt: firestore.FieldValue.serverTimestamp() + }); + + await getCurrentTutorialData(owner, tutorial_id)( + firebase, + firestore, + dispatch + ); + } catch (e) { + console.log(e.message); + } + }; diff --git a/src/store/reducers/tutorialPageReducers/feedReducer.js b/src/store/reducers/tutorialPageReducers/feedReducer.js new file mode 100644 index 00000000..aacd3c67 --- /dev/null +++ b/src/store/reducers/tutorialPageReducers/feedReducer.js @@ -0,0 +1,36 @@ +import * as actions from "../../actions/actionTypes"; + +const initialState = { + loading: false, + error: null, + homepageFeedArray: [] +}; + +const FeedReducer = (state = initialState, { type, payload }) => { + switch (type) { + case actions.GET_TUTORIAL_FEED_START: + return { + ...state, + loading: true + }; + + case actions.GET_TUTORIAL_FEED_SUCCESS: + return { + ...state, + loading: false, + homepageFeedArray: payload + }; + + case actions.GET_TUTORIAL_FEED_FAILED: + return { + ...state, + loading: false, + error: payload + }; + + default: + return state; + } +}; + +export default FeedReducer; diff --git a/src/store/reducers/tutorialPageReducers/index.js b/src/store/reducers/tutorialPageReducers/index.js index 9199a3a9..7b6c5aee 100644 --- a/src/store/reducers/tutorialPageReducers/index.js +++ b/src/store/reducers/tutorialPageReducers/index.js @@ -1,8 +1,10 @@ import { combineReducers } from "redux"; import PostReducer from "./postReducer"; import CommentReducer from "./commentReducer"; +import FeedReducer from "./feedReducer"; export default combineReducers({ post: PostReducer, - comment: CommentReducer + comment: CommentReducer, + feed: FeedReducer }); diff --git a/src/store/reducers/tutorialsReducer/dataReducer.js b/src/store/reducers/tutorialsReducer/dataReducer.js index eb82f280..8e4c8355 100644 --- a/src/store/reducers/tutorialsReducer/dataReducer.js +++ b/src/store/reducers/tutorialsReducer/dataReducer.js @@ -1,10 +1,10 @@ import * as actions from "../../actions/actionTypes"; const initialState = { - user: null, + user: [], org: [], loading: false, - error: null, + error: null }; const TutorialsDataReducer = (state = initialState, { type, payload }) => { @@ -17,15 +17,15 @@ const TutorialsDataReducer = (state = initialState, { type, payload }) => { return { ...state, loading: true, - error: null, + error: null }; case actions.GET_USER_TUTORIALS_BASIC_SUCCESS: return { ...state, - user: payload, + user: [payload], loading: false, - error: false, + error: false }; case actions.GET_ORG_TUTORIALS_BASIC_SUCCESS: @@ -33,7 +33,7 @@ const TutorialsDataReducer = (state = initialState, { type, payload }) => { ...state, org: payload, loading: false, - error: false, + error: false }; case actions.GET_USER_TUTORIALS_BASIC_FAIL: @@ -41,7 +41,7 @@ const TutorialsDataReducer = (state = initialState, { type, payload }) => { return { ...state, loading: false, - error: payload, + error: payload }; default: