Skip to content
This repository has been archived by the owner on Jul 14, 2021. It is now read-only.

feature/SC-3505/nexboard #230

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
1 change: 1 addition & 0 deletions deploy/env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ envs=(
"SERVER_API_URL"
"EDITOR_SOCKET_URL"
"HOMEWORK_URI"
"ETHERPAD_URL"
"ENABLE_LTI"
)

Expand Down
7 changes: 6 additions & 1 deletion src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ const config = {
EDITOR_API_URL: window.EDITOR_API_URL || process.env.EDITOR_API_URL || "http://localhost:4001",
SERVER_API_URL: window.SERVER_API_URL || process.env.SERVER_API_URL || "http://localhost:3030",
EDITOR_SOCKET_URL: window.EDITOR_SOCKET_URL || process.env.EDITOR_SOCKET_URL || "ws://localhost:4001",
ETHERPAD_URL: window.ETHERPAD_URL || process.env.ETHERPAD_URL || "https://etherpad.schul-cloud.org/p/",

HOMEWORK_URI: window.HOMEWORK_URI || process.env.HOMEWORK_URI || "/homework",
NEXBOARD_BOARDS_URI: window.NEXBOARD_BOARD_URI || process.env.NEXBOARD_BOARD_URI || '/nexboard/boards',
NEXBOARD_PROJECTS_URI: window.NEXBOARD_PROJECTS_URI || process.env.NEXBOARD_PROJECTS_URI || '/nexboard/projects',

ENABLE_LTI: window.ENABLE_LTI || process.env.ENABLE_LTI || false,

breakpoints: {
Expand All @@ -22,5 +27,5 @@ const config = {
};

// eslint-disable-next-line no-console
console.info("config", config);
console.info("config", JSON.stringify(config));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have no proper logger available?

Copy link
Contributor Author

@CeEv CeEv Mar 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment no. It is log it to the browser console. We will later wrap the logger into a util. But i do not know if we must replace it with any popular logger. It will pass additional kBit to the editor that do not more then the base console logger.
If any error is throw it is pass to the user information, or to sentry send us. But this stuff is not finished, only as prototype.

export default config;
2 changes: 1 addition & 1 deletion src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// We need the RAW file from the root dir
// https://github.com/parcel-bundler/parcel/issues/1087#issuecomment-576810737
const script = document.createElement("script");
script.setAttribute("src", "env.js");
script.setAttribute("src", "/env.js");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would this make problems when hosting in a subpath

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. The env.js is placed on the same location like the editor base stuff. But the urls /course/id/... also include in the path if they load the env without /.

script.setAttribute("type", "text/javascript");
document.head.prepend(script);
</script>
Expand Down
19 changes: 5 additions & 14 deletions src/plugins/etherpad/Etherpad.jsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
import React, { useState, useEffect, useContext } from "react"
import React, { useEffect } from "react"
import shortid from "shortid"

import LessonContext from "~/Contexts/Lesson.context"
import config from "~/config"

import Input from "~/components/Input"
import Flex from "~/components/Flex"

import { createBoard } from "./utils"

const Etherpad = ({ focused, state }) => {
const Etherpad = ({ state }) => {
useEffect(() => {
if (state._id.value) return
state._id.set(shortid.generate())
}, [])

const { store, dispatch } = useContext(LessonContext)
const url = config.ETHERPAD_URL;

let etherpadFrame
if (state._id.get()) {
return (
<iframe
src={`https://etherpad.schul-cloud.org/p/${
// TODO maybe make this variable?
state._id.value
}`}
src={url+state._id.value}
style={{
width: "100%",
height: 800,
Expand Down
Empty file removed src/plugins/etherpad/utils.js
Empty file.
21 changes: 18 additions & 3 deletions src/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,15 @@ import { createImagePlugin } from "@edtr-io/plugin-image"
// import { h5pPlugin } from "@edtr-io/plugin-h5p"

// import nexboardPlugin from "./nexboard"
import etherpadPlugin, {SizedEtherpadIcon} from "./etherpad"
import etherpadPlugin, { SizedEtherpadIcon } from "./etherpad"
import etherpadPluginPreview from "./etherpad/Preview"

import nexboardPlugin, { SizedNextboardIcon } from "./nexboard"
import nexboardPluginPreview from "./nexboard/Preview"

// import lichtblickPlugin, {SizedLichtblickIcon} from "./lichtblick"
// import lichtblickPluginPreview from "./lichtblick/Preview"

import homework, {SizedHomeworkIcon} from "./homework"
import ltiPlugin, {SizedLtiIcon} from "./lti"
import ltiPluginPreview from "./lti/Preview"
Expand Down Expand Up @@ -305,17 +310,27 @@ addPlugin({
addPlugin({
name: 'etherpad',
title: 'Etherpad',
icon: SizedEtherpadIcon,
description: 'Real time collaboration',
icon: SizedEtherpadIcon,
plugin: etherpadPlugin,
preview: etherpadPluginPreview
})


addPlugin({
name: "nxboard",
title: "Nexboard",
description: "Benutze das digitale Nexboard, um all deine Ideen festzuhalten!",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please localize

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

follow up

icon: SizedNextboardIcon,
plugin: nexboardPlugin,
preview: nexboardPluginPreview
})

addPlugin({
name: 'homework',
title: 'Aufgabe',
icon: SizedHomeworkIcon,
description: "Binde eine Aufgabe aus dem Kurs ein.",
icon: SizedHomeworkIcon,
plugin: homework,
preview: homeworkPreview,
})
Expand Down
118 changes: 67 additions & 51 deletions src/plugins/nexboard/Nexboard.jsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,82 @@
import React, { useState, useEffect, useContext } from "react"
import LessonContext from "~/Contexts/Lesson.context"
import UserContext from "~/Contexts/User"

import { createBoard, getBoard } from "./utils"
import Action from "~/components/Action"
import Flex from "~/components/Flex"
import Loader from "~/components/Loader"

const Nexboard = ({ focused, state }) => {
const [loading, setLoading] = useState(true)
const [board, setBoard] = useState({})
const { store } = useContext(LessonContext)
const { store: user } = useContext(UserContext)
async function bootstrap() {
try {
let board
if (state._id.value) {
board = await getBoard(state._id.value)
} else {
board = await createBoard(
store.lesson._id,
`${store.lesson.title} Nexboard`,
)
}
state._id.set(board._id)
setBoard(board)
} catch (err) {}
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [board, setBoard] = useState({})
const { store } = useContext(LessonContext)

Metauriel marked this conversation as resolved.
Show resolved Hide resolved
async function bootstrap() {
try {
let board
const id = state.id.get();
if (id && id !== '<empty string>') {
board = await getBoard(id)
} else {
const { _id: lessonId, attachments, title } = store.lesson;
board = await createBoard(lessonId.toString(),{
title:`${title} Nexboard`, // TODO: should set by user
attachments,
description: ''
})
state.id.set(board.id.toString())
}
setBoard(board)
} catch (err) {
console.warn(err);
setError(err);
}

setLoading(false)
}
setLoading(false)
}

useEffect(() => {
bootstrap()
}, [])
useEffect(() => {
bootstrap()
}, [])

if (loading) {
return (
<Flex justifyCenter>
<Loader />
</Flex>
)
} else {
return (
<Flex column alignEnd>
<iframe
src={`${board.publicLink}?username=${user.displayName}`}
style={{
width: "100%",
height: "600px",
resize: "vertical",
overflow: "auto",
border: "none",
}}
/>
<Action
a
to={`${board.publicLink}?username=${user.displayName}`}
target="_blank">
if (error) {
return (
<div>
<p>Can not render nexboard!</p>
{JSON.stringify(error.message)}
</div>
)
} else if (loading) {
return (
<Flex justifyCenter>
<Loader />
</Flex>
)
} else {
const displayName = (store.user || {}).displayName || 'user';
// TODO: Username is not pass at he moment
return (
<Flex column alignEnd>
<iframe
src={`${board.publicLink}?username=${displayName}`}
style={{
width: "100%",
height: "600px",
resize: "vertical",
overflow: "auto",
border: "none",
}}
/>
<Action
a
to={`${board.publicLink}?username=${displayName}`}
target="_blank">
in neuem Tab öffnen
</Action>
</Flex>
)
}
</Action>
</Flex>
)
}
}

export default Nexboard
12 changes: 5 additions & 7 deletions src/plugins/nexboard/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,19 @@ import { object, string } from "@edtr-io/plugin"

import Nexboard from "./Nexboard"

import NextboadIcon from "./assets/logo.png"
import NextboadIconSVG from "./assets/logo.svg"

export const nexboardState = object({
id: string(),
})

export const SizedNextboardIcon = () => (
<NextboadIconSVG width="100%" />
)

export const generatePlugin = (Component) => ({
Component,
state: nexboardState,
icon: () => (
<NextboadIcon width="100%" />
),
title: "Nexboard",
description:
"Benutze das digitale Nexboard, um all deine Ideen festzuhalten!",
})

const nexboardPlugin = generatePlugin(Nexboard)
Expand Down
52 changes: 26 additions & 26 deletions src/plugins/nexboard/utils.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
import { serverApi } from "~/utils/api"
import { editorApi, serverApi } from "~/utils/api"
import config from "~/config"
// TODO: do not use sockets for creating attachments
// TODO: config is not set
const { NEXBOARD_BOARDS_URI, NEXBOARD_PROJECTS_URI } = config;

export const createBoard = async (lessonId, title) => {
let nexboardAttachment = await serverApi.get(
`/editor/attachments?key=nexboard&lesson=${lessonId}`,
)
const TYPE = 'nexboard-projectId';
export const createBoard = async (lessonId, { title, attachments = [], description }) => {
let attach = attachments.find(a => a.type === TYPE)
if (!attach) {
const project = await serverApi.post(NEXBOARD_PROJECTS_URI, {
title,
})
attach = await editorApi.post("attachments", {
type: TYPE,
value: project.id.toString(),
target: lessonId,
targetModel: 'lesson'
})
}

if (!nexboardAttachment.length) {
const nexboardProject = await serverApi.post(`/nexboard/projects`, {
// title,
})

nexboardAttachment = await serverApi.post("/editor/attachments", {
key: "nexboard",
value: nexboardProject._id,
lesson: lessonId,
})
} else {
nexboardAttachment = nexboardAttachment[0]
}

const board = await serverApi.post(`/nexboard/boards`, {
title,
projectId: nexboardAttachment.value,
// description,
})
return board
const board = await serverApi.post(NEXBOARD_BOARDS_URI, {
title,
projectId: attach.value.toString(),
description,
})
return board
}

export const getBoard = id => {
return serverApi.get(`/nexboard/boards/${id}`)
return serverApi.get(`${NEXBOARD_BOARDS_URI }/${id.toString()}`)
}