CLASSROOMIO-T-122: Auto saving lesson changes

CLASSROOMIO-T-122: Autosave improvements

CLASSROOMIO-T-122: Add text for Autosave

CLASSROOMIO-T-122: Auto save feature

Merge branch 'CLASSROOMIO-T-122' of https://gitlab.com/thenextgreat/classroomio into CLASSROOMIO-T-122

CLASSROOMIO-T-122: Add Autosave

CLASSROOMIO-T-122: Update savelesson comment

CLASSROOMIO-T-122: Remove autosave from video upload to enable general autosave


Co-authored-by: Rotimi Best <[email protected]>
Co-authored-by: Tunji Adeyemi <[email protected]>


Merge-request: CLASSROOMIO-MR-49
Merged-by: Best Admin <[email protected]>
This commit is contained in:
Rotimi Best
2023-08-18 19:28:41 +00:00
committed by Space Cloud
co-authored by Rotimi Best Tunji Adeyemi
parent 726b513ad0
commit 910d9c50a9
10 changed files with 130 additions and 69 deletions
+4 -1
View File
@@ -32,7 +32,7 @@
}
</script>
<div class="w-full min-h-screen flex items-center justify-center">
<div class="root w-full min-h-screen flex items-center justify-center">
<div class="container border border-gray bg-white dark:bg-gray-800">
<div class="flex items-center flex-col p-2 lg:px-8 lg:py-3">
{#if !showOnlyContent || showLogo}
@@ -85,6 +85,9 @@
</div>
<style>
.root {
background: radial-gradient(circle at 10% 20%, rgb(239, 246, 249) 0%, rgb(206, 239, 253) 90%);
}
.container {
width: 450px;
}
@@ -4,7 +4,6 @@
import * as CONSTANTS from './constants';
export let lessonId = '';
export let saveLesson = () => {};
let tabs = CONSTANTS.videoTabs;
let currentTab = tabs[0].value;
@@ -37,7 +36,7 @@
{#if currentTab === 1}
<YoutubeVideo />
{:else}
<LocalVideo {lessonId} {saveLesson} />
<LocalVideo {lessonId} />
{/if}
</main>
</section>
@@ -6,11 +6,9 @@
deleteLessonVideo,
uploadCourseVideoStore
} from '$lib/components/Course/components/Lesson/store/lessons';
import { enhance } from '$app/forms';
import { Moon } from 'svelte-loading-spinners';
export let lessonId = '';
export let saveLesson = () => {};
let formRes;
let isLoaded = false;
@@ -18,6 +16,15 @@
let submit;
let uploadedFileUrl = '';
let isLoading = false;
let timeoutkey;
const uploadingTexts = [
'Sending your video to our server...',
'Generating a link to your video...',
'Generating transcription for your video...',
'Wrapping things up'
];
let uploadingLoadingText = uploadingTexts[0];
function isVideoAdded(link) {
return $lesson.materials?.videos?.find((v) => v.link === link);
@@ -27,10 +34,8 @@
isLoading = true;
if (!fileInput) return;
console.log('file', fileInput.files[0]);
const formData = new FormData();
formData.append('videoFile', fileInput.files[0]);
console.log('formData', formData);
try {
const response = await axios({
@@ -68,16 +73,35 @@
{
type: 'muse',
link: uploadedFileUrl,
metadata: {}
metadata: response?.metadata || {}
}
];
saveLesson();
}
}
function tryAgain() {
formRes = null;
isLoaded = false;
isLoading = false;
}
$: {
if (isLoading) {
timeoutkey = setTimeout(() => {
console.log('timeout');
const i = uploadingTexts.findIndex((text) => text === uploadingLoadingText);
const next = uploadingTexts[i + 1];
if (next) {
console.log('uploadingLoadingText', next);
uploadingLoadingText = next;
}
}, 5000);
} else if (timeoutkey) {
console.log('clearTimeout');
clearTimeout(timeoutkey);
uploadingLoadingText = uploadingTexts[0];
}
}
$: isDoneUploading(formRes);
@@ -96,7 +120,7 @@
>
{#if isLoading}
<Moon size="40" color="#1d4ed8" unit="px" duration="1s" />
<p class="mt-5">Uploading Video</p>
<p class="mt-5">{uploadingLoadingText}</p>
{:else}
<img src="/upload-video.svg" alt="upload" />
<span class="pt-3">
@@ -1,6 +1,5 @@
<script lang="ts">
import { useCompletion } from 'ai/svelte';
import merge from 'lodash/merge';
import MODES from '$lib/utils/constants/mode.js';
import TrashCanIcon from 'carbon-icons-svelte/lib/TrashCan.svelte';
import IconButton from '$lib/components/IconButton/index.svelte';
@@ -30,12 +29,14 @@
import VideoUploader from '$lib/components/Course/components/Lesson/Materials/Video/Index.svelte';
import { course } from '$lib/components/Course/store';
import * as CONSTANTS from './constants';
import { fetchLesson } from '$lib/utils/services/courses';
export let mode = MODES.view;
export let prevMode = '';
export let lessonId = '';
export let isSaving = false;
let initAutoSave = false;
let timeoutId: NodeJS.Timeout;
let tabs = CONSTANTS.tabs;
let currentTab = tabs[0].value;
let errors = {};
@@ -51,18 +52,14 @@
() =>
(currentTab = tab);
// problem: after we save an uploaded video,
async function saveLesson() {
const { data } = await fetchLesson(lessonId);
if (data?.videos?.length === $lesson.materials.videos.length) {
const materials = merge(data, $lesson.materials);
lesson.update((l) => ({
...l,
materials
}));
}
handleUpdateLessonMaterials($lesson, lessonId);
async function saveLesson(materials = undefined) {
const _lesson = !!materials
? {
...$lesson,
materials
}
: $lesson;
handleUpdateLessonMaterials(_lesson, lessonId);
}
function handleSave(prevMode: string) {
@@ -132,12 +129,38 @@
window.players = players;
}
function autoSave(updatedMaterials) {
if (timeoutId) clearTimeout(timeoutId);
if (!initAutoSave) {
initAutoSave = true;
return;
}
isSaving = true;
timeoutId = setTimeout(async () => {
await saveLesson(updatedMaterials);
isSaving = false;
}, 1500);
}
function handleInputChange() {
$isLessonDirty = true;
}
function onLessonIdChange() {
initAutoSave = false;
}
const onClose = () => {
saveLesson();
$uploadCourseVideoStore.isModalOpen = false;
$uploadCourseVideoStore.formRes = null;
};
$: autoSave($lesson.materials);
$: onLessonIdChange(lessonId);
$: handleSave(prevMode);
$: addBadgeValueToTab($lesson.materials);
@@ -153,7 +176,7 @@
width="w-4/5 h-[566px]"
modalHeading="Add a Video"
>
<VideoUploader {lessonId} {saveLesson} />
<VideoUploader {lessonId} />
</Modal>
<Tabs {tabs} {currentTab} {onChange}>
@@ -164,11 +187,11 @@
writeLabel="Note"
bind:value={$lesson.materials.note}
placeholder="Start typing your lesson"
onInputChange={() => ($isLessonDirty = true)}
on:change={handleInputChange}
bind:textareaRef
bind:buttonRef={aiButtonRef}
>
<div slot="buttons">
<div slot="buttons" class="flex items-center">
<PrimaryButton
className="flex items-center relative"
onClick={() => {
@@ -179,7 +202,7 @@
variant={VARIANTS.OUTLINED}
>
<MachineLearningModel size={20} class="carbon-icon mr-3" />
AI Assistant
AI
<Popover
caret
align="left"
@@ -226,7 +249,7 @@
<TextField
label="Slide link"
bind:value={$lesson.materials.slide_url}
onChange={() => ($isLessonDirty = true)}
onInputChange={handleInputChange}
/>
{:else if $lesson.materials.slide_url}
<iframe
@@ -312,11 +335,12 @@
<Box>
<img src="/no-video.svg" alt="Video not found" />
<h3 class="text-xl font-normal dark:text-white py-2">
No youtube video added for this lesson yet
No video added for this lesson yet
</h3>
<p class="text-sm text-center font-normal py-2">
Share your knowledge with the world by creating engaging lessons.<br />
Start by clicking on New Lesson button.
Share videos with your students and get auto transcription and in video search out of
the box.<br />
Start by clicking on Add Video button.
</p>
<PrimaryButton label="Add Video" className="rounded-md" onClick={openAddVideoModal} />
</Box>
@@ -4,8 +4,7 @@ import { createLesson, updateLesson, deleteLesson } from '$lib/utils/services/co
import type { Lesson, Course, LessonPage } from '$lib/utils/types';
export const uploadCourseVideoStore = writable({
isModalOpen: false,
formRes: null
isModalOpen: false
});
export const lessons: Writable<Lesson[]> = writable([]);
@@ -14,6 +13,7 @@ export const lesson = writable<LessonPage>({
id: null,
totalExercises: 0,
is_complete: false,
isSaving: false,
materials: {
note: '',
slide_url: '',
@@ -104,14 +104,11 @@ export async function handleUpdateLessonMaterials(lesson: any, lessonId: Lesson[
}
export const deleteLessonVideo = (index: any) => {
lesson.update((currentLesson) => {
const videos = currentLesson.materials.videos || [];
return {
...currentLesson,
materials: {
...currentLesson.materials,
videos: videos?.filter((video, i) => i !== index)
}
};
});
lesson.update((currentLesson) => ({
...currentLesson,
materials: {
...currentLesson.materials,
videos: currentLesson.materials.videos.filter((video, i) => i !== index)
}
}));
};
+14 -5
View File
@@ -6,7 +6,7 @@
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
export let disableLogin;
export let disableSignup = false;
export let logo;
export let orgName;
export let isOrgSite = false;
@@ -60,12 +60,21 @@
{/if}
{:else}
<li>
{#if !disableLogin}
<div class="flex">
<PrimaryButton
label="Login"
variant={VARIANTS.TEXT}
onClick={() => goto('/login' + redirect)}
/>
</div>
</li>
<li>
{#if !disableSignup}
<div class="flex">
<PrimaryButton
label="Login"
variant={VARIANTS.TEXT}
onClick={() => goto('/login' + redirect)}
label="Signup"
variant={VARIANTS.CONTAINED}
onClick={() => goto('/signup' + redirect)}
/>
</div>
{/if}
@@ -141,7 +141,7 @@
<main>
<!-- Header Section -->
<header id="header" class="banner w-full h-[80vh] mb-10 relative">
<Navigation logo={$currentOrg.avatar_url} orgName={$currentOrg.name} disableLogin={false} />
<Navigation logo={$currentOrg.avatar_url} orgName={$currentOrg.name} disableSignup={true} />
<div class="absolute h-[80vh] top-0 w-full opacity-80 z-10 bg-white" />
{#if video}
+1
View File
@@ -144,6 +144,7 @@ export interface LessonPage {
id?: string | null;
totalExercises: Number;
is_complete: Boolean;
isSaving: Boolean;
materials: {
note: string;
slide_url: string;
+2 -2
View File
@@ -283,7 +283,7 @@
isOrgSite={data.isOrgSite}
logo={data.isOrgSite ? $currentOrg.avatar_url : undefined}
orgName={data.isOrgSite ? $currentOrg.name : undefined}
disableLogin={false}
disableSignup={false}
/>
{/if}
{/if}
@@ -322,7 +322,7 @@
<style>
main {
background: radial-gradient(circle at 10% 20%, rgb(239, 246, 249) 0%, rgb(206, 239, 253) 90%);
background-color: white;
box-sizing: border-box;
}
@@ -5,6 +5,7 @@
import CheckmarkOutlineIcon from 'carbon-icons-svelte/lib/CheckmarkOutline.svelte';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import { VARIANTS } from '$lib/components/PrimaryButton/constants.js';
import { Loading } from 'carbon-components-svelte';
import RoleBasedSecurity from '$lib/components/RoleBasedSecurity/index.svelte';
import PageNav from '$lib/components/PageNav/index.svelte';
import PageBody from '$lib/components/PageBody/index.svelte';
@@ -12,11 +13,12 @@
import Exercises from '$lib/components/Course/components/Lesson/Exercises/index.svelte';
import MODES from '$lib/utils/constants/mode.js';
import { setCourse, course } from '$lib/components/Course/store';
import Download from 'carbon-icons-svelte/lib/Download.svelte';
import {
lesson,
lessons,
handleSaveLesson,
uploadCourseVideoStore
handleSaveLesson
} from '$lib/components/Course/components/Lesson/store/lessons';
import { browser } from '$app/environment';
import { currentOrg } from '$lib/utils/store/org';
@@ -25,8 +27,6 @@
export let data;
export let form;
let path = '';
let mode = MODES.view;
let prevMode = '';
@@ -34,6 +34,7 @@
let isFetching = false;
let isMarkingComplete = false;
let isLoading = false;
let isSaving = false;
function getLessonOrder(id) {
const index = $lessons.findIndex((lesson) => lesson.id === id);
@@ -153,8 +154,6 @@
mode = MODES.view;
fetchReqData(data.lessonId);
}
$: $uploadCourseVideoStore.formRes = form;
</script>
<CourseContainer {isFetching} {path} isExercisePage={!data.isMaterialsTabActive && data.exerciseId}>
@@ -179,17 +178,22 @@
<div class="flex items-center">
<PrimaryButton
className="mr-2"
label="Download PDF"
variant={VARIANTS.OUTLINED}
onClick={downloadLesson}
{isLoading}
/>
<PrimaryButton
className="mr-2"
label={mode === MODES.edit ? 'Save' : 'Edit'}
variant={VARIANTS.OUTLINED}
onClick={toggleMode}
/>
>
<Download size={16} class="mr-2" />
Download PDF
</PrimaryButton>
<PrimaryButton className="mr-2" variant={VARIANTS.OUTLINED} onClick={toggleMode}>
{#if isSaving}
<Loading withOverlay={false} small />
<span class="text-sm ml-2 italic">Autosaving...</span>
{:else}
{mode === MODES.edit ? 'Done' : 'Edit'}
{/if}
</PrimaryButton>
</div>
{/if}
</RoleBasedSecurity>
@@ -200,7 +204,7 @@
<Exercises lessonId={data.lessonId} exerciseId={data.exerciseId} path={`${path}/exercises`} />
{:else if !!data.lessonId}
<PageBody>
<Materials lessonId={data.lessonId} {mode} {prevMode} />
<Materials lessonId={data.lessonId} {mode} {prevMode} bind:isSaving />
<div class="w-full flex flex-row-reverse">
<PrimaryButton