Feat: interactive courses (#736)
* add courses page * add api for courses * add course page * update course type * update client * update course page * refactor courses pages * render chapter content * shadow in course * fix video handler * update style * mark finished examples * fix media player * update locale * finish chapter * refactor * auto update chapter status * audo finish chapter * fix media provider * fix wavesurfer player * update continue btn * refactor chapters & page * minor fix * fix undefined * refactor * refactor * disable sentry in dev * clean markdown format before alignment * refactor * fix regenerate * fix transcription pre-process for `-` connector * upgrade deps * handle no chapters * add llm chat api * create llm chat * display llm message * create message * handle error * generate llm message * display llm datetime * scroll to message * tts for llm message * add course provider * refactor * translate llm message * fix llm chat introduction * refacotr * upgrade deps * refactor style * handle undefined * fix posts * update locales * update courses api * add enrollments count * upgrade yarn * upgrade deps * restore dep to fix package in mac * upgrade deps
This commit is contained in:
@@ -5,6 +5,7 @@ export * from "./layout";
|
||||
export * from "./loader-spin";
|
||||
export * from "./login-form";
|
||||
export * from "./github-login-form";
|
||||
export * from "./markdown-wrapper";
|
||||
export * from "./mixin-login-form";
|
||||
export * from "./no-records-found";
|
||||
export * from "./page-placeholder";
|
||||
|
||||
30
enjoy/src/renderer/components/misc/markdown-wrapper.tsx
Normal file
30
enjoy/src/renderer/components/misc/markdown-wrapper.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import Markdown from "react-markdown";
|
||||
|
||||
export const MarkdownWrapper = ({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: {
|
||||
children: string;
|
||||
className?: string;
|
||||
}) => {
|
||||
return (
|
||||
<Markdown
|
||||
className={className}
|
||||
components={{
|
||||
a({ node, children, ...props }) {
|
||||
try {
|
||||
new URL(props.href ?? "");
|
||||
props.target = "_blank";
|
||||
props.rel = "noopener noreferrer";
|
||||
} catch (e) {}
|
||||
|
||||
return <a {...props}>{children}</a>;
|
||||
},
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Markdown>
|
||||
);
|
||||
};
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
ExternalLinkIcon,
|
||||
NotebookPenIcon,
|
||||
SpeechIcon,
|
||||
GraduationCapIcon,
|
||||
} from "lucide-react";
|
||||
import { useLocation, Link } from "react-router-dom";
|
||||
import { t } from "i18next";
|
||||
@@ -71,6 +72,14 @@ export const Sidebar = () => {
|
||||
Icon={HomeIcon}
|
||||
/>
|
||||
|
||||
<SidebarItem
|
||||
href="/courses"
|
||||
label={t("sidebar.courses")}
|
||||
tooltip={t("sidebar.courses")}
|
||||
active={activeTab.startsWith("/courses")}
|
||||
Icon={GraduationCapIcon}
|
||||
/>
|
||||
|
||||
<Separator className="hidden xl:block" />
|
||||
|
||||
<SidebarItem
|
||||
|
||||
@@ -49,12 +49,9 @@ export const WavesurferPlayer = (props: {
|
||||
wavesurfer.isPlaying() ? wavesurfer.pause() : wavesurfer.play();
|
||||
}, [wavesurfer]);
|
||||
|
||||
useEffect(() => {
|
||||
// use the intersection observer to only create the wavesurfer instance
|
||||
// when the player is visible
|
||||
if (!entry?.isIntersecting) return;
|
||||
const initialize = () => {
|
||||
if (!containerRef.current) return;
|
||||
if (!src) return;
|
||||
if (wavesurfer) return;
|
||||
|
||||
const ws = WaveSurfer.create({
|
||||
container: containerRef.current,
|
||||
@@ -73,7 +70,14 @@ export const WavesurferPlayer = (props: {
|
||||
});
|
||||
|
||||
setWavesurfer(ws);
|
||||
}, [src, entry]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!entry?.isIntersecting) return;
|
||||
if (wavesurfer?.options?.url === src) return;
|
||||
|
||||
initialize();
|
||||
}, [src, entry, containerRef]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!wavesurfer) return;
|
||||
|
||||
Reference in New Issue
Block a user