Skip to content

Commit

Permalink
New Start Page Updates (#1095)
Browse files Browse the repository at this point in the history
Changes:
- Enlarge logo
- Add visibility checks (Redirect on Unauthorized)
- Resolve #1089 (Add back the eye symbol)
- Resolve #1111 (`py-2` -> `py-px`)
- Resolve #1110 (Use locale setting to determine date format)
- Resolve #1112 (See Screenshot)
- Resolve #1116 (Remove year and term queries of pinned API)
- Resolve #1106 (Add keys to storage)
- Resolve #1108 (Add `isAdmin` to CourseDTO)
- Resolve #1115 (Add `SetSignedPlaylists` before adding streams to DTO)
- Resolve #1109 (Add [this](https://portal.mytum.de/corporatedesign/vorlagen/index_videokonferenzen/hintergruende_videokonferenzen/TUM-Logos-Tapete-hellblau.png) fallback thumbnail to assets and branding configuration)
  • Loading branch information
MatthiasReumann authored Aug 13, 2023
1 parent 2af8604 commit 4b40e4b
Show file tree
Hide file tree
Showing 14 changed files with 118 additions and 54 deletions.
45 changes: 30 additions & 15 deletions api/courses.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,18 +266,6 @@ func (r coursesRoutes) getUsers(c *gin.Context) {
func (r coursesRoutes) getPinned(c *gin.Context) {
tumLiveContext := c.MustGet("TUMLiveContext").(tools.TUMLiveContext)

year, term := tum.GetCurrentSemester()
year, err := strconv.Atoi(c.DefaultQuery("year", strconv.Itoa(year)))
if err != nil {
_ = c.Error(tools.RequestError{
Status: http.StatusBadRequest,
CustomMessage: "invalid year",
Err: err,
})
return
}
term = c.DefaultQuery("term", term)

var pinnedCourses []model.Course
if tumLiveContext.User != nil {
pinnedCourses = tumLiveContext.User.PinnedCourses
Expand All @@ -288,7 +276,7 @@ func (r coursesRoutes) getPinned(c *gin.Context) {
pinnedCourses = commons.Unique(pinnedCourses, func(c model.Course) uint { return c.ID })
resp := make([]model.CourseDTO, 0, len(pinnedCourses))
for _, course := range pinnedCourses {
if !course.IsHidden() && course.Year == year && course.TeachingTerm == term {
if !course.IsHidden() {
resp = append(resp, course.ToDTO())
}
}
Expand All @@ -303,13 +291,16 @@ func sortCourses(courses []model.Course) {
}

func (r coursesRoutes) getCourseBySlug(c *gin.Context) {
tumLiveContext := c.MustGet("TUMLiveContext").(tools.TUMLiveContext)

type URI struct {
Slug string `uri:"slug" binding:"required"`
}

type Query struct {
Year int `form:"year"`
Term string `form:"term"`
Year int `form:"year"`
Term string `form:"term"`
UserID uint `form:"userId"`
}

var uri URI
Expand Down Expand Up @@ -359,14 +350,38 @@ func (r coursesRoutes) getCourseBySlug(c *gin.Context) {
return
}

if (course.IsLoggedIn() && tumLiveContext.User == nil) || (course.IsEnrolled() && !tumLiveContext.User.IsEligibleToWatchCourse(course)) {
c.AbortWithStatus(http.StatusUnauthorized)
}

streams := course.Streams
streamsDTO := make([]model.StreamDTO, len(streams))
for i, s := range streams {
err := tools.SetSignedPlaylists(&s, &model.User{
Model: gorm.Model{ID: query.UserID},
}, course.DownloadsEnabled)
if err != nil {
_ = c.Error(tools.RequestError{
Err: err,
Status: http.StatusInternalServerError,
CustomMessage: "can't sign stream",
})
return
}
streamsDTO[i] = s.ToDTO()
}

isAdmin := course.UserID == query.UserID
for _, user := range course.Admins {
if user.ID == query.UserID {
isAdmin = true
break
}
}

courseDTO := course.ToDTO()
courseDTO.Streams = streamsDTO
courseDTO.IsAdmin = isAdmin

c.JSON(http.StatusOK, courseDTO)
}
Expand Down
10 changes: 2 additions & 8 deletions api/courses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,12 +319,6 @@ func TestCoursesCRUD(t *testing.T) {
url := "/api/courses/users/pinned"

gomino.TestCases{
"invalid year": {
Router: CourseRouterWrapper,
Url: fmt.Sprintf("%s?year=XX&term=S", url),
Middlewares: testutils.GetMiddlewares(tools.ErrorHandler, testutils.TUMLiveContext(testutils.TUMLiveContextStudent)),
ExpectedCode: http.StatusBadRequest,
},
"not logged-in": {
Router: CourseRouterWrapper,
Middlewares: testutils.GetMiddlewares(tools.ErrorHandler, testutils.TUMLiveContext(testutils.TUMLiveContextUserNil)),
Expand All @@ -339,7 +333,7 @@ func TestCoursesCRUD(t *testing.T) {
},
}.
Method(http.MethodGet).
Url(fmt.Sprintf("%s?year=2022&term=W", url)).
Url(url).
Run(t, testutils.Equal)
})

Expand Down Expand Up @@ -415,7 +409,7 @@ func TestCoursesCRUD(t *testing.T) {
}
configGinCourseRouter(r, wrapper)
},
Middlewares: testutils.GetMiddlewares(tools.ErrorHandler, testutils.TUMLiveContext(testutils.TUMLiveContextStudent)),
Middlewares: testutils.GetMiddlewares(tools.ErrorHandler, testutils.TUMLiveContext(testutils.TUMLiveContextAdmin)),
ExpectedCode: http.StatusOK,
ExpectedResponse: response,
},
Expand Down
Binary file added branding/thumb-fallback.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion dao/courses.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ func (d coursesDao) GetCourseBySlugYearAndTerm(ctx context.Context, slug string,
return db.Order("unit_start desc")
}).Preload("Streams", func(db *gorm.DB) *gorm.DB {
return db.Order("start desc")
}).Where("teaching_term = ? AND slug = ? AND year = ?", term, slug, year).First(&course).Error
}).Preload("Admins").Where("teaching_term = ? AND slug = ? AND year = ?", term, slug, year).First(&course).Error
if err == nil {
Cache.SetWithTTL(fmt.Sprintf("courseBySlugYearAndTerm%v%v%v", slug, term, year), course, 1, time.Minute)
}
Expand Down
12 changes: 12 additions & 0 deletions model/course.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type CourseDTO struct {
NextLecture StreamDTO
LastRecording StreamDTO
Streams []StreamDTO
IsAdmin bool // Set in API handler
}

func (c *Course) ToDTO() CourseDTO {
Expand All @@ -66,6 +67,7 @@ func (c *Course) ToDTO() CourseDTO {
DownloadsEnabled: c.DownloadsEnabled,
NextLecture: c.GetNextLecture().ToDTO(),
LastRecording: c.GetLastRecording().ToDTO(),
IsAdmin: false,
}
}

Expand Down Expand Up @@ -315,6 +317,16 @@ func (c Course) IsHidden() bool {
return c.Visibility == "hidden"
}

// IsLoggedIn returns true if visibility is set to 'loggedin' and false if not
func (c Course) IsLoggedIn() bool {
return c.Visibility == "loggedin"
}

// IsEnrolled returns true if visibility is set to 'enrolled' and false if not
func (c Course) IsEnrolled() bool {
return c.Visibility == "enrolled"
}

// AdminJson is the JSON representation of a courses streams for the admin panel
func (c Course) AdminJson(lhs []LectureHall) []gin.H {
var res []gin.H
Expand Down
2 changes: 1 addition & 1 deletion model/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,5 +329,5 @@ func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
if len(u.Name) == 0 {
return ErrUsernameNoText
}
return nil;
return nil
}
8 changes: 8 additions & 0 deletions web/assets/css/home.css
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@
@apply bg-blue-500 dark:bg-indigo-600
}

