mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
Merge pull request #77 from learnhouse/swve/eng-22-improve-editor-topbar
Improve editor elements
This commit is contained in:
commit
7e9ac3712b
7 changed files with 143 additions and 21 deletions
|
|
@ -8,7 +8,7 @@ import { getActivity } from "@services/courses/activities";
|
|||
import AuthProvider from "@components/Security/AuthProvider";
|
||||
import EditorWrapper from "@components/Editor/EditorWrapper";
|
||||
import useSWR, { mutate } from "swr";
|
||||
import { getAPIUrl } from "@services/config/config";
|
||||
import { getAPIUrl, getOrgFromUri } from "@services/config/config";
|
||||
import { swrFetcher } from "@services/utils/ts/requests";
|
||||
|
||||
|
||||
|
|
@ -16,15 +16,18 @@ function EditActivity(params: any) {
|
|||
const router = useRouter();
|
||||
const activityid = params.params.activityid;
|
||||
const courseid = params.params.courseid;
|
||||
const orgslug = params.params.orgslug;
|
||||
const { data: courseInfo, error: error_course } = useSWR(`${getAPIUrl()}courses/meta/course_${courseid}`, swrFetcher);
|
||||
const { data: activity, error: error_activity } = useSWR(`${getAPIUrl()}activities/activity_${activityid}`, swrFetcher);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<AuthProvider>
|
||||
{!courseInfo || !activity ? <div>Loading...</div> : <EditorWrapper course={courseInfo} activity={activity} content={activity.content}></EditorWrapper>}
|
||||
{!courseInfo || !activity ? <div>Loading...</div> : <EditorWrapper orgslug={orgslug} course={courseInfo} activity={activity} content={activity.content}></EditorWrapper>}
|
||||
</AuthProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import { ToolbarButtons } from "./Toolbar/ToolbarButtons";
|
|||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import Image from "next/image";
|
||||
import styled from "styled-components";
|
||||
import { getBackendUrl } from "@services/config/config";
|
||||
import { SlashIcon } from "@radix-ui/react-icons";
|
||||
import { getBackendUrl, getUriWithOrg } from "@services/config/config";
|
||||
import { DividerVerticalIcon, EyeOpenIcon, SlashIcon } from "@radix-ui/react-icons";
|
||||
import Avvvatars from "avvvatars-react";
|
||||
// extensions
|
||||
import InfoCallout from "./Extensions/Callout/Info/InfoCallout";
|
||||
|
|
@ -18,22 +18,30 @@ import WarningCallout from "./Extensions/Callout/Warning/WarningCallout";
|
|||
import ImageBlock from "./Extensions/Image/ImageBlock";
|
||||
import Youtube from "@tiptap/extension-youtube";
|
||||
import VideoBlock from "./Extensions/Video/VideoBlock";
|
||||
import { Save } from "lucide-react";
|
||||
import { Eye, Save } from "lucide-react";
|
||||
import MathEquationBlock from "./Extensions/MathEquation/MathEquationBlock";
|
||||
import PDFBlock from "./Extensions/PDF/PDFBlock";
|
||||
import QuizBlock from "./Extensions/Quiz/QuizBlock";
|
||||
import ToolTip from "@components/UI/Tooltip/Tooltip";
|
||||
import Link from "next/link";
|
||||
|
||||
interface Editor {
|
||||
content: string;
|
||||
ydoc: any;
|
||||
provider: any;
|
||||
activity: any;
|
||||
orgslug: string
|
||||
course: any;
|
||||
setContent: (content: string) => void;
|
||||
}
|
||||
|
||||
function Editor(props: Editor) {
|
||||
const auth: any = React.useContext(AuthContext);
|
||||
// remove course_ from course_id
|
||||
const course_id = props.course.course.course_id.substring(7);
|
||||
|
||||
// remove activity_ from activity_id
|
||||
const activity_id = props.activity.activity_id.substring(9);
|
||||
|
||||
const editor: any = useEditor({
|
||||
editable: true,
|
||||
|
|
@ -107,15 +115,17 @@ function Editor(props: Editor) {
|
|||
<EditorTop>
|
||||
<EditorDocSection>
|
||||
<EditorInfoWrapper>
|
||||
<Link href="/">
|
||||
<EditorInfoLearnHouseLogo width={25} height={25} src={learnhouseIcon} alt="" />
|
||||
</Link>
|
||||
<Link target="_blank" href={`/course/${course_id}`}>
|
||||
<EditorInfoThumbnail src={`${getBackendUrl()}content/uploads/img/${props.course.course.thumbnail}`} alt=""></EditorInfoThumbnail>
|
||||
</Link>
|
||||
<EditorInfoDocName>
|
||||
{" "}
|
||||
<b>{props.course.course.name}</b> <SlashIcon /> {props.activity.name}{" "}
|
||||
</EditorInfoDocName>
|
||||
<EditorSaveButton onClick={() => props.setContent(editor.getJSON())}>
|
||||
Save <Save size={11} />
|
||||
</EditorSaveButton>
|
||||
|
||||
</EditorInfoWrapper>
|
||||
<EditorButtonsWrapper>
|
||||
<ToolbarButtons editor={editor} />
|
||||
|
|
@ -126,6 +136,11 @@ function Editor(props: Editor) {
|
|||
{!auth.isAuthenticated && <span>Loading</span>}
|
||||
{auth.isAuthenticated && <Avvvatars value={auth.userInfo.user_object.user_id} style="shape" />}
|
||||
</EditorUserProfileWrapper>
|
||||
<DividerVerticalIcon style={{ marginTop: "auto", marginBottom: "auto", color: "grey" }} />
|
||||
<EditorLeftOptionsSection>
|
||||
<EditorLeftOptionsSaveButton onClick={() => props.setContent(editor.getJSON())}> Save </EditorLeftOptionsSaveButton>
|
||||
<ToolTip content="Preview"><Link target="_blank" href={`/course/${course_id}/activity/${activity_id}`}><EditorLeftOptionsPreviewButton> <Eye size={15} /> </EditorLeftOptionsPreviewButton></Link></ToolTip>
|
||||
</EditorLeftOptionsSection>
|
||||
</EditorUsersSection>
|
||||
</EditorTop>
|
||||
</motion.div>
|
||||
|
|
@ -186,11 +201,63 @@ const EditorDocSection = styled.div`
|
|||
`;
|
||||
const EditorUsersSection = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const EditorLeftOptionsSection = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const EditorLeftOptionsSaveButton = styled.button`
|
||||
background-color: #8783f7;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 8px;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
|
||||
|
||||
&:hover {
|
||||
background-color: #4a44f9;
|
||||
opacity: 0.8;
|
||||
}
|
||||
`;
|
||||
|
||||
const EditorLeftOptionsPreviewButton = styled.button`
|
||||
background-color: #a4a4a449;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
color: #000000;
|
||||
padding: 8px;
|
||||
margin-right: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
|
||||
// center icon
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background-color: #c0bfbf;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
|
||||
// Inside EditorDocSection
|
||||
const EditorInfoWrapper = styled.div`
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -3,14 +3,17 @@ import * as Y from "yjs";
|
|||
import { WebrtcProvider } from "y-webrtc";
|
||||
import Editor from "./Editor";
|
||||
import { updateActivity } from "@services/courses/activities";
|
||||
import { toast } from "react-hot-toast";
|
||||
import Toast from "@components/UI/Toast/Toast";
|
||||
|
||||
interface EditorWrapperProps {
|
||||
content: string;
|
||||
activity: any;
|
||||
course:any
|
||||
course: any
|
||||
orgslug: string;
|
||||
}
|
||||
|
||||
function EditorWrapper(props: EditorWrapperProps) : JSX.Element {
|
||||
function EditorWrapper(props: EditorWrapperProps): JSX.Element {
|
||||
// A new Y document
|
||||
const ydoc = new Y.Doc();
|
||||
const [providerState, setProviderState] = React.useState<any>({});
|
||||
|
|
@ -24,18 +27,31 @@ function EditorWrapper(props: EditorWrapperProps) : JSX.Element {
|
|||
setIsLoading(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function setContent(content: any) {
|
||||
let activity = props.activity;
|
||||
activity.content = content;
|
||||
const res = await updateActivity(activity, activity.activity_id);
|
||||
alert(JSON.stringify(res));
|
||||
|
||||
toast.promise(
|
||||
updateActivity(activity, activity.activity_id),
|
||||
{
|
||||
loading: 'Saving...',
|
||||
success: <b>Activity saved!</b>,
|
||||
error: <b>Could not save.</b>,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
createRTCProvider();
|
||||
return <div>Loading...</div>;
|
||||
} else {
|
||||
return <Editor course={props.course} activity={props.activity} content={props.content} setContent={setContent} provider={providerState} ydoc={ydocState}></Editor>;
|
||||
return <>
|
||||
<Toast></Toast>
|
||||
<Editor orgslug={props.orgslug} course={props.course} activity={props.activity} content={props.content} setContent={setContent} provider={providerState} ydoc={ydocState}></Editor>;
|
||||
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
11
front/components/UI/Toast/Toast.tsx
Normal file
11
front/components/UI/Toast/Toast.tsx
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import React from 'react'
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
|
||||
|
||||
function Toast() {
|
||||
return (
|
||||
<><Toaster /></>
|
||||
)
|
||||
}
|
||||
|
||||
export default Toast
|
||||
24
front/package-lock.json
generated
24
front/package-lock.json
generated
|
|
@ -31,6 +31,7 @@
|
|||
"react": "^18.2.0",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-katex": "^3.0.1",
|
||||
"react-spinners": "^0.13.8",
|
||||
"styled-components": "^6.0.0-beta.9",
|
||||
|
|
@ -6952,6 +6953,21 @@
|
|||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
|
||||
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
|
||||
},
|
||||
"node_modules/react-hot-toast": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz",
|
||||
"integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==",
|
||||
"dependencies": {
|
||||
"goober": "^2.1.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16",
|
||||
"react-dom": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
|
|
@ -13070,6 +13086,14 @@
|
|||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
|
||||
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
|
||||
},
|
||||
"react-hot-toast": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz",
|
||||
"integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==",
|
||||
"requires": {
|
||||
"goober": "^2.1.10"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
"react": "^18.2.0",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-katex": "^3.0.1",
|
||||
"react-spinners": "^0.13.8",
|
||||
"styled-components": "^6.0.0-beta.9",
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ async def get_course_meta(request: Request, course_id: str, current_user: Public
|
|||
for chapter in chapters:
|
||||
chapters_list_with_activities.append(
|
||||
{"id": chapters[chapter]["id"], "name": chapters[chapter]["name"], "activities": [activities_list[activity] for activity in chapters[chapter]["activityIds"]]})
|
||||
course = Course(**course)
|
||||
course = CourseInDB(**course)
|
||||
|
||||
# Get activity by user
|
||||
trail = await trails.find_one(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue