Group videos on chronological order
authorChocobozzz <me@florianbigard.com>
Fri, 31 May 2019 09:14:38 +0000 (11:14 +0200)
committerChocobozzz <me@florianbigard.com>
Fri, 31 May 2019 09:15:50 +0000 (11:15 +0200)
12 files changed:
client/src/app/+accounts/account-videos/account-videos.component.ts
client/src/app/+my-account/my-account-history/my-account-history.component.ts
client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts
client/src/app/shared/video/abstract-video-list.html
client/src/app/shared/video/abstract-video-list.scss
client/src/app/shared/video/abstract-video-list.ts
client/src/app/shared/video/videos-selection.component.ts
client/src/app/videos/video-list/video-local.component.ts
client/src/app/videos/video-list/video-recently-added.component.ts
client/src/app/videos/video-list/video-trending.component.ts
client/src/app/videos/video-list/video-user-subscriptions.component.ts
shared/core-utils/miscs/date.ts [new file with mode: 0644]

index 6d26a4322684f175c04d0834145a65267dd2fb84..5a99aadcee5c876e5a8e268dc5ad1434f3ede06e 100644 (file)
@@ -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
   ) {
index 73340d21aee1133b5a72804fc71f7203c46e7a09..13607119e147f7c429697c4f10a54a6099aae6e2 100644 (file)
@@ -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
index 5e60b34b4249c64146e52e23753aa089352cbc3f..629fd4450b9762714b4df3bd423c725f1cf032e9 100644 (file)
@@ -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
   ) {
index 14f48b54bc43ea6456f5cd27a528afe53f08175b..11cf1bd9230a3a8cc0d3d14c6a4f1d3c57ee7891 100644 (file)
     myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true"
     class="videos"
   >
-    <my-video-miniature
-      *ngFor="let video of videos; trackBy: videoById" [video]="video" [user]="user" [ownerDisplayType]="ownerDisplayType"
-      [displayVideoActions]="displayVideoActions" [displayOptions]="displayOptions"
-      (videoBlacklisted)="removeVideoFromArray(video)" (videoRemoved)="removeVideoFromArray(video)"
-    >
-    </my-video-miniature>
+    <ng-container *ngFor="let video of videos; trackBy: videoById;">
+      <div class="date-title" *ngIf="getCurrentGroupedDateLabel(video)">
+        {{ getCurrentGroupedDateLabel(video) }}
+      </div>
+
+
+      <my-video-miniature
+        [video]="video" [user]="user" [ownerDisplayType]="ownerDisplayType"
+        [displayVideoActions]="displayVideoActions" [displayOptions]="displayOptions"
+        (videoBlacklisted)="removeVideoFromArray(video)" (videoRemoved)="removeVideoFromArray(video)"
+      >
+      </my-video-miniature>
+    </ng-container>
   </div>
 </div>
index d94596113a025b3046b441c7e10ab6e66c22d0ee..98b80fdfd10d1ac9897a63fb4552af5df1c028ab 100644 (file)
   }
 }
 
+.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;
 }
index fa9d38735831cca4cd5ea7d795ef1143cc103be8..eba05c07d90278b3a32a18e1ed737fa18277376b 100644 (file)
@@ -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 */ }
 
index 955ebca9fcdf8765bb36c658e05484704e00fb59..d69f7b70e3208c977417a458fa27a59bf4d75c69 100644 (file)
@@ -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<any>
 
   constructor (
+    protected i18n: I18n,
     protected router: Router,
     protected route: ActivatedRoute,
     protected notifier: Notifier,
index 13d4023c27a3b3595b7673ac59cc26375531d384..65543343ca8264e333bd846bd3ada95e6b034975 100644 (file)
@@ -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()
index 80cef813ef6e49548bbb0bef0ca46ee8877be972..f54bade98711fa27462c8213e8463cea1fedb7e1 100644 (file)
@@ -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()
index e2ad95bc4f31098f2b6f15c48253224104acd17a..a2c819ebe7819d976485cecdfaa84085289e20d5 100644 (file)
@@ -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()
index 2f0685ccce12f6a7634034aa0fd87d039254c80a..3caa371d8886562d5ee67609c3bf0a60b1d849d1 100644 (file)
@@ -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 (file)
index 0000000..7f0b444
--- /dev/null
@@ -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()
+}