.tum-live-border-primary {
@apply border-blue-500 dark:border-indigo-600
}

.tum-live-font-primary {
@apply text-blue-500 dark:text-indigo-600
}

/* Elements */
.tum-live-button {
@apply rounded-full px-3 py-1 text-sm font-semibold
Expand Down
Binary file added web/assets/img/thumb-fallback.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions web/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ func getDefaultStaticBrandingFiles() []staticFile {
{Name: "favicon.ico", Path: "assets/favicon.ico"},
{Name: "icons-192.png", Path: "assets/img/icons-192.png"},
{Name: "icons-512.png", Path: "assets/img/icons-512.png"},
{Name: "thumb-fallback.png", Path: "assets/img/thumb-fallback.png"},
}
}

Expand Down
40 changes: 24 additions & 16 deletions web/template/home.gohtml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{{- /*gotype: github.com/joschahenningsen/TUM-Live/web.IndexData*/ -}}
{{$user := .TUMLiveContext.User}}
{{$userID := 0}}
{{if $user}}{{$userID = $user.ID}}{{end}}
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
Expand Down Expand Up @@ -47,7 +49,7 @@
<i class="fa-solid fa-bars" :class="{'rotate-90': navigation.value}"></i>
</button>
<button class="mx-3" type="button" id="logo" title="Start" @click="showMain()">
<img src="/logo.svg" width="42" alt="TUM-Live Logo">
<img src="/logo.svg" width="64" alt="TUM-Live Logo">
</button>
</div>

