diff --git a/web/assets/init-admin.js b/web/assets/init-admin.js index 518f41ffd..e54a093c8 100644 --- a/web/assets/init-admin.js +++ b/web/assets/init-admin.js @@ -177,28 +177,32 @@ document.addEventListener("alpine:init", () => { * - "csupdate": Custom event triggered when the change set is updated. * The detail property of the event object contains the new value of the specified field. */ - Alpine.directive("change-set-listen", (el, { expression, modifiers }, { evaluate, cleanup }) => { - const [changeSetExpression, fieldName = null] = expression.split("."); - const changeSet = evaluate(changeSetExpression); + Alpine.directive("change-set-listen", (el, { expression, modifiers }, { effect, evaluate, cleanup }) => { + effect(() => { + const [changeSetExpression, fieldName = null] = expression.split("."); + const changeSet = evaluate(changeSetExpression); - if (!changeSet) { - return; - } + const onChangeSetUpdateHandler = (data) => { + console.log('onChangeSetUpdateHandler', el, data); + const value = fieldName != null ? data[fieldName] : data; + if (modifiers.includes("text")) { + el.innerText = `${value}`; + } + el.dispatchEvent(new CustomEvent(nativeEventName, { detail: { changeSet, value } })); + }; - const onChangeSetUpdateHandler = (data) => { - const value = fieldName != null ? data[fieldName] : data; - if (modifiers.includes("text")) { - el.innerText = `${value}`; + if (!changeSet) { + return; } - el.dispatchEvent(new CustomEvent(nativeEventName, { detail: { changeSet, value } })); - }; - - onChangeSetUpdateHandler(changeSet.get()); - changeSet.listen(onChangeSetUpdateHandler); - cleanup(() => { changeSet.removeListener(onChangeSetUpdateHandler); - }) + onChangeSetUpdateHandler(changeSet.get()); + changeSet.listen(onChangeSetUpdateHandler); + + cleanup(() => { + changeSet.removeListener(onChangeSetUpdateHandler); + }) + }); }); /** @@ -225,7 +229,6 @@ document.addEventListener("alpine:init", () => { const onUpdate = evaluateLater(expression); const onChangeSetUpdateHandler = (e) => { - console.log(el, "trigger"); const isDirty = e.detail.changeSet.isDirty(); if (modifiers.includes("clean") && isDirty) { diff --git a/web/ts/api/admin-lecture-list.ts b/web/ts/api/admin-lecture-list.ts index f1df533f6..fc65bc786 100644 --- a/web/ts/api/admin-lecture-list.ts +++ b/web/ts/api/admin-lecture-list.ts @@ -93,27 +93,24 @@ export type VideoSection = { }; export type VideoSectionDelta = { - toAdd: VideoSection[], - toUpdate: VideoSection[], - toDelete: VideoSection[], -} + toAdd: VideoSection[]; + toUpdate: VideoSection[]; + toDelete: VideoSection[]; +}; // Checks if two video sections have the same id but different data export function videoSectionHasChanged(a: VideoSection, b: VideoSection) { - return a.id === b.id && ( - a.description !== b.description || - videoSectionTimestamp(a) !== videoSectionTimestamp(b) - ); + return a.id === b.id && (a.description !== b.description || videoSectionTimestamp(a) !== videoSectionTimestamp(b)); } export function videoSectionGenKey(section: VideoSection): string { if (section.id != null) { return `sid_${section.id}`; } - return `sts_${(new Date()).getTime()}`; + return `sts_${new Date().getTime()}`; } -export function videoSectionListDelta(oldSections: VideoSection[], newSections: VideoSection[]) : VideoSectionDelta { +export function videoSectionListDelta(oldSections: VideoSection[], newSections: VideoSection[]): VideoSectionDelta { const sectionsToAdd = []; const sectionsToUpdate = []; const sectionsToDelete = []; @@ -127,11 +124,13 @@ export function videoSectionListDelta(oldSections: VideoSection[], newSections: // Updating Video Sections const oldVideoSection = oldSections.find((oldSection: VideoSection) => oldSection.id === section.id); - if (videoSectionHasChanged(section, oldVideoSection)) { sectionsToUpdate.push(section); } + if (videoSectionHasChanged(section, oldVideoSection)) { + sectionsToUpdate.push(section); + } } for (const section of oldSections) { // Deleted Sections - if (!newSections.some(({id}) => section.id === id)) { + if (!newSections.some(({ id }) => section.id === id)) { sectionsToDelete.push(section); } } @@ -144,7 +143,9 @@ export function videoSectionListDelta(oldSections: VideoSection[], newSections: } export function videoSectionFriendlyTimestamp(a: VideoSection): string { - return `${a.startHours.toString().padStart(2, "0")}:${a.startMinutes.toString().padStart(2, "0")}:${a.startSeconds.toString().padStart(2, "0")}`; + return `${a.startHours.toString().padStart(2, "0")}:${a.startMinutes.toString().padStart(2, "0")}:${a.startSeconds + .toString() + .padStart(2, "0")}`; } export function videoSectionTimestamp(a: VideoSection): number { @@ -286,10 +287,13 @@ export const AdminLectureList = { * @param sections */ addSections: async (lectureId: number, sections: VideoSection[]): Promise => { - const result = await post(`/api/stream/${lectureId}/sections`, sections.map((s) => ({ - ...s, - streamID: lectureId, - }))); + const result = await post( + `/api/stream/${lectureId}/sections`, + sections.map((s) => ({ + ...s, + streamID: lectureId, + })), + ); return result.json(); }, diff --git a/web/ts/data-store/admin-lecture-list.ts b/web/ts/data-store/admin-lecture-list.ts index ef346ae42..6633bf78e 100644 --- a/web/ts/data-store/admin-lecture-list.ts +++ b/web/ts/data-store/admin-lecture-list.ts @@ -3,10 +3,12 @@ import { AdminLectureList, Lecture, LectureFile, - UpdateLectureMetaRequest, VideoSection, videoSectionSort, + UpdateLectureMetaRequest, + VideoSection, + videoSectionSort, } from "../api/admin-lecture-list"; import { FileType } from "../edit-course"; -import {PostFormDataListener} from "../utilities/fetch-wrappers"; +import { PostFormDataListener } from "../utilities/fetch-wrappers"; const dateFormatOptions: Intl.DateTimeFormatOptions = { weekday: "long", @@ -181,7 +183,7 @@ export class AdminLectureListProvider extends StreamableMapProvider a.id !== videoSection.id), - videoSection, - ].sort(videoSectionSort) + videoSections: [...s.videoSections.filter((a) => a.id !== videoSection.id), videoSection].sort( + videoSectionSort, + ), }; } return s; @@ -214,9 +215,7 @@ export class AdminLectureListProvider extends StreamableMapProvider a.id !== videoSectionId), - ].sort(videoSectionSort) + videoSections: [...s.videoSections.filter((a) => a.id !== videoSectionId)].sort(videoSectionSort), }; } return s; diff --git a/web/ts/edit-course.ts b/web/ts/edit-course.ts index 6dcca49a4..2825f8655 100644 --- a/web/ts/edit-course.ts +++ b/web/ts/edit-course.ts @@ -8,12 +8,16 @@ import { LectureVideoTypeCam, LectureVideoTypeComb, LectureVideoTypePres, - VideoSection, videoSectionFriendlyTimestamp, videoSectionHasChanged, videoSectionListDelta, - videoSectionSort, videoSectionTimestamp, + VideoSection, + videoSectionFriendlyTimestamp, + videoSectionHasChanged, + videoSectionListDelta, + videoSectionSort, + videoSectionTimestamp, } from "./api/admin-lecture-list"; import { ChangeSet, comparatorPipeline, ignoreKeys, singleProperty } from "./change-set"; import { AlpineComponent } from "./components/alpine-component"; -import {uploadFile} from "./utilities/fetch-wrappers"; +import { uploadFile } from "./utilities/fetch-wrappers"; export enum UIEditMode { none, @@ -125,12 +129,15 @@ export function lectureEditor(lecture: Lecture): AlpineComponent { } // List length is the same but items do have no id, so they are new. - if (a.videoSections.some(({ id }) => id === undefined) || b.videoSections.some(({ id }) => id === undefined)) { + if ( + a.videoSections.some(({ id }) => id === undefined) || + b.videoSections.some(({ id }) => id === undefined) + ) { return true; } // A section has edited and different information now - return (a.videoSections.some((sA) => b.videoSections.some((sB) => videoSectionHasChanged(sA, sB)))); + return a.videoSections.some((sA) => b.videoSections.some((sB) => videoSectionHasChanged(sA, sB))); }), ]); @@ -211,7 +218,7 @@ export function lectureEditor(lecture: Lecture): AlpineComponent { DataStore.adminLectureList.deleteAttachment(this.lectureData.courseId, this.lectureData.lectureId, id); }, - friendlySectionTimestamp(section: VideoSection): string{ + friendlySectionTimestamp(section: VideoSection): string { return videoSectionFriendlyTimestamp(section); }, @@ -220,22 +227,30 @@ export function lectureEditor(lecture: Lecture): AlpineComponent { }, genPseudoSectionKey(): string { - return `sts_${(new Date()).getTime()}`; + return `sts_${new Date().getTime()}`; }, addSection(section: VideoSection) { - this.changeSet.patch("videoSections", [...this.lectureData.videoSections, { - ...section, - key: this.genPseudoSectionKey(), - }].sort(videoSectionSort)); + this.changeSet.patch( + "videoSections", + [ + ...this.lectureData.videoSections, + { + ...section, + key: this.genPseudoSectionKey(), + }, + ].sort(videoSectionSort), + ); }, updateSection(section: VideoSection) { const sectionKey = this.getSectionKey(section); - this.changeSet.patch("videoSections", [ - ...this.lectureData.videoSections.filter((s) => sectionKey !== this.getSectionKey(s)), - section - ].sort(videoSectionSort)); + this.changeSet.patch( + "videoSections", + [...this.lectureData.videoSections.filter((s) => sectionKey !== this.getSectionKey(s)), section].sort( + videoSectionSort, + ), + ); }, deleteSection(section) { @@ -255,9 +270,7 @@ export function lectureEditor(lecture: Lecture): AlpineComponent { section.startMinutes < 60 && section.startSeconds !== null && section.startSeconds < 60 && - !this.lectureData.videoSections.some( - (s) => videoSectionTimestamp(s) == videoSectionTimestamp(section) - ) + !this.lectureData.videoSections.some((s) => videoSectionTimestamp(s) == videoSectionTimestamp(section)) ); }, @@ -299,7 +312,8 @@ export function lectureEditor(lecture: Lecture): AlpineComponent { * Save changes send them to backend and commit change set. */ async saveEdit() { - const { courseId, lectureId, name, description, lectureHallId, isChatEnabled, videoSections } = this.lectureData; + const { courseId, lectureId, name, description, lectureHallId, isChatEnabled, videoSections } = + this.lectureData; const changedKeys = this.changeSet.changedKeys(); try {