Skip to content

Commit

Permalink
Improvement/refactor course sections (TUM-Dev#1156)
Browse files Browse the repository at this point in the history
* started wit hrefactor of course management

* add dao & route

* update route

* started wit hfetching

* Add changeset abstraction

* cleanup file

* remove get all streams again, as there is AdminJson method

* Its rendering

* further dev :S

* added some directive

* little fixes

* ...

* ... no idea...

* right path i guess

* changeset working except video sections

* fix discard files

* save from laptop

* Add changeset doku

* make private works like charm

* changing works pretty well

* add video upload

* add series update

* ported transcoding

* add readme and lecture hall select

* more description

* simplified lecture hall set

* linter

* more fixes

* :)

* add attachments

* :)

* add video sections to admin streams

* main functionality

* using fetch wrtappers

* moved uploadFile and postFormData

* fix linteer

* Impl. Feedback

* add video sections to admin streams

* main functionality

* using fetch wrtappers

* moved uploadFile and postFormData

* version workers with tag (TUM-Dev#1157)

* fix linteer

* fix delete lecture

* reenable page reload on create

* some linter fixes

* fix merge complications

* fix changeset; adding onchange listener

* fixed adding sections

* added directives

* fix typo

* improved directives

* design improvement

* fixed creating and deleting

* add sections fixed

* cleanup

* :)

* fix alpine js error issues

* remoev console log

* nested :)

* remove console log

* lint-fix

* fix admin.LectureList initialisation in settings tab

---------

Co-authored-by: Joscha Henningsen <44805696+joschahenningsen@users.noreply.github.com>
Co-authored-by: Joscha Henningsen <joscha.henningsen@tum.de>
  • Loading branch information
3 people committed Sep 30, 2023
1 parent ee92bd6 commit bad8f2c
Show file tree
Hide file tree
Showing 15 changed files with 543 additions and 322 deletions.
6 changes: 4 additions & 2 deletions api/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import (
"strings"
"time"

"github.com/getsentry/sentry-go"
"github.com/gin-gonic/gin"
"github.com/TUM-Dev/gocast/dao"
"github.com/TUM-Dev/gocast/model"
"github.com/TUM-Dev/gocast/tools"
"github.com/TUM-Dev/gocast/tools/bot"
"github.com/TUM-Dev/gocast/voice-service/pb"
"github.com/getsentry/sentry-go"
"github.com/gin-gonic/gin"
uuid "github.com/satori/go.uuid"
log "github.com/sirupsen/logrus"
"gorm.io/gorm"
Expand Down Expand Up @@ -496,6 +496,8 @@ func (r streamRoutes) createVideoSectionBatch(c *gin.Context) {
log.WithError(err).Error("failed to generate video section images")
}
}()

c.JSON(http.StatusOK, sections)
}

