feat: support embed of generic links
This commit is contained in:
+80
@@ -0,0 +1,80 @@
|
|||||||
|
<script>
|
||||||
|
import { CopyButton, Tag } from 'carbon-components-svelte';
|
||||||
|
import TrashCanIcon from 'carbon-icons-svelte/lib/TrashCan.svelte';
|
||||||
|
import IconButton from '$lib/components/IconButton/index.svelte';
|
||||||
|
import TextField from '$lib/components/Form/TextField.svelte';
|
||||||
|
import { lesson, isLessonDirty } from '$lib/components/Course/components/Lesson/store/lessons';
|
||||||
|
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||||
|
|
||||||
|
let genericLinks = '';
|
||||||
|
let error = '';
|
||||||
|
|
||||||
|
function getVideoUrls(urls = '') {
|
||||||
|
return (urls || '').split(',').filter((url) => !!url.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
function addVideo() {
|
||||||
|
const links = getVideoUrls(genericLinks);
|
||||||
|
const validLinks = links.filter(isValidLink);
|
||||||
|
|
||||||
|
if (validLinks.length === 0) {
|
||||||
|
error = 'Invalid link(s)';
|
||||||
|
} else {
|
||||||
|
const existingLinks = $lesson?.materials?.videos || [];
|
||||||
|
|
||||||
|
$lesson.materials.videos = [
|
||||||
|
...existingLinks,
|
||||||
|
...validLinks.map((link = '') => ({
|
||||||
|
type: 'generic',
|
||||||
|
link,
|
||||||
|
metadata: {}
|
||||||
|
}))
|
||||||
|
];
|
||||||
|
genericLinks = '';
|
||||||
|
error = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidLink(link = '') {
|
||||||
|
const linkRegex = /^(https?:\/\/)?(www\.)?([a-zA-Z]+\.com\/)([a-zA-Z0-9_-]+)/;
|
||||||
|
|
||||||
|
return linkRegex.test(link.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeVideo(index = 0) {
|
||||||
|
$lesson.materials.videos = $lesson.materials.videos.filter((v, i) => i !== index);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="w-full flex items-{error ? 'center' : 'end'} justify-between gap-5">
|
||||||
|
<TextField
|
||||||
|
label="Embed link"
|
||||||
|
bind:value={genericLinks}
|
||||||
|
className="flex-1 text-left "
|
||||||
|
inputClassName="text-sm"
|
||||||
|
onChange={() => ($isLessonDirty = true)}
|
||||||
|
placeholder="https://www.videoplayer.com/"
|
||||||
|
errorMessage={error}
|
||||||
|
/>
|
||||||
|
<PrimaryButton label="Add Video" className="rounded-md" onClick={addVideo} />
|
||||||
|
</div>
|
||||||
|
<p class="mt-4 pl-2 text-sm">
|
||||||
|
Videos added: <strong>
|
||||||
|
{$lesson?.materials?.videos.filter((v) => v.type === 'generic').length || 0}
|
||||||
|
</strong>
|
||||||
|
</p>
|
||||||
|
<div class="">
|
||||||
|
{#each $lesson?.materials?.videos as video, index}
|
||||||
|
{#if video.type === 'generic'}
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<Tag type="blue">
|
||||||
|
{video.link}
|
||||||
|
</Tag>
|
||||||
|
<CopyButton text={video.link} feedback="Copied to clipboard" />
|
||||||
|
<IconButton value="delete-video" onClick={() => removeVideo(index)}>
|
||||||
|
<TrashCanIcon size={16} class="carbon-icon dark:text-white" />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
+3
@@ -1,6 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import YoutubeVideo from './YoutubeVideo.svelte';
|
import YoutubeVideo from './YoutubeVideo.svelte';
|
||||||
import LocalVideo from './LocalVideo.svelte';
|
import LocalVideo from './LocalVideo.svelte';
|
||||||
|
import EmbedLink from './EmbedLink.svelte';
|
||||||
import * as CONSTANTS from './constants';
|
import * as CONSTANTS from './constants';
|
||||||
|
|
||||||
export let lessonId = '';
|
export let lessonId = '';
|
||||||
@@ -37,6 +38,8 @@
|
|||||||
<main class="w-full h-full">
|
<main class="w-full h-full">
|
||||||
{#if currentTab === 1}
|
{#if currentTab === 1}
|
||||||
<YoutubeVideo />
|
<YoutubeVideo />
|
||||||
|
{:else if currentTab === 2}
|
||||||
|
<EmbedLink />
|
||||||
{:else}
|
{:else}
|
||||||
<LocalVideo {lessonId} />
|
<LocalVideo {lessonId} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
+8
-1
@@ -1,5 +1,7 @@
|
|||||||
import Link from 'carbon-icons-svelte/lib/Link.svelte';
|
import Link from 'carbon-icons-svelte/lib/Link.svelte';
|
||||||
import Video from 'carbon-icons-svelte/lib/Video.svelte';
|
import Video from 'carbon-icons-svelte/lib/Video.svelte';
|
||||||
|
import DirectLink from 'carbon-icons-svelte/lib/DirectLink.svelte';
|
||||||
|
|
||||||
export const videoTabs = [
|
export const videoTabs = [
|
||||||
{
|
{
|
||||||
value: 1,
|
value: 1,
|
||||||
@@ -8,7 +10,12 @@ export const videoTabs = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 2,
|
value: 2,
|
||||||
title: 'Video',
|
title: 'Embed Link',
|
||||||
|
icon: DirectLink
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 3,
|
||||||
|
title: 'Upload',
|
||||||
icon: Video
|
icon: Video
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
+11
@@ -35,6 +35,17 @@
|
|||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
allowfullscreen
|
allowfullscreen
|
||||||
/>
|
/>
|
||||||
|
{:else if video.type === 'generic'}
|
||||||
|
<iframe
|
||||||
|
width="100%"
|
||||||
|
height="569"
|
||||||
|
class="iframe"
|
||||||
|
src={video.link}
|
||||||
|
title="Embeded Video player"
|
||||||
|
frameborder="0"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
|
allowfullscreen
|
||||||
|
/>
|
||||||
{:else if video.metadata?.svid}
|
{:else if video.metadata?.svid}
|
||||||
<div style="position:relative;padding-bottom:51.416579%">
|
<div style="position:relative;padding-bottom:51.416579%">
|
||||||
<iframe
|
<iframe
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
export let prevMode = '';
|
export let prevMode = '';
|
||||||
export let lessonId = '';
|
export let lessonId = '';
|
||||||
export let isSaving = false;
|
export let isSaving = false;
|
||||||
|
export let isStudent = false;
|
||||||
export let toggleMode = () => {};
|
export let toggleMode = () => {};
|
||||||
|
|
||||||
let lessonTitle = '';
|
let lessonTitle = '';
|
||||||
@@ -343,6 +344,17 @@
|
|||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
allowfullscreen
|
allowfullscreen
|
||||||
/>
|
/>
|
||||||
|
{:else if video.type === 'generic'}
|
||||||
|
<iframe
|
||||||
|
width="100%"
|
||||||
|
height="569"
|
||||||
|
class="iframe"
|
||||||
|
src={video.link}
|
||||||
|
title="Embeded Video player"
|
||||||
|
frameborder="0"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
|
allowfullscreen
|
||||||
|
/>
|
||||||
{:else if video.metadata?.svid}
|
{:else if video.metadata?.svid}
|
||||||
<div style="position:relative;padding-bottom:51.416579%">
|
<div style="position:relative;padding-bottom:51.416579%">
|
||||||
<iframe
|
<iframe
|
||||||
@@ -381,10 +393,12 @@
|
|||||||
<h3 class="text-xl font-normal dark:text-white py-2">
|
<h3 class="text-xl font-normal dark:text-white py-2">
|
||||||
No note, video or slide added for this lesson yet
|
No note, video or slide added for this lesson yet
|
||||||
</h3>
|
</h3>
|
||||||
<p class="text-sm text-center font-normal py-2">
|
{#if !isStudent}
|
||||||
Share your knowledge with your students by creating engaging content<br />
|
<p class="text-sm text-center font-normal py-2">
|
||||||
Start by clicking on <strong>Get started</strong> button.
|
Share your knowledge with your students by creating engaging content<br />
|
||||||
</p>
|
Start by clicking on <strong>Get started</strong> button.
|
||||||
<PrimaryButton label="Get started" className="rounded-md" onClick={toggleMode} />
|
</p>
|
||||||
|
<PrimaryButton label="Get started" className="rounded-md" onClick={toggleMode} />
|
||||||
|
{/if}
|
||||||
</Box>
|
</Box>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -298,7 +298,16 @@
|
|||||||
width="lg:w-full xl:w-11/12"
|
width="lg:w-full xl:w-11/12"
|
||||||
className="overflow-x-hidden"
|
className="overflow-x-hidden"
|
||||||
>
|
>
|
||||||
<Materials lessonId={data.lessonId} {mode} {prevMode} {toggleMode} bind:isSaving />
|
<!-- Shows Lesson Note / Slides / Video -->
|
||||||
|
<Materials
|
||||||
|
lessonId={data.lessonId}
|
||||||
|
{mode}
|
||||||
|
{prevMode}
|
||||||
|
{toggleMode}
|
||||||
|
bind:isSaving
|
||||||
|
{isStudent}
|
||||||
|
/>
|
||||||
|
|
||||||
{#if isStudent}
|
{#if isStudent}
|
||||||
<div class="w-full hidden lg:flex flex-row-reverse mt-10">
|
<div class="w-full hidden lg:flex flex-row-reverse mt-10">
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
|
|||||||
Reference in New Issue
Block a user