Expand All @@ -67,8 +69,8 @@
{{else}}
<div>
<button type="button" @click="userContext.toggle(true)"
class="tum-live-button-primary mx-3 flex items-center justify-center rounded-full h-[32px] w-[32px]">
<span class="text-white font-semibold uppercase">{{printf "%.1s" $user.GetPreferredName}}</span>
class="border-2 tum-live-border-primary mx-3 flex items-center justify-center rounded-full h-[32px] w-[32px]">
<i class="fa-solid fa-user tum-live-font-primary"></i>
</button>
<div class="relative">
<article x-cloak x-show="userContext.value"
Expand Down Expand Up @@ -173,7 +175,7 @@
<template x-for="course in pinnedCourses" :key="course.ID">
<a class="tum-live-side-navigation-group-item hover"
:href="course.URL()"
@click.prevent="showCourse(course.Slug)"
@click.prevent="showCourse(course.Slug, course.Year, course.TeachingTerm)"
x-text="course.Name">
</a>
</template>
Expand Down Expand Up @@ -304,7 +306,7 @@
</article>
</template>
<template x-if="state.isCourse()">
<article x-data="home.courseContext(state.slug, state.year, state.term)"
<article x-data="home.courseContext(state.slug, state.year, state.term, {{$userID}})"
x-init="$watch('state', (s) => reload(s.slug, s.year, s.term))"
class="tum-live-course-view">
<header>
Expand All @@ -331,14 +333,14 @@
.ics
</a>
{{if $user}}
{{if or (eq $user.Role 1) (eq $user.Role 2) }}
<template x-if="course.IsAdmin || {{$user.Role}} === 1">
<a :href="`/admin/course/${course.ID}`"
class="tum-live-button tum-live-button-tertiary flex items-center text-xs"
title="Go to admin page">
<i class="fa-solid fa-hammer mr-2"></i>
Admin
</a>
{{end}}
</template>
{{end}}
</section>
</header>
Expand Down Expand Up @@ -367,7 +369,7 @@
</span>
</div>
<a :href="`/w/${livestream.Course.Slug}/${livestream.Stream.ID}`">
<div :style="`background-image:url('/api/stream/${livestream.Stream.ID}/thumbs/live')`"
<div :style="`background:url('/api/stream/${livestream.Stream.ID}/thumbs/live'), url('/thumb-fallback.png')`"
class="h-full tum-live-thumbnail"></div>
</a>
</div>
Expand Down Expand Up @@ -437,7 +439,7 @@
<article class="tum-live-stream group sm:col-span-1 col-span-full"
@click.outside="vod.Dropdown.toggle(false)">
<a :href="course.WatchURL(vod.ID)" class="block mb-2">
<div :style="`background-image:url('/api/stream/${vod.ID}/thumbs/vod')`"
<div :style="`background: url('/api/stream/${vod.ID}/thumbs/vod'), url('/thumb-fallback.png')`"
class="aspect-video tum-live-thumbnail">
<div :id="`vod-progress-${vod.ID}`"
class="tum-live-thumbnail-progress">
Expand All @@ -448,8 +450,14 @@
</template>
</div>
</div>
<div class="tum-live-badge absolute bg-black/[.8] text-white bottom-4 right-2 z-40 text-xs">
<span x-text="vod.DurationString()"></span>
<div class="flex space-x-1 absolute text-white bottom-4 right-2 z-40 text-xs">
<div x-cloak x-show="vod.Progress?.watched"
class="bg-black/[.8] rounded-full px-2 py-1 text-white">
<i class="fa-solid fa-eye mr-0"></i>
</div>
<div class="tum-live-badge bg-black/[.8]">
<span x-text="vod.DurationString()"></span>
</div>
</div>
</div>
</a>
Expand All @@ -459,15 +467,15 @@
<a class="title" :href="course.WatchURL(vod.ID)"
x-text="vod.Name"></a>
</template>
<span class="date text-xs"
<span class="date text-sm"
x-text="vod.FriendlyDateStart()"></span>
</div>
<button type="button" @click="vod.Dropdown.toggle()"
class="md:group-hover:block md:hidden px-2">
<i class="fa-solid fa-ellipsis-vertical"></i>
</button>
<template x-if="vod.Dropdown.value">
<div class="absolute tum-live-menu py-2 absolute bottom-full right-0 h-fit overflow-hidden z-50 w-56">
<div class="absolute tum-live-menu py-px bottom-full right-0 h-fit overflow-hidden z-50 w-56">
{{if $user}}
<button type="button"
@click="vod.Progress.ToggleWatched()"
Expand Down Expand Up @@ -620,7 +628,7 @@
</template>
</div>
<a :href="`/w/${livestream.Course.Slug}/${livestream.Stream.ID}`">
<div :style="`background-image:url('/api/stream/${livestream.Stream.ID}/thumbs/live')`"
<div :style="`background:url('/api/stream/${livestream.Stream.ID}/thumbs/live'), url('/thumb-fallback.png')`"
class="h-full tum-live-thumbnail"></div>
</a>
<div class="tum-live-badge absolute bg-black/[.8] text-white bottom-2 right-2 z-40 text-xs">
Expand Down Expand Up @@ -698,7 +706,7 @@
<template x-for="course in recently.get()" :key="course.ID">
<article class="tum-live-stream sm:col-span-1 col-span-full p-3">
<a :href="course.LastRecordingURL()" class="block mb-2">
<div :style="`background-image:url('/api/stream/${course.LastRecording.ID}/thumbs/vod')`"
<div :style="`background:url('/api/stream/${course.LastRecording.ID}/thumbs/vod'), url('/thumb-fallback.png')`"
class="aspect-video tum-live-thumbnail">
<div :id="`vod-progress-${course.LastRecording.ID}`"
class="tum-live-thumbnail-progress">
Expand Down Expand Up @@ -734,7 +742,7 @@
</template>
</article>
</template>
</div>
</span>
</article>
</template>
</article>
Expand Down
15 changes: 12 additions & 3 deletions web/ts/api/courses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,14 @@ export class Stream {
}

public FriendlyDateStart(): string {
return new Date(this.Start).toLocaleString();
return new Date(this.Start).toLocaleString("default", {
weekday: "long",
year: "numeric",
month: "numeric",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
});
}

public MonthOfStart(): string {
Expand Down Expand Up @@ -114,6 +121,8 @@ export class Course {

readonly Pinned: boolean = false;

readonly IsAdmin: boolean;

private readonly Streams?: Stream[];

readonly Recordings?: Stream[];
Expand Down Expand Up @@ -211,8 +220,8 @@ export const CoursesAPI = {
return get(url.toString()).then((courses) => courses.map((c) => Course.New(c)));
},

async get(slug: string, year?: number, term?: string) {
const url = new CustomURL(`/api/courses/${slug}`, { year, term });
async get(slug: string, year?: number, term?: string, userId?: number) {
const url = new CustomURL(`/api/courses/${slug}`, { year, term, userId });
return get(url.toString(), {}, true).then((course) => Course.New(course));
},
};
Loading

0 comments on commit 4b40e4b

Please sign in to comment.