From 34c7f429e411fb911dfa56a816b638fd665717a4 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 31 May 2019 11:14:38 +0200 Subject: [PATCH] Group videos on chronological order --- .../account-videos.component.ts | 2 +- .../my-account-history.component.ts | 2 +- .../video-channel-videos.component.ts | 2 +- .../app/shared/video/abstract-video-list.html | 19 +++-- .../app/shared/video/abstract-video-list.scss | 12 ++++ .../app/shared/video/abstract-video-list.ts | 70 +++++++++++++++++++ .../video/videos-selection.component.ts | 2 + .../video-list/video-local.component.ts | 2 +- .../video-recently-added.component.ts | 3 +- .../video-list/video-trending.component.ts | 2 +- .../video-user-subscriptions.component.ts | 3 +- shared/core-utils/miscs/date.ts | 49 +++++++++++++ 12 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 shared/core-utils/miscs/date.ts diff --git a/client/src/app/+accounts/account-videos/account-videos.component.ts b/client/src/app/+accounts/account-videos/account-videos.component.ts index 6d26a4322..5a99aadce 100644 --- a/client/src/app/+accounts/account-videos/account-videos.component.ts +++ b/client/src/app/+accounts/account-videos/account-videos.component.ts @@ -29,6 +29,7 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit, private accountSub: Subscription constructor ( + protected i18n: I18n, protected router: Router, protected serverService: ServerService, protected route: ActivatedRoute, @@ -36,7 +37,6 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit, protected notifier: Notifier, protected confirmService: ConfirmService, protected screenService: ScreenService, - private i18n: I18n, private accountService: AccountService, private videoService: VideoService ) { diff --git a/client/src/app/+my-account/my-account-history/my-account-history.component.ts b/client/src/app/+my-account/my-account-history/my-account-history.component.ts index 73340d21a..13607119e 100644 --- a/client/src/app/+my-account/my-account-history/my-account-history.component.ts +++ b/client/src/app/+my-account/my-account-history/my-account-history.component.ts @@ -27,6 +27,7 @@ export class MyAccountHistoryComponent extends AbstractVideoList implements OnIn videosHistoryEnabled: boolean constructor ( + protected i18n: I18n, protected router: Router, protected serverService: ServerService, protected route: ActivatedRoute, @@ -34,7 +35,6 @@ export class MyAccountHistoryComponent extends AbstractVideoList implements OnIn protected userService: UserService, protected notifier: Notifier, protected screenService: ScreenService, - protected i18n: I18n, private confirmService: ConfirmService, private videoService: VideoService, private userHistoryService: UserHistoryService diff --git a/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts b/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts index 5e60b34b4..629fd4450 100644 --- a/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts +++ b/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts @@ -29,6 +29,7 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On private videoChannelSub: Subscription constructor ( + protected i18n: I18n, protected router: Router, protected serverService: ServerService, protected route: ActivatedRoute, @@ -36,7 +37,6 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On protected notifier: Notifier, protected confirmService: ConfirmService, protected screenService: ScreenService, - private i18n: I18n, private videoChannelService: VideoChannelService, private videoService: VideoService ) { diff --git a/client/src/app/shared/video/abstract-video-list.html b/client/src/app/shared/video/abstract-video-list.html index 14f48b54b..11cf1bd92 100644 --- a/client/src/app/shared/video/abstract-video-list.html +++ b/client/src/app/shared/video/abstract-video-list.html @@ -22,11 +22,18 @@ myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" class="videos" > - - + +
+ {{ getCurrentGroupedDateLabel(video) }} +
+ + + + +
diff --git a/client/src/app/shared/video/abstract-video-list.scss b/client/src/app/shared/video/abstract-video-list.scss index d94596113..98b80fdfd 100644 --- a/client/src/app/shared/video/abstract-video-list.scss +++ b/client/src/app/shared/video/abstract-video-list.scss @@ -24,6 +24,18 @@ } } +.date-title { + font-size: 16px; + font-weight: $font-semibold; + margin-bottom: 20px; + margin-top: -10px; + padding-top: 20px; + + &:not(:first-child) { + border-top: 1px solid $separator-border-color; + } +} + .margin-content { @include adapt-margin-content-width; } diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts index fa9d38735..eba05c07d 100644 --- a/client/src/app/shared/video/abstract-video-list.ts +++ b/client/src/app/shared/video/abstract-video-list.ts @@ -11,6 +11,17 @@ import { MiniatureDisplayOptions, OwnerDisplayType } from '@app/shared/video/vid import { Syndication } from '@app/shared/video/syndication.model' import { Notifier, ServerService } from '@app/core' import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' +import { I18n } from '@ngx-translate/i18n-polyfill' +import { isThisMonth, isThisWeek, isToday, isYesterday } from '@shared/core-utils/miscs/date' + +enum GroupDate { + UNKNOWN = 0, + TODAY = 1, + YESTERDAY = 2, + THIS_WEEK = 3, + THIS_MONTH = 4, + OLDER = 5 +} export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableForReuseHook { pagination: ComponentPagination = { @@ -31,6 +42,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor displayModerationBlock = false titleTooltip: string displayVideoActions = true + groupByDate = false disabled = false @@ -50,11 +62,15 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor protected abstract serverService: ServerService protected abstract screenService: ScreenService protected abstract router: Router + protected abstract i18n: I18n abstract titlePage: string private resizeSubscription: Subscription private angularState: number + private groupedDateLabels: { [id in GroupDate]: string } + private groupedDates: { [id: number]: GroupDate } = {} + abstract getVideosObservable (page: number): Observable<{ videos: Video[], totalVideos: number }> abstract generateSyndicationList (): void @@ -64,6 +80,15 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor } ngOnInit () { + this.groupedDateLabels = { + [GroupDate.UNKNOWN]: null, + [GroupDate.TODAY]: this.i18n('Today'), + [GroupDate.YESTERDAY]: this.i18n('Yesterday'), + [GroupDate.THIS_WEEK]: this.i18n('This week'), + [GroupDate.THIS_MONTH]: this.i18n('This month'), + [GroupDate.OLDER]: this.i18n('Older') + } + // Subscribe to route changes const routeParams = this.route.snapshot.queryParams this.loadRouteParams(routeParams) @@ -113,6 +138,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor this.pagination.totalItems = totalVideos this.videos = this.videos.concat(videos) + if (this.groupByDate) this.buildGroupedDateLabels() + this.onMoreVideos() }, @@ -134,6 +161,49 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor this.videos = this.videos.filter(v => v.id !== video.id) } + buildGroupedDateLabels () { + let currentGroupedDate: GroupDate = GroupDate.UNKNOWN + + for (const video of this.videos) { + const publishedDate = video.publishedAt + + if (currentGroupedDate < GroupDate.TODAY && isToday(publishedDate)) { + currentGroupedDate = GroupDate.TODAY + this.groupedDates[ video.id ] = currentGroupedDate + continue + } + + if (currentGroupedDate < GroupDate.YESTERDAY && isYesterday(publishedDate)) { + currentGroupedDate = GroupDate.YESTERDAY + this.groupedDates[ video.id ] = currentGroupedDate + continue + } + + if (currentGroupedDate < GroupDate.THIS_WEEK && isThisWeek(publishedDate)) { + currentGroupedDate = GroupDate.THIS_WEEK + this.groupedDates[ video.id ] = currentGroupedDate + continue + } + + if (currentGroupedDate < GroupDate.THIS_MONTH && isThisMonth(publishedDate)) { + currentGroupedDate = GroupDate.THIS_MONTH + this.groupedDates[ video.id ] = currentGroupedDate + continue + } + + if (currentGroupedDate < GroupDate.OLDER) { + currentGroupedDate = GroupDate.OLDER + this.groupedDates[ video.id ] = currentGroupedDate + } + } + } + + getCurrentGroupedDateLabel (video: Video) { + if (this.groupByDate === false) return undefined + + return this.groupedDateLabels[this.groupedDates[video.id]] + } + // On videos hook for children that want to do something protected onMoreVideos () { /* empty */ } diff --git a/client/src/app/shared/video/videos-selection.component.ts b/client/src/app/shared/video/videos-selection.component.ts index 955ebca9f..d69f7b70e 100644 --- a/client/src/app/shared/video/videos-selection.component.ts +++ b/client/src/app/shared/video/videos-selection.component.ts @@ -20,6 +20,7 @@ import { Video } from '@app/shared/video/video.model' import { PeerTubeTemplateDirective } from '@app/shared/angular/peertube-template.directive' import { VideoSortField } from '@app/shared/video/sort-field.type' import { ComponentPagination } from '@app/shared/rest/component-pagination.model' +import { I18n } from '@ngx-translate/i18n-polyfill' export type SelectionType = { [ id: number ]: boolean } @@ -44,6 +45,7 @@ export class VideosSelectionComponent extends AbstractVideoList implements OnIni globalButtonsTemplate: TemplateRef constructor ( + protected i18n: I18n, protected router: Router, protected route: ActivatedRoute, protected notifier: Notifier, diff --git a/client/src/app/videos/video-list/video-local.component.ts b/client/src/app/videos/video-list/video-local.component.ts index 13d4023c2..65543343c 100644 --- a/client/src/app/videos/video-list/video-local.component.ts +++ b/client/src/app/videos/video-list/video-local.component.ts @@ -22,13 +22,13 @@ export class VideoLocalComponent extends AbstractVideoList implements OnInit, On filter: VideoFilter = 'local' constructor ( + protected i18n: I18n, protected router: Router, protected serverService: ServerService, protected route: ActivatedRoute, protected notifier: Notifier, protected authService: AuthService, protected screenService: ScreenService, - private i18n: I18n, private videoService: VideoService ) { super() diff --git a/client/src/app/videos/video-list/video-recently-added.component.ts b/client/src/app/videos/video-list/video-recently-added.component.ts index 80cef813e..f54bade98 100644 --- a/client/src/app/videos/video-list/video-recently-added.component.ts +++ b/client/src/app/videos/video-list/video-recently-added.component.ts @@ -17,15 +17,16 @@ import { Notifier, ServerService } from '@app/core' export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy { titlePage: string sort: VideoSortField = '-publishedAt' + groupByDate = true constructor ( + protected i18n: I18n, protected route: ActivatedRoute, protected serverService: ServerService, protected router: Router, protected notifier: Notifier, protected authService: AuthService, protected screenService: ScreenService, - private i18n: I18n, private videoService: VideoService ) { super() diff --git a/client/src/app/videos/video-list/video-trending.component.ts b/client/src/app/videos/video-list/video-trending.component.ts index e2ad95bc4..a2c819ebe 100644 --- a/client/src/app/videos/video-list/video-trending.component.ts +++ b/client/src/app/videos/video-list/video-trending.component.ts @@ -19,13 +19,13 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit, defaultSort: VideoSortField = '-trending' constructor ( + protected i18n: I18n, protected router: Router, protected serverService: ServerService, protected route: ActivatedRoute, protected notifier: Notifier, protected authService: AuthService, protected screenService: ScreenService, - private i18n: I18n, private videoService: VideoService ) { super() diff --git a/client/src/app/videos/video-list/video-user-subscriptions.component.ts b/client/src/app/videos/video-list/video-user-subscriptions.component.ts index 2f0685ccc..3caa371d8 100644 --- a/client/src/app/videos/video-list/video-user-subscriptions.component.ts +++ b/client/src/app/videos/video-list/video-user-subscriptions.component.ts @@ -19,15 +19,16 @@ export class VideoUserSubscriptionsComponent extends AbstractVideoList implement titlePage: string sort = '-publishedAt' as VideoSortField ownerDisplayType: OwnerDisplayType = 'auto' + groupByDate = true constructor ( + protected i18n: I18n, protected router: Router, protected serverService: ServerService, protected route: ActivatedRoute, protected notifier: Notifier, protected authService: AuthService, protected screenService: ScreenService, - private i18n: I18n, private videoService: VideoService ) { super() diff --git a/shared/core-utils/miscs/date.ts b/shared/core-utils/miscs/date.ts new file mode 100644 index 000000000..7f0b4443b --- /dev/null +++ b/shared/core-utils/miscs/date.ts @@ -0,0 +1,49 @@ +function isToday (d: Date) { + const today = new Date() + + return areDatesEqual(d, today) +} + +function isYesterday (d: Date) { + const yesterday = new Date() + yesterday.setDate(yesterday.getDate() - 1) + + return areDatesEqual(d, yesterday) +} + +function isThisWeek (d: Date) { + const minDateOfThisWeek = new Date() + minDateOfThisWeek.setHours(0, 0, 0) + + // getDay() -> Sunday - Saturday : 0 - 6 + // We want to start our week on Monday + let dayOfWeek = minDateOfThisWeek.getDay() - 1 + if (dayOfWeek < 0) dayOfWeek = 6 // Sunday + + minDateOfThisWeek.setDate(minDateOfThisWeek.getDate() - dayOfWeek) + + return d >= minDateOfThisWeek +} + +function isThisMonth (d: Date) { + const thisMonth = new Date().getMonth() + + return d.getMonth() === thisMonth +} + +// --------------------------------------------------------------------------- + +export { + isYesterday, + isThisWeek, + isThisMonth, + isToday +} + +// --------------------------------------------------------------------------- + +function areDatesEqual (d1: Date, d2: Date) { + return d1.getFullYear() === d2.getFullYear() && + d1.getMonth() === d2.getMonth() && + d1.getDate() === d2.getDate() +}