Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate Tutorial creation with backend #846

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
20 changes: 14 additions & 6 deletions src/components/Editor/QuillEditor.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
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";
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);

Expand All @@ -18,11 +19,13 @@ 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();
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: {
Expand All @@ -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();
setCurrentStepContent(tutorial_id, id, html)(firestore, dispatch);
});
provider = new FirestoreProvider(onlineFirebaseApp, ydoc, basePath, {
disableAwareness: true
Expand Down
11 changes: 6 additions & 5 deletions src/components/Tutorials/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ 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";
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";
Expand Down Expand Up @@ -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
);
}
Expand All @@ -179,6 +179,7 @@ const ViewTutorial = () => {
<Grid>
<Grid xs={24} sm={24} md={24}>
<EditControls
isPublished={tutorialData.isPublished}
stepPanelVisible={stepPanelVisible}
isDesktop={isDesktop}
noteID={stepsData[currentStep].id}
Expand Down Expand Up @@ -264,7 +265,7 @@ const ViewTutorial = () => {
<>
{mode === "view" && (
<div data-testId="tutorial-content">
<RichTextRenderer delta={currentStepContent} />
<HtmlTextRenderer html={currentStepContent} />
</div>
)}
{mode === "edit" && (
Expand Down
9 changes: 7 additions & 2 deletions src/components/Tutorials/subComps/EditControls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
import FormatAlignLeftIcon from "@mui/icons-material/FormatAlignLeft";
import FormatPaintIcon from "@mui/icons-material/FormatPaint";
import UserList from "../../Editor/UserList";
import { hideUnHideStep } from "../../../store/actions";
import { publishUnpublishTutorial } from "../../../store/actions";
import { useFirebase, useFirestore } from "react-redux-firebase";
import { useDispatch } from "react-redux";
import RemoveStepModal from "./RemoveStepModal";
import ColorPickerModal from "./ColorPickerModal";
import { Box, Stack } from "@mui/system";

const EditControls = ({
isPublished,
stepPanelVisible,
isDesktop,
setMode,
Expand Down Expand Up @@ -102,6 +103,10 @@ const EditControls = ({
);
};

const handlePublishTutorial = () => {
publishUnpublishTutorial(owner, tutorial_id, isPublished)(firebase, firestore, dispatch);
}

return (
<>
<Stack
Expand Down Expand Up @@ -220,7 +225,7 @@ const EditControls = ({
<FileCopyIcon /> Preview mode
</Button>
)}
<Button data-testid={"publishTutorial"} type="dashed">
<Button data-testid={"publishTutorial"} onClick={handlePublishTutorial} type="dashed">
<FileCopyIcon /> Publish
</Button>
<DropdownMenu key="more" />
Expand Down
10 changes: 10 additions & 0 deletions src/components/Tutorials/subComps/HtmlTextRenderer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from "react";
import DOMPurify from "dompurify";

const HtmlTextRenderer = ({ html = "<p>Html Text Renderer</p>" }) => {
// used to remove any sensitive tags like <script> which might me malicious
const sanitizedHTML = DOMPurify.sanitize(html);
return <div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />;
};

export default HtmlTextRenderer;
15 changes: 0 additions & 15 deletions src/components/Tutorials/subComps/RichTextRenderer.jsx

This file was deleted.

4 changes: 3 additions & 1 deletion src/store/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,18 @@ export {
clearTutorialImagesReducer,
clearTutorialsBasicData,
createTutorial,
getCurrentStepContentFromRTDB,
getCurrentStepContentFromFirestore,
getCurrentTutorialData,
getOrgTutorialsBasicData,
getUserTutorialsBasicData,
hideUnHideStep,
publishUnpublishTutorial,
remoteTutorialImages,
removeStep,
searchFromTutorialsIndex,
setCurrentStep,
setCurrentStepNo,
setCurrentStepContent,
setTutorialTheme,
updateStepTime,
updateStepTitle,
Expand Down
Loading