From 3a0eb15b6609cbeab7bc8c58ea94e3641176593e Mon Sep 17 00:00:00 2001 From: Shiva Date: Wed, 19 Jul 2023 22:57:14 +0530 Subject: [PATCH 1/4] feat/collapsible sidebar --- src/components/Tutorials/index.jsx | 107 ++++++++++++++++-- .../Tutorials/subComps/ControlButtons.jsx | 10 +- .../Tutorials/subComps/TutorialTitle.jsx | 3 +- 3 files changed, 105 insertions(+), 15 deletions(-) diff --git a/src/components/Tutorials/index.jsx b/src/components/Tutorials/index.jsx index ec366e4f..b54f951d 100644 --- a/src/components/Tutorials/index.jsx +++ b/src/components/Tutorials/index.jsx @@ -22,6 +22,66 @@ import Spinner from "../../helpers/spinner"; import AddNewStepModal from "./subComps/AddNewStep"; import QuillEditor from "../Editor/QuillEditor"; import RichTextRenderer from "./subComps/RichTextRenderer"; +import { Collapse, Button } from "@mui/material"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import { makeStyles } from "@mui/styles"; + +const useStyles = makeStyles(theme => ({ + flexRow: { + display: "flex", + flexDirection: "row" + }, + collapseContainer: { + minWidth: "100%", + "& > div > div": { + minWidth: "100%" + }, + overflow: "hidden", + transition: theme.transitions.create(["width"]) + }, + widthTransition: { + overflow: "hidden", + transition: theme.transitions.create(["width"]) + }, + expandButton: { + display: "flex", + alignItems: "start", + paddingTop: "15px" + }, + rotateChildren: { + display: "flex", + alignItems: "center", + justifyContent: "center", + transition: theme.transitions.create("transform", { + duration: theme.transitions.duration.shortest + }) + }, + ExpandIcon: { + fontSize: 50 + }, + editorContainer: { + width: "100%", + padding: "0 10px 10px 10px", + overflow: "hidden", + background: "white" + } +})); + +const ExpandMore = props => { + const { expand, children, ...other } = props; + const classes = useStyles({ expand }); + + return ( + + ); +}; const ViewTutorial = () => { const firebase = useFirebase(); @@ -37,10 +97,12 @@ const ViewTutorial = () => { const [currentStepContent, setCurrentStepContent] = useState(null); const [stepsData, setStepData] = useState(null); const [tutorialData, setTutorialData] = useState(null); + const [expand, setExpand] = useState(true); const isDesktop = useMediaQuery({ query: "(min-device-width: 767px)" }); const { owner, tutorial_id } = useParams(); + const classes = useStyles(); useEffect(() => { getCurrentTutorialData(owner, tutorial_id)(firebase, firestore, dispatch); @@ -147,23 +209,44 @@ const ViewTutorial = () => { /> - + + { + setExpand(prev => !prev); + setStepPanelVisible(prev => !prev); + }} + aria-expanded={expand} + aria-label="show more" + > + + + - setStepPanelVisible(false)} - hideButton={isDesktop} - setCurrentStep={setCurrentStep} - setStepData={setStepData} - /> + + setStepPanelVisible(false)} + hideButton={isDesktop} + setCurrentStep={setCurrentStep} + setStepData={setStepData} + /> + - + ({ justifyContent: "space-between", [theme.breakpoints.down("sm")]: { flexDirection: "column", - alignItems: "center" + alignItems: "center", + gap: "30px" } }, rightButtonsGroup: { @@ -22,7 +23,11 @@ const useStyles = makeStyles(theme => ({ borderWidth: "2px", "&:hover": { borderWidth: "2px" - } + }, + minWidth: "fit-content", + }, + completeButton: { + minWidth: "fit-content" } })); @@ -86,6 +91,7 @@ const ControlButtons = ({ ) ); }} + className={classes.completeButton} > {stepsData[currentStep].completed ? "Reset Step" diff --git a/src/components/Tutorials/subComps/TutorialTitle.jsx b/src/components/Tutorials/subComps/TutorialTitle.jsx index a4537c9a..7afb46cf 100644 --- a/src/components/Tutorials/subComps/TutorialTitle.jsx +++ b/src/components/Tutorials/subComps/TutorialTitle.jsx @@ -35,7 +35,8 @@ const TutorialHeading = ({ data-testid="tutorialTitle" variant="h5" sx={{ - pt: 5 + pt: 2, + pb: 2 }} > {tutorialData.title} From 11733791887ddb4ff935c8c3f27baa26a0ab2022 Mon Sep 17 00:00:00 2001 From: Shiva Date: Mon, 24 Jul 2023 20:46:59 +0530 Subject: [PATCH 2/4] Refactored editor instances for individual steps --- src/components/Editor/QuillEditor.jsx | 14 +++++++++++--- src/components/Tutorials/index.jsx | 4 ++-- .../Tutorials/subComps/HtmlTextRenderer.jsx | 7 +++++++ .../Tutorials/subComps/RichTextRenderer.jsx | 15 --------------- src/store/actions/tutorialsActions.js | 6 +++--- 5 files changed, 23 insertions(+), 23 deletions(-) create mode 100644 src/components/Tutorials/subComps/HtmlTextRenderer.jsx delete mode 100644 src/components/Tutorials/subComps/RichTextRenderer.jsx diff --git a/src/components/Editor/QuillEditor.jsx b/src/components/Editor/QuillEditor.jsx index c87aec18..bcc8daed 100644 --- a/src/components/Editor/QuillEditor.jsx +++ b/src/components/Editor/QuillEditor.jsx @@ -10,6 +10,7 @@ import Quill from "quill"; import QuillCursors from "quill-cursors"; import { FirestoreProvider, getColor } from "@gmcfall/yjs-firestore-provider"; import { onlineFirebaseApp } from "../../config"; +import { QuillDeltaToHtmlConverter } from "quill-delta-to-html"; Quill.register("modules/cursors", QuillCursors); @@ -20,9 +21,11 @@ const QuillEditor = ({ id, data, tutorial_id }) => { let noteID = id || "test_note"; const firebase = useFirebase(); const dispatch = useDispatch(); - const basePath = ["cl_codelabz", "organization", "codelabzorg", tutorial_id]; + // This path in cloud firestore contains yjs documents storing content of a step + // (actual data used to render is present in "steps" collection in the same doc) + const basePath = ["tutorials", tutorial_id, "yjsStepDocs", id]; let provider, binding, ydoc; - + const currentUserHandle = useSelector( ({ firebase: { @@ -43,8 +46,13 @@ const QuillEditor = ({ id, data, tutorial_id }) => { // on updating text in editor this gets triggered ydoc.on("update", () => { + // deltaText is quill editor's data structure to store text const deltaText = ydoc.getText("quill").toDelta(); - setCurrentStep(deltaText)(dispatch); + var config = {}; + var converter = new QuillDeltaToHtmlConverter(deltaText, config); + + var html = converter.convert(); + setCurrentStep(html)(dispatch); }); provider = new FirestoreProvider(onlineFirebaseApp, ydoc, basePath, { disableAwareness: true diff --git a/src/components/Tutorials/index.jsx b/src/components/Tutorials/index.jsx index b54f951d..71ce996c 100644 --- a/src/components/Tutorials/index.jsx +++ b/src/components/Tutorials/index.jsx @@ -21,7 +21,7 @@ import { useFirebase, useFirestore } from "react-redux-firebase"; import Spinner from "../../helpers/spinner"; import AddNewStepModal from "./subComps/AddNewStep"; import QuillEditor from "../Editor/QuillEditor"; -import RichTextRenderer from "./subComps/RichTextRenderer"; +import HtmlTextRenderer from "./subComps/HtmlTextRenderer"; import { Collapse, Button } from "@mui/material"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import { makeStyles } from "@mui/styles"; @@ -264,7 +264,7 @@ const ViewTutorial = () => { <> {mode === "view" && (
- +
)} {mode === "edit" && ( diff --git a/src/components/Tutorials/subComps/HtmlTextRenderer.jsx b/src/components/Tutorials/subComps/HtmlTextRenderer.jsx new file mode 100644 index 00000000..9cb4da1d --- /dev/null +++ b/src/components/Tutorials/subComps/HtmlTextRenderer.jsx @@ -0,0 +1,7 @@ +import React from "react"; + +const HtmlTextRenderer = ({ html = "

Html Text Renderer

" }) => { + return
; +}; + +export default HtmlTextRenderer; diff --git a/src/components/Tutorials/subComps/RichTextRenderer.jsx b/src/components/Tutorials/subComps/RichTextRenderer.jsx deleted file mode 100644 index d9a09b25..00000000 --- a/src/components/Tutorials/subComps/RichTextRenderer.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html'; -import React from 'react' - - -const RichTextRenderer = ({ delta }) => { - // Renders rich text from quill - var config = {}; - var converter = new QuillDeltaToHtmlConverter(delta, config); - - var html = converter.convert(); - - return
; - }; - -export default RichTextRenderer \ No newline at end of file diff --git a/src/store/actions/tutorialsActions.js b/src/store/actions/tutorialsActions.js index 69cb40ed..1e44128e 100644 --- a/src/store/actions/tutorialsActions.js +++ b/src/store/actions/tutorialsActions.js @@ -151,7 +151,7 @@ export const createTutorial = id: step_id, title: "Step One Title", time: 1, - content: "Sample tutorial step one", + content: "Switch to editor mode to begin your first step", visibility: true, deleted: false, }, @@ -258,7 +258,7 @@ export const addNewTutorialStep = .doc(tutorial_id) .update({ [`steps.${id}`]: { - content: "", + content: `Switch to editor mode to begin ${title} step`, id, time, title, @@ -269,7 +269,7 @@ export const addNewTutorialStep = }); await firebase.ref().child("notes").child(tutorial_id).child(id).set({ - text: "", + text: `Switch to editor mode to begin ${title} step`, deleted: false, }); From 239152b17a5bb126f954af802e9948b7ee1c0278 Mon Sep 17 00:00:00 2001 From: Shiva Date: Wed, 26 Jul 2023 17:11:21 +0530 Subject: [PATCH 3/4] integrated editor to backend with updated database design --- package.json | 1 + src/components/Editor/QuillEditor.jsx | 8 +- src/components/Tutorials/index.jsx | 6 +- .../Tutorials/subComps/HtmlTextRenderer.jsx | 5 +- src/store/actions/index.js | 3 +- src/store/actions/tutorialsActions.js | 249 +++++++++--------- 6 files changed, 138 insertions(+), 134 deletions(-) diff --git a/package.json b/package.json index 74c1a05b..4ec2f6d5 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "chalk": "^5.2.0", "codemirror": "^5.56.0", "color-thief-react": "^1.0.2", + "dompurify": "^3.0.5", "elasticlunr": "^0.9.5", "emoji-picker-react": "^4.4.9", "firebase": "^9.17.2", diff --git a/src/components/Editor/QuillEditor.jsx b/src/components/Editor/QuillEditor.jsx index bcc8daed..64e400c1 100644 --- a/src/components/Editor/QuillEditor.jsx +++ b/src/components/Editor/QuillEditor.jsx @@ -1,9 +1,9 @@ import React, { useEffect, useRef, useState } from "react"; import "../../css/quillEditor.css"; -import { useFirebase } from "react-redux-firebase"; +import { useFirestore } from "react-redux-firebase"; import { useDispatch, useSelector } from "react-redux"; import { Prompt } from "react-router-dom"; -import { setCurrentStep } from "../../store/actions"; +import { setCurrentStepContent } from "../../store/actions"; import * as Y from "yjs"; import { QuillBinding } from "y-quill"; import Quill from "quill"; @@ -19,7 +19,7 @@ const QuillEditor = ({ id, data, tutorial_id }) => { const editorRef = useRef(null); const containerRef = useRef(null); let noteID = id || "test_note"; - const firebase = useFirebase(); + const firestore = useFirestore(); const dispatch = useDispatch(); // This path in cloud firestore contains yjs documents storing content of a step // (actual data used to render is present in "steps" collection in the same doc) @@ -52,7 +52,7 @@ const QuillEditor = ({ id, data, tutorial_id }) => { var converter = new QuillDeltaToHtmlConverter(deltaText, config); var html = converter.convert(); - setCurrentStep(html)(dispatch); + setCurrentStepContent(tutorial_id, id, html)(firestore, dispatch); }); provider = new FirestoreProvider(onlineFirebaseApp, ydoc, basePath, { disableAwareness: true diff --git a/src/components/Tutorials/index.jsx b/src/components/Tutorials/index.jsx index 71ce996c..3450bee4 100644 --- a/src/components/Tutorials/index.jsx +++ b/src/components/Tutorials/index.jsx @@ -13,7 +13,7 @@ import StepsTitle from "./subComps/StepsTitle"; import { useDispatch, useSelector } from "react-redux"; import { useParams } from "react-router-dom"; import { - getCurrentStepContentFromRTDB, + getCurrentStepContentFromFirestore, getCurrentTutorialData, setCurrentStepNo } from "../../store/actions"; @@ -152,8 +152,8 @@ const ViewTutorial = () => { useEffect(() => { if (stepsData) { setTimeRemaining(TutorialTimeRemaining(stepsData, currentStep)); - getCurrentStepContentFromRTDB(tutorial_id, stepsData[currentStep].id)( - firebase, + getCurrentStepContentFromFirestore(tutorial_id, stepsData[currentStep].id)( + firestore, dispatch ); } diff --git a/src/components/Tutorials/subComps/HtmlTextRenderer.jsx b/src/components/Tutorials/subComps/HtmlTextRenderer.jsx index 9cb4da1d..a035c7f9 100644 --- a/src/components/Tutorials/subComps/HtmlTextRenderer.jsx +++ b/src/components/Tutorials/subComps/HtmlTextRenderer.jsx @@ -1,7 +1,10 @@ import React from "react"; +import DOMPurify from "dompurify"; const HtmlTextRenderer = ({ html = "

Html Text Renderer

" }) => { - return
; + // used to remove any sensitive tags like