type UpdateVideoSectionRequest struct {
Expand Down
3 changes: 2 additions & 1 deletion dao/courses.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ func (d coursesDao) GetCourseByToken(token string) (course model.Course, err err
func (d coursesDao) GetCourseById(ctx context.Context, id uint) (course model.Course, err error) {
var foundCourse model.Course
dbErr := DB.Preload("Streams.TranscodingProgresses").
Preload("Streams.VideoSections").
Preload("Streams.Files").
Preload("Streams", func(db *gorm.DB) *gorm.DB {
return db.Order("streams.start desc")
Expand All @@ -218,7 +219,7 @@ func (d coursesDao) GetCourseBySlugYearAndTerm(ctx context.Context, slug string,
return cachedCourses.(model.Course), nil
}
var course model.Course
err := DB.Preload("Streams.Units", func(db *gorm.DB) *gorm.DB {
err := DB.Preload("Streams.VideoSections").Preload("Streams.Units", func(db *gorm.DB) *gorm.DB {
return db.Order("unit_start desc")
}).Preload("Streams", func(db *gorm.DB) *gorm.DB {
return db.Order("start desc")
Expand Down
13 changes: 13 additions & 0 deletions model/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,18 @@ func (s Stream) getJson(lhs []LectureHall, course Course) gin.H {
}
}

var videoSections []gin.H
for _, section := range s.VideoSections {
videoSections = append(videoSections, gin.H{
"id": section.ID,
"description": section.Description,
"startHours": section.StartHours,
"startMinutes": section.StartMinutes,
"startSeconds": section.StartSeconds,
"fileID": section.FileID,
})
}

return gin.H{
"lectureId": s.Model.ID,
"courseId": s.CourseID,
Expand All @@ -346,6 +358,7 @@ func (s Stream) getJson(lhs []LectureHall, course Course) gin.H {
"courseSlug": course.Slug,
"private": s.Private,
"downloadableVods": s.GetVodFiles(),
"videoSections": videoSections,
}
}

Expand Down
106 changes: 100 additions & 6 deletions web/assets/init-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ document.addEventListener("alpine:init", () => {
'color'
];

const nativeEventName = "csupdate";

const convert = (modifiers, value) => {
if (modifiers.includes("int")) {
return parseInt(value);
Expand Down Expand Up @@ -79,7 +81,6 @@ document.addEventListener("alpine:init", () => {
Alpine.directive("bind-change-set", (el, { expression, value, modifiers }, { evaluate, cleanup }) => {
const changeSet = evaluate(expression);
const fieldName = value || el.name;
const nativeEventName = "csupdate";

if (el.type === "file") {
const isSingle = modifiers.includes("single")
Expand All @@ -92,7 +93,7 @@ document.addEventListener("alpine:init", () => {
if (!data[fieldName]) {
el.value = "";
}
el.dispatchEvent(new CustomEvent(nativeEventName, { detail: data[fieldName] }));
el.dispatchEvent(new CustomEvent(nativeEventName, { detail: { changeSet, value: data[fieldName] } }));
};

changeSet.listen(onChangeSetUpdateHandler);
Expand All @@ -109,7 +110,7 @@ document.addEventListener("alpine:init", () => {

const onChangeSetUpdateHandler = (data) => {
el.checked = !!data[fieldName];
el.dispatchEvent(new CustomEvent(nativeEventName, { detail: !!data[fieldName] }));
el.dispatchEvent(new CustomEvent(nativeEventName, { detail: { changeSet, value: !!data[fieldName] }}));
};

changeSet.listen(onChangeSetUpdateHandler);
Expand All @@ -122,26 +123,29 @@ document.addEventListener("alpine:init", () => {
})
} else if (el.tagName === "textarea" || textInputTypes.includes(el.type)) {
const keyupHandler = (e) => changeSet.patch(fieldName, convert(modifiers, e.target.value));
const changeHandler = (e) => changeSet.patch(fieldName, convert(modifiers, e.target.value));

const onChangeSetUpdateHandler = (data) => {
el.value = `${data[fieldName]}`;
el.dispatchEvent(new CustomEvent(nativeEventName, { detail: data[fieldName] }));
el.dispatchEvent(new CustomEvent(nativeEventName, { detail: { changeSet, value: data[fieldName] } }));
};

changeSet.listen(onChangeSetUpdateHandler);
el.addEventListener('keyup', keyupHandler)
el.addEventListener('keyup', keyupHandler);
el.addEventListener('change', changeHandler);
el.value = `${changeSet.get()[fieldName]}`;

cleanup(() => {
changeSet.removeListener(onChangeSetUpdateHandler);
el.removeEventListener('keyup', keyupHandler)
el.removeEventListener('change', changeHandler)
})
} else {
const changeHandler = (e) => changeSet.patch(fieldName, convert(modifiers, e.target.value));

const onChangeSetUpdateHandler = (data) => {
el.value = `${data[fieldName]}`;
el.dispatchEvent(new CustomEvent(nativeEventName, { detail: data[fieldName] }));
el.dispatchEvent(new CustomEvent(nativeEventName, { detail: { changeSet, value: data[fieldName] } }));
};

changeSet.listen(onChangeSetUpdateHandler);
Expand All @@ -154,4 +158,94 @@ document.addEventListener("alpine:init", () => {
})
}
});

/**
* Alpine.js directive for dynamically triggering a custom event and updating an element's inner text
* based on changes to a "change set" object's field.
*
* Syntax:
* <div x-change-set-listen.text="changeSetExpression.fieldName"></div>
*
* Parameters:
* - changeSetExpression: The JavaScript expression evaluating to the change set object
* - fieldName: The specific field within the change set to monitor for changes
*
* Modifiers:
* - "text": When provided, the directive will also update the element's innerText.
*
* Custom Events:
* - "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 }, { effect, evaluate, cleanup }) => {
effect(() => {
const [changeSetExpression, fieldName = null] = expression.split(".");
const changeSet = evaluate(changeSetExpression);

const onChangeSetUpdateHandler = (data) => {
const value = fieldName != null ? data[fieldName] : data;
if (modifiers.includes("text")) {
el.innerText = `${value}`;
}
el.dispatchEvent(new CustomEvent(nativeEventName, { detail: { changeSet, value } }));
};

if (!changeSet) {
return;
}

changeSet.removeListener(onChangeSetUpdateHandler);
onChangeSetUpdateHandler(changeSet.get());
changeSet.listen(onChangeSetUpdateHandler);

cleanup(() => {
changeSet.removeListener(onChangeSetUpdateHandler);
})
});
});

/**
* Alpine.js directive for executing custom logic in response to the "csupdate" event,
* which is usually triggered by changes in a "change set" object's field.
*
* Syntax:
* <div x-on-change-set-update="[expression]"></div>
*
* Parameters:
* - expression: The JavaScript expression to be evaluated when the "csupdate" event is triggered.
*
* Modifiers:
* - "init": When provided, the directive will execute the expression during initialization (no matter if its dirty or clean).
* - "clean": When provided, the directive will only execute if changeSet is not dirty.
* - "dirty": When provided, the directive will only execute if changeSet is dirty.
*
* Example usage:
* <div x-change-set-listen="sectionEditChangeSet"
* x-on-change-set-update.init="$el.innerText = friendlySectionTimestamp(sectionEditChangeSet.get())">
* </div>
*/
Alpine.directive("on-change-set-update", (el, { expression, modifiers }, { evaluate, evaluateLater, cleanup }) => {
const onUpdate = evaluateLater(expression);

const onChangeSetUpdateHandler = (e) => {
const isDirty = e.detail.changeSet.isDirty();

if (modifiers.includes("clean") && isDirty) {
return;
}
if (modifiers.includes("dirty") && !isDirty) {
return;
}
onUpdate();
};
el.addEventListener(nativeEventName, onChangeSetUpdateHandler);

if (modifiers.includes("init")) {
evaluate(expression);
}

cleanup(() => {
el.removeEventListener(nativeEventName, onChangeSetUpdateHandler);
})
})
});
2 changes: 1 addition & 1 deletion web/template/partial/course/manage/course_settings.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
}">
<form class="form-container-body" id="course-settings-form" method="post" @submit.prevent="
async () => {
if(admin.LectureList.hasIndividualChatEnabledSettings()){
if((new admin.LectureList({{.ID}})).hasIndividualChatEnabledSettings()){
showCourseSettingsModal = true;
}
else{
Expand Down
Loading

0 comments on commit bad8f2c

Please sign in to comment.