Files
classroomio/packages/ui/src/custom/public-course/lesson-view.svelte
T

97 lines
3.0 KiB
Svelte

<script lang="ts">
import { cn } from '../../tools';
import { SafeHtmlContent } from '../safe-html-content';
import { MediaPlayer } from '../media-player';
import Callout from './callout.svelte';
import type { PublicCourseCalloutAnimation, PublicCourseCalloutData, PublicLessonViewData } from './types';
interface Props {
lesson: PublicLessonViewData;
/** Shown in the player captions menu; supplied by the host app (`$t(...)`) — see authenticated `lesson-video-player`. */
videoCaptionsLabel: string;
callout?: PublicCourseCalloutData | null;
calloutAnimation?: PublicCourseCalloutAnimation;
class?: string;
id?: string;
}
let { lesson, videoCaptionsLabel, callout = null, calloutAnimation, class: className, id }: Props = $props();
const resolvedCalloutAnimation = $derived<PublicCourseCalloutAnimation>(
calloutAnimation ?? callout?.animation ?? 'waves'
);
const captionTracks = $derived.by(() => {
const transcript = lesson.video?.transcript;
if (!transcript?.vttUrl) {
return [];
}
return [
{
kind: 'captions' as const,
src: transcript.vttUrl,
srclang: transcript.language,
label: videoCaptionsLabel,
default: false
}
];
});
</script>
<!--
Public lesson page. Width / spacing intentionally mirrors the authenticated
lesson view (see `apps/dashboard/src/lib/features/course/pages/lesson.svelte`)
so creators see the same content layout whether they're logged in or sharing
a public link. The `prose` class (no `ui:` prefix) hooks into the dashboard's
app.css typography rules.
-->
<div {id} class={cn('ui:mx-auto ui:w-full ui:max-w-3xl ui:px-4 ui:py-8 ui:sm:px-6 ui:lg:py-10', className)}>
{#if !lesson.isUnlocked}
<Callout variant="full" {callout} animation={resolvedCalloutAnimation} />
{:else}
{#if lesson.video}
<div class="ui:mb-6 ui:w-full ui:overflow-hidden ui:bg-muted ui:rounded-lg">
<MediaPlayer
source={{
type: lesson.video.type,
url: lesson.video.link,
metadata: {
...(lesson.video.metadata ?? {}),
title: lesson.video.metadata?.title ?? lesson.title
},
tracks: captionTracks
}}
options={{
maxHeight: '569px',
width: '100%',
controls: true,
playsinline: true
}}
/>
</div>
{/if}
{#if lesson.sectionTitle}
<div class="ui:text-xs ui:font-medium ui:uppercase ui:tracking-wide ui:text-muted-foreground">
{lesson.sectionTitle}
</div>
{/if}
<h1 class="ui:mt-2 ui:text-2xl ui:tracking-tight ui:text-foreground ui:sm:text-3xl">
{lesson.title}
</h1>
<div class="prose sm:prose-sm ui:mt-8 ui:max-w-none ui:dark:text-white">
{#if lesson.body}
<SafeHtmlContent content={lesson.body} />
{:else}
<p class="ui:text-muted-foreground">No content yet.</p>
{/if}
</div>
<Callout variant="inline" {callout} animation={resolvedCalloutAnimation} />
{/if}
</div>