aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+videos
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/+videos')
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html4
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts13
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.scss4
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts13
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts15
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts15
-rw-r--r--client/src/app/+videos/+video-edit/video-add.component.scss4
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment-add.component.html15
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss114
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment-add.component.ts7
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment.component.html12
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment.component.scss244
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment.component.ts4
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comments.component.html15
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comments.component.scss4
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comments.component.ts1
-rw-r--r--client/src/app/+videos/+video-watch/modal/video-support.component.html15
-rw-r--r--client/src/app/+videos/+video-watch/modal/video-support.component.scss3
-rw-r--r--client/src/app/+videos/+video-watch/modal/video-support.component.ts31
-rw-r--r--client/src/app/+videos/+video-watch/player-styles.component.scss4
-rw-r--r--client/src/app/+videos/+video-watch/player-styles.component.ts15
-rw-r--r--client/src/app/+videos/+video-watch/recommendations/recent-videos-recommendation.service.ts2
-rw-r--r--client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html4
-rw-r--r--client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss33
-rw-r--r--client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.ts2
-rw-r--r--client/src/app/+videos/+video-watch/video-avatar-channel.component.html21
-rw-r--r--client/src/app/+videos/+video-watch/video-avatar-channel.component.scss44
-rw-r--r--client/src/app/+videos/+video-watch/video-avatar-channel.component.ts27
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.html13
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.scss64
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.ts76
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.module.ts16
-rw-r--r--client/src/app/+videos/video-list/overview/video-overview.component.html6
-rw-r--r--client/src/app/+videos/video-list/overview/video-overview.component.scss79
-rw-r--r--client/src/app/+videos/video-list/video-user-subscriptions.component.ts3
35 files changed, 522 insertions, 420 deletions
diff --git a/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html b/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html
index 6a07dafa7..092952204 100644
--- a/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html
+++ b/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.html
@@ -34,12 +34,12 @@
34 34
35 <div class="modal-footer inputs"> 35 <div class="modal-footer inputs">
36 <input 36 <input
37 type="button" role="button" i18n-value value="Cancel" class="action-button action-button-cancel" 37 type="button" role="button" i18n-value value="Cancel" class="peertube-button grey-button"
38 (click)="hide()" (key.enter)="hide()" 38 (click)="hide()" (key.enter)="hide()"
39 > 39 >
40 40
41 <input 41 <input
42 type="submit" i18n-value value="Add this caption" class="action-button-submit" 42 type="submit" i18n-value value="Add this caption" class="peertube-button orange-button"
43 [disabled]="!form.valid" (click)="addCaption()" 43 [disabled]="!form.valid" (click)="addCaption()"
44 > 44 >
45 </div> 45 </div>
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
index 8780ca567..8e035b6bb 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
@@ -1,8 +1,8 @@
1 1
2import { forkJoin } from 'rxjs' 2import { forkJoin } from 'rxjs'
3import { Component, EventEmitter, OnInit, Output } from '@angular/core' 3import { AfterViewChecked, AfterViewInit, Component, EventEmitter, OnInit, Output } from '@angular/core'
4import { Router } from '@angular/router' 4import { Router } from '@angular/router'
5import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core' 5import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService } from '@app/core'
6import { scrollToTop } from '@app/helpers' 6import { scrollToTop } from '@app/helpers'
7import { FormValidatorService } from '@app/shared/shared-forms' 7import { FormValidatorService } from '@app/shared/shared-forms'
8import { VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' 8import { VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
@@ -19,7 +19,7 @@ import { VideoSend } from './video-send'
19 './video-send.scss' 19 './video-send.scss'
20 ] 20 ]
21}) 21})
22export class VideoGoLiveComponent extends VideoSend implements OnInit, CanComponentDeactivate { 22export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterViewInit, CanComponentDeactivate {
23 @Output() firstStepDone = new EventEmitter<string>() 23 @Output() firstStepDone = new EventEmitter<string>()
24 @Output() firstStepError = new EventEmitter<void>() 24 @Output() firstStepError = new EventEmitter<void>()
25 25
@@ -41,7 +41,8 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, CanCompon
41 protected videoService: VideoService, 41 protected videoService: VideoService,
42 protected videoCaptionService: VideoCaptionService, 42 protected videoCaptionService: VideoCaptionService,
43 private liveVideoService: LiveVideoService, 43 private liveVideoService: LiveVideoService,
44 private router: Router 44 private router: Router,
45 private hooks: HooksService
45 ) { 46 ) {
46 super() 47 super()
47 } 48 }
@@ -50,6 +51,10 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, CanCompon
50 super.ngOnInit() 51 super.ngOnInit()
51 } 52 }
52 53
54 ngAfterViewInit () {
55 this.hooks.runAction('action:go-live.init', 'video-edit')
56 }
57
53 canDeactivate () { 58 canDeactivate () {
54 return { canDeactivate: true } 59 return { canDeactivate: true }
55 } 60 }
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.scss b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.scss
index 1fef74994..dd87641fc 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.scss
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.scss
@@ -3,8 +3,8 @@
3 3
4.first-step-block { 4.first-step-block {
5 .torrent-or-magnet { 5 .torrent-or-magnet {
6 @include divider($color: pvar(--inputPlaceholderColor), $background: pvar(--submenuColor)); 6 @include divider($color: pvar(--inputPlaceholderColor), $background: pvar(--submenuBackgroundColor));
7 7
8 &[data-content] { 8 &[data-content] {
9 margin: 1.5rem 0; 9 margin: 1.5rem 0;
10 } 10 }
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
index 01087e525..3aae24732 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
@@ -1,6 +1,6 @@
1import { Component, ElementRef, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' 1import { AfterViewInit, Component, ElementRef, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
2import { Router } from '@angular/router' 2import { Router } from '@angular/router'
3import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core' 3import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService } from '@app/core'
4import { scrollToTop } from '@app/helpers' 4import { scrollToTop } from '@app/helpers'
5import { FormValidatorService } from '@app/shared/shared-forms' 5import { FormValidatorService } from '@app/shared/shared-forms'
6import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' 6import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main'
@@ -18,7 +18,7 @@ import { VideoSend } from './video-send'
18 './video-send.scss' 18 './video-send.scss'
19 ] 19 ]
20}) 20})
21export class VideoImportTorrentComponent extends VideoSend implements OnInit, CanComponentDeactivate { 21export class VideoImportTorrentComponent extends VideoSend implements OnInit, AfterViewInit, CanComponentDeactivate {
22 @Output() firstStepDone = new EventEmitter<string>() 22 @Output() firstStepDone = new EventEmitter<string>()
23 @Output() firstStepError = new EventEmitter<void>() 23 @Output() firstStepError = new EventEmitter<void>()
24 @ViewChild('torrentfileInput') torrentfileInput: ElementRef<HTMLInputElement> 24 @ViewChild('torrentfileInput') torrentfileInput: ElementRef<HTMLInputElement>
@@ -43,7 +43,8 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca
43 protected videoService: VideoService, 43 protected videoService: VideoService,
44 protected videoCaptionService: VideoCaptionService, 44 protected videoCaptionService: VideoCaptionService,
45 private router: Router, 45 private router: Router,
46 private videoImportService: VideoImportService 46 private videoImportService: VideoImportService,
47 private hooks: HooksService
47 ) { 48 ) {
48 super() 49 super()
49 } 50 }
@@ -52,6 +53,10 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca
52 super.ngOnInit() 53 super.ngOnInit()
53 } 54 }
54 55
56 ngAfterViewInit () {
57 this.hooks.runAction('action:video-torrent-import.init', 'video-edit')
58 }
59
55 canDeactivate () { 60 canDeactivate () {
56 return { canDeactivate: true } 61 return { canDeactivate: true }
57 } 62 }
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
index c447c179d..7a9fe369f 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
@@ -1,7 +1,7 @@
1import { map, switchMap } from 'rxjs/operators' 1import { map, switchMap } from 'rxjs/operators'
2import { Component, EventEmitter, OnInit, Output } from '@angular/core' 2import { AfterViewInit, Component, EventEmitter, OnInit, Output } from '@angular/core'
3import { Router } from '@angular/router' 3import { Router } from '@angular/router'
4import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core' 4import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService } from '@app/core'
5import { getAbsoluteAPIUrl, scrollToTop } from '@app/helpers' 5import { getAbsoluteAPIUrl, scrollToTop } from '@app/helpers'
6import { FormValidatorService } from '@app/shared/shared-forms' 6import { FormValidatorService } from '@app/shared/shared-forms'
7import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' 7import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main'
@@ -18,7 +18,7 @@ import { VideoSend } from './video-send'
18 './video-send.scss' 18 './video-send.scss'
19 ] 19 ]
20}) 20})
21export class VideoImportUrlComponent extends VideoSend implements OnInit, CanComponentDeactivate { 21export class VideoImportUrlComponent extends VideoSend implements OnInit, AfterViewInit, CanComponentDeactivate {
22 @Output() firstStepDone = new EventEmitter<string>() 22 @Output() firstStepDone = new EventEmitter<string>()
23 @Output() firstStepError = new EventEmitter<void>() 23 @Output() firstStepError = new EventEmitter<void>()
24 24
@@ -42,8 +42,9 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom
42 protected videoService: VideoService, 42 protected videoService: VideoService,
43 protected videoCaptionService: VideoCaptionService, 43 protected videoCaptionService: VideoCaptionService,
44 private router: Router, 44 private router: Router,
45 private videoImportService: VideoImportService 45 private videoImportService: VideoImportService,
46 ) { 46 private hooks: HooksService
47 ) {
47 super() 48 super()
48 } 49 }
49 50
@@ -51,6 +52,10 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom
51 super.ngOnInit() 52 super.ngOnInit()
52 } 53 }
53 54
55 ngAfterViewInit () {
56 this.hooks.runAction('action:video-url-import.init', 'video-edit')
57 }
58
54 canDeactivate () { 59 canDeactivate () {
55 return { canDeactivate: true } 60 return { canDeactivate: true }
56 } 61 }
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
index ca21b61cd..effb37077 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
@@ -1,15 +1,15 @@
1import { Subscription } from 'rxjs' 1import { Subscription } from 'rxjs'
2import { HttpErrorResponse, HttpEventType, HttpResponse } from '@angular/common/http' 2import { HttpErrorResponse, HttpEventType, HttpResponse } from '@angular/common/http'
3import { Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core' 3import { AfterViewInit, Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'
4import { Router } from '@angular/router' 4import { Router } from '@angular/router'
5import { AuthService, CanComponentDeactivate, Notifier, ServerService, UserService } from '@app/core' 5import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService, UserService } from '@app/core'
6import { scrollToTop, uploadErrorHandler } from '@app/helpers' 6import { scrollToTop, uploadErrorHandler } from '@app/helpers'
7import { FormValidatorService } from '@app/shared/shared-forms' 7import { FormValidatorService } from '@app/shared/shared-forms'
8import { BytesPipe, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' 8import { BytesPipe, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
9import { LoadingBarService } from '@ngx-loading-bar/core' 9import { LoadingBarService } from '@ngx-loading-bar/core'
10import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
10import { VideoPrivacy } from '@shared/models' 11import { VideoPrivacy } from '@shared/models'
11import { VideoSend } from './video-send' 12import { VideoSend } from './video-send'
12import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
13 13
14@Component({ 14@Component({
15 selector: 'my-video-upload', 15 selector: 'my-video-upload',
@@ -20,7 +20,7 @@ import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
20 './video-send.scss' 20 './video-send.scss'
21 ] 21 ]
22}) 22})
23export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy, CanComponentDeactivate { 23export class VideoUploadComponent extends VideoSend implements OnInit, AfterViewInit, OnDestroy, CanComponentDeactivate {
24 @Output() firstStepDone = new EventEmitter<string>() 24 @Output() firstStepDone = new EventEmitter<string>()
25 @Output() firstStepError = new EventEmitter<void>() 25 @Output() firstStepError = new EventEmitter<void>()
26 @ViewChild('videofileInput') videofileInput: ElementRef<HTMLInputElement> 26 @ViewChild('videofileInput') videofileInput: ElementRef<HTMLInputElement>
@@ -60,7 +60,8 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
60 protected videoService: VideoService, 60 protected videoService: VideoService,
61 protected videoCaptionService: VideoCaptionService, 61 protected videoCaptionService: VideoCaptionService,
62 private userService: UserService, 62 private userService: UserService,
63 private router: Router 63 private router: Router,
64 private hooks: HooksService
64 ) { 65 ) {
65 super() 66 super()
66 } 67 }
@@ -79,6 +80,10 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
79 }) 80 })
80 } 81 }
81 82
83 ngAfterViewInit () {
84 this.hooks.runAction('action:video-upload.init', 'video-edit')
85 }
86
82 ngOnDestroy () { 87 ngOnDestroy () {
83 if (this.videoUploadObservable) this.videoUploadObservable.unsubscribe() 88 if (this.videoUploadObservable) this.videoUploadObservable.unsubscribe()
84 } 89 }
diff --git a/client/src/app/+videos/+video-edit/video-add.component.scss b/client/src/app/+videos/+video-edit/video-add.component.scss
index 5db9e823d..1ebee946b 100644
--- a/client/src/app/+videos/+video-edit/video-add.component.scss
+++ b/client/src/app/+videos/+video-edit/video-add.component.scss
@@ -67,7 +67,7 @@ $nav-link-height: 40px;
67 &.active { 67 &.active {
68 border-color: $border-color; 68 border-color: $border-color;
69 border-bottom-color: transparent; 69 border-bottom-color: transparent;
70 background-color: pvar(--submenuColor) !important; 70 background-color: pvar(--submenuBackgroundColor) !important;
71 71
72 span { 72 span {
73 border-bottom-color: pvar(--mainColor); 73 border-bottom-color: pvar(--mainColor);
@@ -84,7 +84,7 @@ $nav-link-height: 40px;
84 border: $border-width $border-type $border-color; 84 border: $border-width $border-type $border-color;
85 border-top: transparent; 85 border-top: transparent;
86 86
87 background-color: pvar(--submenuColor); 87 background-color: pvar(--submenuBackgroundColor);
88 border-bottom-left-radius: 3px; 88 border-bottom-left-radius: 3px;
89 border-bottom-right-radius: 3px; 89 border-bottom-right-radius: 3px;
90 width: 100%; 90 width: 100%;
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html
index fdefed09a..7bd9b7c90 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html
+++ b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html
@@ -1,6 +1,6 @@
1<form novalidate [formGroup]="form" (ngSubmit)="formValidated()"> 1<form novalidate [formGroup]="form" (ngSubmit)="formValidated()">
2 <div class="avatar-and-textarea"> 2 <div class="avatar-and-textarea">
3 <img [src]="getAvatarUrl()" alt="Avatar" /> 3 <my-account-avatar [account]="user?.account" size="25"></my-account-avatar>
4 4
5 <div class="form-group"> 5 <div class="form-group">
6 <textarea i18n-placeholder placeholder="Add comment..." myAutoResize 6 <textarea i18n-placeholder placeholder="Add comment..." myAutoResize
@@ -8,8 +8,8 @@
8 (click)="openVisitorModal($event)" 8 (click)="openVisitorModal($event)"
9 formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }" 9 formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }"
10 (keyup.control.enter)="onValidKey()" (keyup.meta.enter)="onValidKey()" #textarea> 10 (keyup.control.enter)="onValidKey()" (keyup.meta.enter)="onValidKey()" #textarea>
11
12 </textarea> 11 </textarea>
12
13 <my-help class="markdown-guide" helpType="custom" iconName="markdown" tooltipPlacement="left auto" autoClose="true" i18n-title title="Markdown compatible"> 13 <my-help class="markdown-guide" helpType="custom" iconName="markdown" tooltipPlacement="left auto" autoClose="true" i18n-title title="Markdown compatible">
14 <ng-template ptTemplate="customHtml"> 14 <ng-template ptTemplate="customHtml">
15 <span i18n>Markdown compatible that supports:</span> 15 <span i18n>Markdown compatible that supports:</span>
@@ -41,10 +41,11 @@
41 </div> 41 </div>
42 42
43 <div class="comment-buttons"> 43 <div class="comment-buttons">
44 <button *ngIf="isAddButtonDisplayed()" class="cancel-button" (click)="cancelCommentReply()" type="button" i18n> 44 <button *ngIf="isAddButtonDisplayed()" class="peertube-button tertiary-button cancel-button" (click)="cancelCommentReply()" type="button" i18n>
45 Cancel 45 Cancel
46 </button> 46 </button>
47 <button *ngIf="isAddButtonDisplayed()" [ngClass]="{ disabled: !form.valid || addingComment }"> 47
48 <button *ngIf="isAddButtonDisplayed()" class="peertube-button orange-button" [ngClass]="{ disabled: !form.valid || addingComment }">
48 {{ addingCommentButtonValue }} 49 {{ addingCommentButtonValue }}
49 </button> 50 </button>
50 </div> 51 </div>
@@ -55,6 +56,7 @@
55 <h4 class="modal-title" id="modal-basic-title" i18n>You are one step away from commenting</h4> 56 <h4 class="modal-title" id="modal-basic-title" i18n>You are one step away from commenting</h4>
56 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hideModals()"></my-global-icon> 57 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hideModals()"></my-global-icon>
57 </div> 58 </div>
59
58 <div class="modal-body"> 60 <div class="modal-body">
59 <span i18n> 61 <span i18n>
60 You can comment using an account on any ActivityPub-compatible instance (PeerTube/Mastodon/Pleroma account for example). 62 You can comment using an account on any ActivityPub-compatible instance (PeerTube/Mastodon/Pleroma account for example).
@@ -62,14 +64,15 @@
62 64
63 <my-remote-subscribe [interact]="true" [uri]="getUri()"></my-remote-subscribe> 65 <my-remote-subscribe [interact]="true" [uri]="getUri()"></my-remote-subscribe>
64 </div> 66 </div>
67
65 <div class="modal-footer inputs"> 68 <div class="modal-footer inputs">
66 <input 69 <input
67 type="button" role="button" i18n-value value="Cancel" class="action-button action-button-cancel" 70 type="button" role="button" i18n-value value="Cancel" class="peertube-button grey-button"
68 (click)="hideModals()" (key.enter)="hideModals()" 71 (click)="hideModals()" (key.enter)="hideModals()"
69 > 72 >
70 73
71 <input 74 <input
72 type="submit" i18n-value value="Login to comment" class="action-button-submit" 75 type="submit" i18n-value value="Login to comment" class="peertube-button orange-button"
73 (click)="gotoLogin()" 76 (click)="gotoLogin()"
74 > 77 >
75 </div> 78 </div>
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss
index d938e2e28..1aa9255c2 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss
+++ b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss
@@ -1,6 +1,10 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4$markdown-icon-height: 18px;
5$markdown-icon-width: 30px;
6$peertube-textarea-height: 60px;
7
4form { 8form {
5 margin-bottom: 30px; 9 margin-bottom: 30px;
6} 10}
@@ -9,9 +13,7 @@ form {
9 display: flex; 13 display: flex;
10 margin-bottom: 10px; 14 margin-bottom: 10px;
11 15
12 img { 16 my-account-avatar {
13 @include avatar(25px);
14
15 vertical-align: top; 17 vertical-align: top;
16 margin-right: 10px; 18 margin-right: 10px;
17 } 19 }
@@ -20,83 +22,55 @@ form {
20 flex-grow: 1; 22 flex-grow: 1;
21 margin: 0; 23 margin: 0;
22 position: relative; 24 position: relative;
25 }
23 26
24 $peertube-textarea-height: 60px; 27 textarea {
25 $markdown-icon-height: 18px; 28 @include peertube-textarea(100%, $peertube-textarea-height);
26 $markdown-icon-width: 30px; 29 @include button-focus(pvar(--mainColorLightest));
27
28 .markdown-guide {
29 position: absolute;
30 top: 5px;
31 right: 9px;
32
33 ::ng-deep .help-tooltip-button {
34 my-global-icon {
35 height: $markdown-icon-height;
36 width: $markdown-icon-width;
37
38 svg {
39 color: #C6C6C6;
40 fill: #C6C6C6;
41 border-radius: 3px;
42 }
43 }
44
45 &:focus, &:active, &:hover {
46 my-global-icon svg {
47 background-color: #C6C6C6;
48 color: pvar(--mainBackgroundColor);
49 fill: pvar(--mainBackgroundColor);
50 }
51 }
52 }
53 }
54
55 textarea {
56 @include peertube-textarea(100%, $peertube-textarea-height);
57 @include button-focus(pvar(--mainColorLightest));
58 30
59 min-height: calc(#{$peertube-textarea-height} - 15px * 2); 31 min-height: calc(#{$peertube-textarea-height} - 15px * 2);
60 padding-right: $markdown-icon-width + 15px !important; 32 padding-right: $markdown-icon-width + 15px !important;
61 33
62 @media screen and (max-width: 600px) { 34 @media screen and (max-width: 600px) {
63 padding-right: $markdown-icon-width + 19px !important; 35 padding-right: $markdown-icon-width + 19px !important;
64 } 36 }
65 37
66 &:focus::placeholder { 38 &:focus::placeholder {
67 opacity: 0; 39 opacity: 0;
68 }
69 } 40 }
70 } 41 }
71} 42}
72 43
73.comment-buttons { 44.markdown-guide {
74 display: flex; 45 position: absolute;
75 justify-content: flex-end; 46 top: 5px;
47 right: 9px;
76 48
77 button { 49 ::ng-deep .help-tooltip-button {
78 @include peertube-button; 50 my-global-icon {
79 @include disable-outline; 51 height: $markdown-icon-height;
80 @include disable-default-a-behaviour; 52 width: $markdown-icon-width;
81 53
82 &:not(:last-child) { 54 svg {
83 margin-right: .5rem; 55 color: #C6C6C6;
56 fill: #C6C6C6;
57 border-radius: 3px;
58 }
84 } 59 }
85 60
86 &:last-child { 61 &:focus, &:active, &:hover {
87 @include orange-button; 62 my-global-icon svg {
63 background-color: #C6C6C6;
64 color: pvar(--mainBackgroundColor);
65 fill: pvar(--mainBackgroundColor);
66 }
88 } 67 }
89 } 68 }
69}
90 70
91 .cancel-button { 71.comment-buttons {
92 @include tertiary-button; 72 display: flex;
93 73 justify-content: flex-end;
94 font-weight: $font-semibold;
95 display: inline-block;
96 padding: 0 10px 0 10px;
97 white-space: nowrap;
98 background: transparent;
99 }
100} 74}
101 75
102.emoji-flex { 76.emoji-flex {
@@ -119,7 +93,8 @@ form {
119} 93}
120 94
121@media screen and (max-width: 600px) { 95@media screen and (max-width: 600px) {
122 textarea, .comment-buttons button { 96 textarea,
97 .comment-buttons button {
123 font-size: 14px !important; 98 font-size: 14px !important;
124 } 99 }
125 100
@@ -129,12 +104,7 @@ form {
129} 104}
130 105
131.modal-body { 106.modal-body {
132 .btn { 107 > span {
133 @include peertube-button;
134 @include orange-button;
135 }
136
137 span {
138 float: left; 108 float: left;
139 margin-bottom: 20px; 109 margin-bottom: 20px;
140 } 110 }
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.ts b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.ts
index f1f0dfeba..0e1362ad3 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.ts
+++ b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.ts
@@ -4,7 +4,7 @@ import { Router } from '@angular/router'
4import { Notifier, User } from '@app/core' 4import { Notifier, User } from '@app/core'
5import { VIDEO_COMMENT_TEXT_VALIDATOR } from '@app/shared/form-validators/video-comment-validators' 5import { VIDEO_COMMENT_TEXT_VALIDATOR } from '@app/shared/form-validators/video-comment-validators'
6import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 6import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
7import { Video, Account } from '@app/shared/shared-main' 7import { Video } from '@app/shared/shared-main'
8import { VideoComment, VideoCommentService } from '@app/shared/shared-video-comment' 8import { VideoComment, VideoCommentService } from '@app/shared/shared-video-comment'
9import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 9import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
10import { VideoCommentCreate } from '@shared/models' 10import { VideoCommentCreate } from '@shared/models'
@@ -143,11 +143,6 @@ export class VideoCommentAddComponent extends FormReactive implements OnChanges,
143 return window.location.href 143 return window.location.href
144 } 144 }
145 145
146 getAvatarUrl () {
147 if (this.user) return this.user.accountAvatarUrl
148 return Account.GET_DEFAULT_AVATAR_URL()
149 }
150
151 gotoLogin () { 146 gotoLogin () {
152 this.hideModals() 147 this.hideModals()
153 this.router.navigate([ '/login' ]) 148 this.router.navigate([ '/login' ])
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.html b/client/src/app/+videos/+video-watch/comment/video-comment.component.html
index ba41b6f48..4592c9c69 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment.component.html
+++ b/client/src/app/+videos/+video-watch/comment/video-comment.component.html
@@ -1,14 +1,6 @@
1<div *ngIf="isCommentDisplayed()" class="root-comment"> 1<div *ngIf="isCommentDisplayed()" class="root-comment">
2 <div class="left"> 2 <div class="left">
3 <a *ngIf="!comment.isDeleted" [href]="comment.account.url" target="_blank" rel="noopener noreferrer"> 3 <my-account-avatar *ngIf="!comment.isDeleted" [href]="comment.account.url" [account]="comment.account"></my-account-avatar>
4 <img
5 class="comment-avatar"
6 [src]="comment.accountAvatarUrl"
7 (error)="switchToDefaultAvatar($event)"
8 alt="Avatar"
9 />
10 </a>
11
12 <div class="vertical-border"></div> 4 <div class="vertical-border"></div>
13 </div> 5 </div>
14 6
@@ -46,7 +38,7 @@
46 <div *ngIf="isUserLoggedIn()" tabindex=0 (click)="onWantToReply()" class="comment-action-reply" i18n>Reply</div> 38 <div *ngIf="isUserLoggedIn()" tabindex=0 (click)="onWantToReply()" class="comment-action-reply" i18n>Reply</div>
47 39
48 <my-user-moderation-dropdown 40 <my-user-moderation-dropdown
49 [prependActions]="prependModerationActions" tabindex=0 41 [prependActions]="prependModerationActions" tabindex=0 [buttonStyled]="false"
50 buttonSize="small" [account]="commentAccount" [user]="commentUser" i18n-label label="Options" placement="bottom-left auto" 42 buttonSize="small" [account]="commentAccount" [user]="commentUser" i18n-label label="Options" placement="bottom-left auto"
51 ></my-user-moderation-dropdown> 43 ></my-user-moderation-dropdown>
52 </div> 44 </div>
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.scss b/client/src/app/+videos/+video-watch/comment/video-comment.component.scss
index f6ff376b9..cf33a5b0e 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment.component.scss
+++ b/client/src/app/+videos/+video-watch/comment/video-comment.component.scss
@@ -22,144 +22,140 @@
22 .right { 22 .right {
23 width: 100%; 23 width: 100%;
24 } 24 }
25}
25 26
26 .comment-avatar { 27.comment {
27 @include avatar(36px); 28 flex-grow: 1;
28 } 29 // Fix word-wrap with flex
29 30 min-width: 1px;
30 .comment { 31}
31 flex-grow: 1;
32 // Fix word-wrap with flex
33 min-width: 1px;
34
35 .highlighted-comment {
36 display: inline-block;
37 background-color: #F5F5F5;
38 color: #3d3d3d;
39 padding: 0 5px;
40 font-size: 13px;
41 margin-bottom: 5px;
42 font-weight: $font-semibold;
43 border-radius: 3px;
44 }
45 32
46 .comment-account-date { 33.highlighted-comment {
47 display: flex; 34 display: inline-block;
48 margin-bottom: 4px; 35 background-color: #F5F5F5;
49 36 color: #3d3d3d;
50 .video-author { 37 padding: 0 5px;
51 height: 20px; 38 font-size: 13px;
52 background-color: #888888; 39 margin-bottom: 5px;
53 border-radius: 12px; 40 font-weight: $font-semibold;
54 margin-bottom: 2px; 41 border-radius: 3px;
55 max-width: 100%; 42}
56 box-sizing: border-box;
57 flex-direction: row;
58 align-items: center;
59 display: inline-flex;
60 padding-right: 6px;
61 padding-left: 6px;
62 color: white !important;
63 }
64 43
65 .comment-account { 44.comment-account-date {
66 word-break: break-all; 45 display: flex;
67 font-weight: 600; 46 margin-bottom: 4px;
68 font-size: 90%; 47}
69 48
70 a { 49.video-author {
71 @include disable-default-a-behaviour; 50 height: 20px;
51 background-color: #888888;
52 border-radius: 12px;
53 margin-bottom: 2px;
54 max-width: 100%;
55 box-sizing: border-box;
56 flex-direction: row;
57 align-items: center;
58 display: inline-flex;
59 padding-right: 6px;
60 padding-left: 6px;
61 color: white !important;
62}
72 63
73 color: pvar(--mainForegroundColor); 64.comment-account {
65 word-break: break-all;
66 font-weight: 600;
67 font-size: 90%;
74 68
75 &:hover { 69 a {
76 text-decoration: underline; 70 @include disable-default-a-behaviour;
77 }
78 }
79 71
80 .comment-account-fid { 72 color: pvar(--mainForegroundColor);
81 opacity: .6;
82 }
83 }
84 73
85 .comment-date { 74 &:hover {
86 font-size: 90%; 75 text-decoration: underline;
87 color: pvar(--greyForegroundColor);
88 margin-left: 5px;
89 text-decoration: none;
90
91 &:hover {
92 text-decoration: underline;
93 }
94 }
95 } 76 }
77 }
96 78
97 .comment-html { 79 .comment-account-fid {
98 @include peertube-word-wrap; 80 opacity: .6;
81 }
82}
99 83
100 // Mentions 84.comment-date {
101 ::ng-deep a { 85 font-size: 90%;
86 color: pvar(--greyForegroundColor);
87 margin-left: 5px;
88 text-decoration: none;
102 89
103 &:not(.linkified-url) { 90 &:hover {
104 @include disable-default-a-behaviour; 91 text-decoration: underline;
92 }
93}
105 94
106 color: pvar(--mainForegroundColor); 95.comment-html {
96 @include peertube-word-wrap;
107 97
108 font-weight: $font-semibold; 98 // Mentions
109 } 99 ::ng-deep a {
110 100
111 } 101 &:not(.linkified-url) {
102 @include disable-default-a-behaviour;
112 103
113 // Paragraphs 104 color: pvar(--mainForegroundColor);
114 ::ng-deep p {
115 margin-bottom: .3rem;
116 }
117 105
118 &.comment-html-deleted { 106 font-weight: $font-semibold;
119 color: pvar(--greyForegroundColor);
120 margin-bottom: 1rem;
121 }
122 } 107 }
123 108
124 .comment-actions { 109 }
125 margin-bottom: 10px; 110
126 display: flex; 111 // Paragraphs
112 ::ng-deep p {
113 margin-bottom: .3rem;
114 }
127 115
128 ::ng-deep .dropdown-toggle, 116 &.comment-html-deleted {
129 .comment-action-reply { 117 color: pvar(--greyForegroundColor);
130 color: pvar(--greyForegroundColor); 118 margin-bottom: 1rem;
131 cursor: pointer; 119 }
132 margin-right: 10px; 120}
133 121
134 &:hover, &:active, &:focus, &:focus-visible { 122.comment-actions {
135 color: pvar(--mainForegroundColor); 123 margin-bottom: 10px;
136 } 124 display: flex;
137 }
138 125
139 ::ng-deep .action-button { 126 ::ng-deep .dropdown-toggle,
140 background-color: transparent; 127 .comment-action-reply {
141 padding: 0; 128 color: pvar(--greyForegroundColor);
142 font-weight: unset; 129 cursor: pointer;
143 } 130 margin-right: 10px;
144 }
145 131
146 my-video-comment-add { 132 &:hover, &:active, &:focus, &:focus-visible {
147 ::ng-deep form { 133 color: pvar(--mainForegroundColor);
148 margin-top: 1rem;
149 margin-bottom: 0;
150 }
151 } 134 }
152 } 135 }
153 136
154 .children { 137 ::ng-deep .action-button {
155 // Reduce avatars size for replies 138 background-color: transparent;
156 .comment-avatar { 139 padding: 0;
157 @include avatar(25px); 140 font-weight: unset;
158 } 141 }
142}
159 143
160 .left { 144my-video-comment-add {
161 margin-right: 6px; 145 ::ng-deep form {
162 } 146 margin-top: 1rem;
147 margin-bottom: 0;
148 }
149}
150
151.children {
152 // Reduce avatars size for replies
153 .comment-avatar {
154 @include avatar(25px);
155 }
156
157 .left {
158 margin-right: 6px;
163 } 159 }
164} 160}
165 161
@@ -170,27 +166,23 @@
170} 166}
171 167
172@media screen and (max-width: 600px) { 168@media screen and (max-width: 600px) {
173 .root-comment { 169 .children {
174 .children { 170 margin-left: -20px;
175 margin-left: -20px;
176 171
177 .left { 172 .left {
178 align-items: flex-start; 173 align-items: flex-start;
179 174
180 .vertical-border { 175 .vertical-border {
181 margin-left: 2px; 176 margin-left: 2px;
182 }
183 } 177 }
184 } 178 }
179 }
185 180
186 .comment { 181 .comment-account-date {
187 .comment-account-date { 182 flex-direction: column;
188 flex-direction: column;
189 183
190 .comment-date { 184 .comment-date {
191 margin-left: 0; 185 margin-left: 0;
192 }
193 }
194 } 186 }
195 } 187 }
196} 188}
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.ts b/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
index 5c5d72b22..dd3db0c65 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
+++ b/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
@@ -131,10 +131,6 @@ export class VideoCommentComponent implements OnInit, OnChanges {
131 ) 131 )
132 } 132 }
133 133
134 switchToDefaultAvatar ($event: Event) {
135 ($event.target as HTMLImageElement).src = Account.GET_DEFAULT_AVATAR_URL()
136 }
137
138 isCommentDisplayed () { 134 isCommentDisplayed () {
139 // Not deleted 135 // Not deleted
140 return !this.comment.isDeleted || 136 return !this.comment.isDeleted ||
diff --git a/client/src/app/+videos/+video-watch/comment/video-comments.component.html b/client/src/app/+videos/+video-watch/comment/video-comments.component.html
index 4a6426d30..9e6fde2e0 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comments.component.html
+++ b/client/src/app/+videos/+video-watch/comment/video-comments.component.html
@@ -1,12 +1,7 @@
1<div> 1<div>
2 <div class="title-block"> 2 <div class="title-block">
3 <h2 class="title-page title-page-single"> 3 <h2 class="title-page title-page-single">
4 <ng-container *ngIf="totalNotDeletedComments > 0; then hasComments; else noComments"></ng-container> 4 {totalNotDeletedComments, plural, =0 {Comments} =1 {1 Comment} other {{{totalNotDeletedComments}} Comments}}
5 <ng-template #hasComments>
6 <ng-container i18n *ngIf="totalNotDeletedComments === 1; else manyComments">1 Comment</ng-container>
7 <ng-template i18n #manyComments>{{ totalNotDeletedComments }} Comments</ng-template>
8 </ng-template>
9 <ng-template i18n #noComments>Comments</ng-template>
10 </h2> 5 </h2>
11 6
12 <my-feed [syndicationItems]="syndicationItems"></my-feed> 7 <my-feed [syndicationItems]="syndicationItems"></my-feed>
@@ -79,15 +74,17 @@
79 <span class="glyphicon glyphicon-menu-down"></span> 74 <span class="glyphicon glyphicon-menu-down"></span>
80 75
81 <ng-container *ngIf="comment.totalRepliesFromVideoAuthor > 0; then hasAuthorComments; else noAuthorComments"></ng-container> 76 <ng-container *ngIf="comment.totalRepliesFromVideoAuthor > 0; then hasAuthorComments; else noAuthorComments"></ng-container>
77
82 <ng-template #hasAuthorComments> 78 <ng-template #hasAuthorComments>
83 <ng-container *ngIf="comment.totalReplies !== comment.totalRepliesFromVideoAuthor; else onlyAuthorComments" i18n> 79 <ng-container *ngIf="comment.totalReplies !== comment.totalRepliesFromVideoAuthor; else onlyAuthorComments" i18n>
84 View {{ comment.totalReplies }} replies from {{ video?.account?.displayName || 'the author' }} and others 80 View {comment.totalReplies, plural, =1 {1 reply} other {{{ comment.totalReplies }} replies}} from {{ video?.account?.displayName || 'the author' }} and others
85 </ng-container> 81 </ng-container>
86 <ng-template i18n #onlyAuthorComments> 82 <ng-template i18n #onlyAuthorComments>
87 View {{ comment.totalReplies }} replies from {{ video?.account?.displayName || 'the author' }} 83 View {comment.totalReplies, plural, =1 {1 reply} other {{{ comment.totalReplies }} replies}} from {{ video?.account?.displayName || 'the author' }}
88 </ng-template> 84 </ng-template>
89 </ng-template> 85 </ng-template>
90 <ng-template i18n #noAuthorComments>View {{ comment.totalReplies }} replies</ng-template> 86
87 <ng-template i18n #noAuthorComments>View {comment.totalReplies, plural, =1 {1 reply} other {{{ comment.totalReplies }} replies}}</ng-template>
91 88
92 <my-small-loader class="comment-thread-loading ml-1" [loading]="threadLoading[comment.id]"></my-small-loader> 89 <my-small-loader class="comment-thread-loading ml-1" [loading]="threadLoading[comment.id]"></my-small-loader>
93 </div> 90 </div>
diff --git a/client/src/app/+videos/+video-watch/comment/video-comments.component.scss b/client/src/app/+videos/+video-watch/comment/video-comments.component.scss
index df42fae73..e6778e1a9 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comments.component.scss
+++ b/client/src/app/+videos/+video-watch/comment/video-comments.component.scss
@@ -27,7 +27,11 @@
27 margin-left: 5px; 27 margin-left: 5px;
28 opacity: 0; 28 opacity: 0;
29 transition: ease-in .2s opacity; 29 transition: ease-in .2s opacity;
30 width: 12px;
31 position: relative;
32 top: -3px;
30 } 33 }
34
31 &:hover my-feed { 35 &:hover my-feed {
32 opacity: 1; 36 opacity: 1;
33 } 37 }
diff --git a/client/src/app/+videos/+video-watch/comment/video-comments.component.ts b/client/src/app/+videos/+video-watch/comment/video-comments.component.ts
index d36dd9e34..210236b61 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comments.component.ts
+++ b/client/src/app/+videos/+video-watch/comment/video-comments.component.ts
@@ -5,7 +5,6 @@ import { AuthService, ComponentPagination, ConfirmService, hasMoreItems, Notifie
5import { HooksService } from '@app/core/plugins/hooks.service' 5import { HooksService } from '@app/core/plugins/hooks.service'
6import { Syndication, VideoDetails } from '@app/shared/shared-main' 6import { Syndication, VideoDetails } from '@app/shared/shared-main'
7import { VideoComment, VideoCommentService, VideoCommentThreadTree } from '@app/shared/shared-video-comment' 7import { VideoComment, VideoCommentService, VideoCommentThreadTree } from '@app/shared/shared-video-comment'
8import { ThisReceiver } from '@angular/compiler'
9 8
10@Component({ 9@Component({
11 selector: 'my-video-comments', 10 selector: 'my-video-comments',
diff --git a/client/src/app/+videos/+video-watch/modal/video-support.component.html b/client/src/app/+videos/+video-watch/modal/video-support.component.html
deleted file mode 100644
index 935656d23..000000000
--- a/client/src/app/+videos/+video-watch/modal/video-support.component.html
+++ /dev/null
@@ -1,15 +0,0 @@
1<ng-template #modal let-hide="close">
2 <div class="modal-header">
3 <h4 i18n class="modal-title">Support {{ video.account.displayName }}</h4>
4 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
5 </div>
6
7 <div class="modal-body" [innerHTML]="videoHTMLSupport"></div>
8
9 <div class="modal-footer inputs">
10 <input
11 type="button" role="button" i18n-value value="Maybe later" class="action-button action-button-cancel"
12 (click)="hide()" (key.enter)="hide()"
13 >
14 </div>
15</ng-template>
diff --git a/client/src/app/+videos/+video-watch/modal/video-support.component.scss b/client/src/app/+videos/+video-watch/modal/video-support.component.scss
deleted file mode 100644
index 184e09027..000000000
--- a/client/src/app/+videos/+video-watch/modal/video-support.component.scss
+++ /dev/null
@@ -1,3 +0,0 @@
1.action-button-cancel {
2 margin-right: 0 !important;
3}
diff --git a/client/src/app/+videos/+video-watch/modal/video-support.component.ts b/client/src/app/+videos/+video-watch/modal/video-support.component.ts
deleted file mode 100644
index bd5290a72..000000000
--- a/client/src/app/+videos/+video-watch/modal/video-support.component.ts
+++ /dev/null
@@ -1,31 +0,0 @@
1import { Component, Input, ViewChild } from '@angular/core'
2import { MarkdownService } from '@app/core'
3import { VideoDetails } from '@app/shared/shared-main'
4import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
5
6@Component({
7 selector: 'my-video-support',
8 templateUrl: './video-support.component.html',
9 styleUrls: [ './video-support.component.scss' ]
10})
11export class VideoSupportComponent {
12 @Input() video: VideoDetails = null
13
14 @ViewChild('modal', { static: true }) modal: NgbModal
15
16 videoHTMLSupport = ''
17
18 constructor (
19 private markdownService: MarkdownService,
20 private modalService: NgbModal
21 ) { }
22
23 show () {
24 const modalRef = this.modalService.open(this.modal, { centered: true })
25
26 this.markdownService.enhancedMarkdownToHTML(this.video.support)
27 .then(r => this.videoHTMLSupport = r)
28
29 return modalRef
30 }
31}
diff --git a/client/src/app/+videos/+video-watch/player-styles.component.scss b/client/src/app/+videos/+video-watch/player-styles.component.scss
new file mode 100644
index 000000000..7f1442a59
--- /dev/null
+++ b/client/src/app/+videos/+video-watch/player-styles.component.scss
@@ -0,0 +1,4 @@
1@import 'node_modules/video.js/dist/video-js';
2
3$assets-path: '../../assets/';
4@import '../../../sass/player/index';
diff --git a/client/src/app/+videos/+video-watch/player-styles.component.ts b/client/src/app/+videos/+video-watch/player-styles.component.ts
new file mode 100644
index 000000000..9b1672a8c
--- /dev/null
+++ b/client/src/app/+videos/+video-watch/player-styles.component.ts
@@ -0,0 +1,15 @@
1import { Component, ViewEncapsulation } from '@angular/core'
2
3/*
4* Allows to lazy load global player styles in the watch component
5*/
6
7@Component({
8 selector: 'my-player-styles',
9 template: '',
10 styleUrls: [ './player-styles.component.scss' ],
11 // tslint:disable:use-component-view-encapsulation
12 encapsulation: ViewEncapsulation.None
13})
14export class PlayerStylesComponent {
15}
diff --git a/client/src/app/+videos/+video-watch/recommendations/recent-videos-recommendation.service.ts b/client/src/app/+videos/+video-watch/recommendations/recent-videos-recommendation.service.ts
index 29fa268f4..2a851f13a 100644
--- a/client/src/app/+videos/+video-watch/recommendations/recent-videos-recommendation.service.ts
+++ b/client/src/app/+videos/+video-watch/recommendations/recent-videos-recommendation.service.ts
@@ -61,7 +61,7 @@ export class RecentVideosRecommendationService implements RecommendationService
61 componentPagination: pagination, 61 componentPagination: pagination,
62 advancedSearch: new AdvancedSearch({ 62 advancedSearch: new AdvancedSearch({
63 tagsOneOf: recommendation.tags.join(','), 63 tagsOneOf: recommendation.tags.join(','),
64 sort: '-createdAt', 64 sort: '-publishedAt',
65 searchTarget: 'local', 65 searchTarget: 'local',
66 nsfw: user.nsfwPolicy 66 nsfw: user.nsfwPolicy
67 ? this.videos.nsfwPolicyToParam(user.nsfwPolicy) 67 ? this.videos.nsfwPolicyToParam(user.nsfwPolicy)
diff --git a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html
index 3c7c679b8..e0e9f92e7 100644
--- a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html
+++ b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html
@@ -1,4 +1,4 @@
1<div class="other-videos"> 1<div class="other-videos" [ngClass]="{ 'display-as-row': displayAsRow }">
2 <ng-container *ngIf="hasVideos$ | async"> 2 <ng-container *ngIf="hasVideos$ | async">
3 <div class="title-page-container"> 3 <div class="title-page-container">
4 <h2 i18n class="title-page title-page-single"> 4 <h2 i18n class="title-page title-page-single">
@@ -14,7 +14,7 @@
14 14
15 <ng-container *ngFor="let video of (videos$ | async); let i = index; let length = count"> 15 <ng-container *ngFor="let video of (videos$ | async); let i = index; let length = count">
16 <my-video-miniature 16 <my-video-miniature
17 [displayOptions]="displayOptions" [video]="video" [user]="userMiniature" 17 [displayOptions]="displayOptions" [video]="video" [user]="userMiniature" [displayAsRow]="displayAsRow"
18 (videoBlocked)="onVideoRemoved()" (videoRemoved)="onVideoRemoved()" (videoAccountMuted)="onVideoRemoved()"> 18 (videoBlocked)="onVideoRemoved()" (videoRemoved)="onVideoRemoved()" (videoAccountMuted)="onVideoRemoved()">
19 </my-video-miniature> 19 </my-video-miniature>
20 20
diff --git a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss
index b278c9654..c9fae6f27 100644
--- a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss
+++ b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss
@@ -1,3 +1,6 @@
1@import '_variables';
2@import '_mixins';
3
1.title-page-container { 4.title-page-container {
2 display: flex; 5 display: flex;
3 justify-content: space-between; 6 justify-content: space-between;
@@ -11,6 +14,10 @@
11 } 14 }
12} 15}
13 16
17.title-page {
18 margin-top: 0;
19}
20
14.title-page-autoplay { 21.title-page-autoplay {
15 display: flex; 22 display: flex;
16 width: max-content; 23 width: max-content;
@@ -29,3 +36,29 @@
29hr { 36hr {
30 margin-top: 0; 37 margin-top: 0;
31} 38}
39
40my-video-miniature {
41 display: block;
42}
43
44.other-videos:not(.display-as-row) my-video-miniature {
45 min-width: $video-thumbnail-medium-width;
46 max-width: $video-thumbnail-medium-width;
47}
48
49.display-as-row {
50 my-video-miniature {
51 margin-bottom: 20px;
52 }
53
54 hr {
55 display: none;
56 }
57
58 @media screen and (max-width: $mobile-view) {
59 my-video-miniature {
60 margin-bottom: 10px;
61 }
62 }
63}
64
diff --git a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.ts b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.ts
index a1c8e0661..89b9c01b6 100644
--- a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.ts
+++ b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.ts
@@ -16,6 +16,8 @@ import { RecommendedVideosStore } from './recommended-videos.store'
16export class RecommendedVideosComponent implements OnInit, OnChanges { 16export class RecommendedVideosComponent implements OnInit, OnChanges {
17 @Input() inputRecommendation: RecommendationInfo 17 @Input() inputRecommendation: RecommendationInfo
18 @Input() playlist: VideoPlaylist 18 @Input() playlist: VideoPlaylist
19 @Input() displayAsRow: boolean
20
19 @Output() gotRecommendations = new EventEmitter<Video[]>() 21 @Output() gotRecommendations = new EventEmitter<Video[]>()
20 22
21 autoPlayNextVideo: boolean 23 autoPlayNextVideo: boolean
diff --git a/client/src/app/+videos/+video-watch/video-avatar-channel.component.html b/client/src/app/+videos/+video-watch/video-avatar-channel.component.html
new file mode 100644
index 000000000..a02373f2d
--- /dev/null
+++ b/client/src/app/+videos/+video-watch/video-avatar-channel.component.html
@@ -0,0 +1,21 @@
1<div class="wrapper" [ngClass]="'avatar-' + size">
2 <ng-container *ngIf="!isChannelAvatarNull() && !genericChannel">
3 <a [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle">
4 <img [src]="video.videoChannelAvatarUrl" i18n-alt alt="Channel avatar" class="channel-avatar" />
5 </a>
6
7 <my-account-avatar [account]="video.account" [title]="accountLinkTitle" [internalHref]="[ '/accounts', video.byAccount ]"></my-account-avatar>
8</ng-container>
9
10 <ng-container *ngIf="!isChannelAvatarNull() && genericChannel">
11 <my-account-avatar [account]="video.account" [title]="accountLinkTitle" [internalHref]="[ '/accounts', video.byAccount ]"></my-account-avatar>
12
13 <a [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle">
14 <img [src]="video.videoChannelAvatarUrl" i18n-alt alt="Channel avatar" class="channel-avatar" />
15 </a>
16 </ng-container>
17
18 <ng-container *ngIf="isChannelAvatarNull()">
19 <my-account-avatar [account]="video.account" [title]="accountLinkTitle" [internalHref]="[ '/accounts', video.byAccount ]"></my-account-avatar>
20 </ng-container>
21</div>
diff --git a/client/src/app/+videos/+video-watch/video-avatar-channel.component.scss b/client/src/app/+videos/+video-watch/video-avatar-channel.component.scss
new file mode 100644
index 000000000..4998e85fa
--- /dev/null
+++ b/client/src/app/+videos/+video-watch/video-avatar-channel.component.scss
@@ -0,0 +1,44 @@
1@import '_mixins';
2
3.wrapper {
4 $avatar-size: 35px;
5
6 width: $avatar-size;
7 height: $avatar-size;
8 position: relative;
9 margin-right: 5px;
10 margin-bottom: 5px;
11
12 &.avatar-sm {
13 width: 28px;
14 height: 28px;
15 margin-bottom: 3px;
16 }
17
18 a {
19 @include disable-outline;
20 }
21
22 a img {
23 height: 100%;
24 object-fit: cover;
25 position: absolute;
26 top:50%;
27 left:50%;
28 transform: translate(-50%,-50%);
29 border-radius: 5px;
30
31 &:not(.channel-avatar) {
32 border-radius: 50%;
33 }
34 }
35
36 a:nth-of-type(2) img {
37 height: 60%;
38 width: 60%;
39 border: 2px solid pvar(--mainBackgroundColor);
40 transform: translateX(15%);
41 position: relative;
42 background-color: pvar(--mainBackgroundColor);
43 }
44}
diff --git a/client/src/app/+videos/+video-watch/video-avatar-channel.component.ts b/client/src/app/+videos/+video-watch/video-avatar-channel.component.ts
new file mode 100644
index 000000000..0b6e796df
--- /dev/null
+++ b/client/src/app/+videos/+video-watch/video-avatar-channel.component.ts
@@ -0,0 +1,27 @@
1import { Component, Input, OnInit } from '@angular/core'
2import { Video } from '@app/shared/shared-main/video'
3
4@Component({
5 selector: 'my-video-avatar-channel',
6 templateUrl: './video-avatar-channel.component.html',
7 styleUrls: [ './video-avatar-channel.component.scss' ]
8})
9export class VideoAvatarChannelComponent implements OnInit {
10 @Input() video: Video
11 @Input() byAccount: string
12
13 @Input() size: 'md' | 'sm' = 'md'
14 @Input() genericChannel: boolean
15
16 channelLinkTitle = ''
17 accountLinkTitle = ''
18
19 ngOnInit () {
20 this.channelLinkTitle = $localize`${this.video.account.name} (channel page)`
21 this.accountLinkTitle = $localize`${this.video.byAccount} (account page)`
22 }
23
24 isChannelAvatarNull () {
25 return this.video.channel.avatar === null
26 }
27}
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.html b/client/src/app/+videos/+video-watch/video-watch.component.html
index b17f898ce..eadb2148a 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.html
+++ b/client/src/app/+videos/+video-watch/video-watch.component.html
@@ -16,6 +16,8 @@
16 [playlist]="playlist" class="playlist" 16 [playlist]="playlist" class="playlist"
17 (videoFound)="onPlaylistVideoFound($event)" 17 (videoFound)="onPlaylistVideoFound($event)"
18 ></my-video-watch-playlist> 18 ></my-video-watch-playlist>
19
20 <my-plugin-placeholder pluginId="player-next"></my-plugin-placeholder>
19 </div> 21 </div>
20 22
21 <div class="row"> 23 <div class="row">
@@ -142,7 +144,7 @@
142 <ng-container *ngIf="isUserLoggedIn()"> 144 <ng-container *ngIf="isUserLoggedIn()">
143 <my-video-actions-dropdown 145 <my-video-actions-dropdown
144 placement="bottom auto" buttonDirection="horizontal" [buttonStyled]="true" [video]="video" [videoCaptions]="videoCaptions" 146 placement="bottom auto" buttonDirection="horizontal" [buttonStyled]="true" [video]="video" [videoCaptions]="videoCaptions"
145 [displayOptions]="videoActionsOptions" (videoRemoved)="onVideoRemoved()" (modalOpened)="onModalOpened()" 147 [displayOptions]="videoActionsOptions" (videoRemoved)="onVideoRemoved()"
146 ></my-video-actions-dropdown> 148 ></my-video-actions-dropdown>
147 </ng-container> 149 </ng-container>
148 </div> 150 </div>
@@ -230,8 +232,8 @@
230 </div> 232 </div>
231 233
232 <div *ngIf="video.isLocal === false" class="video-attribute"> 234 <div *ngIf="video.isLocal === false" class="video-attribute">
233 <span i18n class="video-attribute-label">Origin instance</span> 235 <span i18n class="video-attribute-label">Origin</span>
234 <a class="video-attribute-value" target="_blank" rel="noopener noreferrer" [href]="video.originInstanceUrl">{{ video.originInstanceHost }}</a> 236 <a class="video-attribute-value" target="_blank" rel="noopener noreferrer" [href]="getVideoUrl()">{{ video.originInstanceHost }}</a>
235 </div> 237 </div>
236 238
237 <div *ngIf="!!video.originallyPublishedAt" class="video-attribute"> 239 <div *ngIf="!!video.originallyPublishedAt" class="video-attribute">
@@ -289,6 +291,7 @@
289 </div> 291 </div>
290 292
291 <my-recommended-videos 293 <my-recommended-videos
294 [displayAsRow]="displayOtherVideosAsRow()"
292 [inputRecommendation]="{ uuid: video.uuid, tags: video.tags }" 295 [inputRecommendation]="{ uuid: video.uuid, tags: video.tags }"
293 [playlist]="playlist" 296 [playlist]="playlist"
294 (gotRecommendations)="onRecommendations($event)" 297 (gotRecommendations)="onRecommendations($event)"
@@ -313,6 +316,8 @@
313</div> 316</div>
314 317
315<ng-container *ngIf="video !== null"> 318<ng-container *ngIf="video !== null">
316 <my-video-support #videoSupportModal [video]="video"></my-video-support> 319 <my-support-modal #supportModal [video]="video"></my-support-modal>
317 <my-video-share #videoShareModal [video]="video" [videoCaptions]="videoCaptions" [playlist]="playlist"></my-video-share> 320 <my-video-share #videoShareModal [video]="video" [videoCaptions]="videoCaptions" [playlist]="playlist"></my-video-share>
318</ng-container> 321</ng-container>
322
323<my-player-styles></my-player-styles>
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.scss b/client/src/app/+videos/+video-watch/video-watch.component.scss
index 555126cbc..e8ad10a11 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.scss
+++ b/client/src/app/+videos/+video-watch/video-watch.component.scss
@@ -3,7 +3,7 @@
3@import '_bootstrap-variables'; 3@import '_bootstrap-variables';
4@import '_miniature'; 4@import '_miniature';
5 5
6$player-factor: 1.7; // 16/9 6$player-factor: 16/9;
7$video-info-margin-left: 44px; 7$video-info-margin-left: 44px;
8 8
9@function getPlayerHeight($width){ 9@function getPlayerHeight($width){
@@ -179,12 +179,6 @@ $video-info-margin-left: 44px;
179 &:hover { 179 &:hover {
180 opacity: 0.8; 180 opacity: 0.8;
181 } 181 }
182
183 img {
184 @include avatar(18px);
185
186 margin: -2px 5px 0 0;
187 }
188 } 182 }
189 183
190 .video-info-channel-left { 184 .video-info-channel-left {
@@ -212,11 +206,6 @@ $video-info-margin-left: 44px;
212 } 206 }
213 } 207 }
214 208
215 my-feed {
216 margin-left: 5px;
217 margin-top: 1px;
218 }
219
220 .video-actions-rates { 209 .video-actions-rates {
221 margin: 0 0 10px 0; 210 margin: 0 0 10px 0;
222 align-items: start; 211 align-items: start;
@@ -413,37 +402,12 @@ $video-info-margin-left: 44px;
413 } 402 }
414 } 403 }
415 } 404 }
405}
416 406
417 ::ng-deep .other-videos { 407my-recommended-videos {
418 padding-left: 15px; 408 display: block;
419 min-width: $video-miniature-width; 409 padding-left: 15px;
420 410 min-width: 250px;
421 @media screen and (min-width: 1800px - (3* $video-miniature-width)) {
422 width: min-content;
423 }
424
425 .title-page {
426 margin: 0 !important;
427 }
428
429 .video-miniature {
430 display: flex;
431 width: max-content;
432 height: 100%;
433 padding-bottom: 20px;
434 flex-wrap: wrap;
435 }
436
437 .video-bottom {
438 @media screen and (max-width: 1800px - (3* $video-miniature-width)) {
439 margin-left: 1rem;
440 }
441 @media screen and (max-width: 500px) {
442 margin-left: 0;
443 margin-top: .5rem;
444 }
445 }
446 }
447} 411}
448 412
449my-video-comments { 413my-video-comments {
@@ -537,6 +501,7 @@ my-video-comments {
537 } 501 }
538} 502}
539 503
504// Use the same breakpoint than in the typescript component to display the other video miniatures as row
540@media screen and (max-width: 1100px) { 505@media screen and (max-width: 1100px) {
541 #video-wrapper { 506 #video-wrapper {
542 flex-direction: column; 507 flex-direction: column;
@@ -549,15 +514,10 @@ my-video-comments {
549 514
550 .video-bottom { 515 .video-bottom {
551 flex-direction: column; 516 flex-direction: column;
517 }
552 518
553 ::ng-deep .other-videos { 519 my-recommended-videos {
554 padding-left: 0 !important; 520 padding-left: 0;
555
556 ::ng-deep .video-miniature {
557 flex-direction: row;
558 width: auto;
559 }
560 }
561 } 521 }
562} 522}
563 523
@@ -579,10 +539,6 @@ my-video-comments {
579 } 539 }
580 } 540 }
581 541
582 ::ng-deep .other-videos .video-miniature {
583 flex-direction: column;
584 }
585
586 .privacy-concerns { 542 .privacy-concerns {
587 width: 100%; 543 width: 100%;
588 } 544 }
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts
index 7a98cab3b..366e9bb57 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.component.ts
@@ -21,6 +21,7 @@ import { RedirectService } from '@app/core/routing/redirect.service'
21import { isXPercentInViewport, scrollToTop } from '@app/helpers' 21import { isXPercentInViewport, scrollToTop } from '@app/helpers'
22import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' 22import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main'
23import { VideoShareComponent } from '@app/shared/shared-share-modal' 23import { VideoShareComponent } from '@app/shared/shared-share-modal'
24import { SupportModalComponent } from '@app/shared/shared-support-modal'
24import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' 25import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
25import { VideoActionsDisplayType, VideoDownloadComponent } from '@app/shared/shared-video-miniature' 26import { VideoActionsDisplayType, VideoDownloadComponent } from '@app/shared/shared-video-miniature'
26import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' 27import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
@@ -28,7 +29,12 @@ import { MetaService } from '@ngx-meta/core'
28import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 29import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
29import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 30import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
30import { ServerConfig, ServerErrorCode, UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '@shared/models' 31import { ServerConfig, ServerErrorCode, UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '@shared/models'
31import { getStoredP2PEnabled, getStoredTheater } from '../../../assets/player/peertube-player-local-storage' 32import {
33 cleanupVideoWatch,
34 getStoredP2PEnabled,
35 getStoredTheater,
36 getStoredVideoWatchHistory
37} from '../../../assets/player/peertube-player-local-storage'
32import { 38import {
33 CustomizationOptions, 39 CustomizationOptions,
34 P2PMediaLoaderOptions, 40 P2PMediaLoaderOptions,
@@ -39,7 +45,6 @@ import {
39} from '../../../assets/player/peertube-player-manager' 45} from '../../../assets/player/peertube-player-manager'
40import { isWebRTCDisabled, timeToInt } from '../../../assets/player/utils' 46import { isWebRTCDisabled, timeToInt } from '../../../assets/player/utils'
41import { environment } from '../../../environments/environment' 47import { environment } from '../../../environments/environment'
42import { VideoSupportComponent } from './modal/video-support.component'
43import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' 48import { VideoWatchPlaylistComponent } from './video-watch-playlist.component'
44 49
45type URLOptions = CustomizationOptions & { playerMode: PlayerMode } 50type URLOptions = CustomizationOptions & { playerMode: PlayerMode }
@@ -54,7 +59,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
54 59
55 @ViewChild('videoWatchPlaylist', { static: true }) videoWatchPlaylist: VideoWatchPlaylistComponent 60 @ViewChild('videoWatchPlaylist', { static: true }) videoWatchPlaylist: VideoWatchPlaylistComponent
56 @ViewChild('videoShareModal') videoShareModal: VideoShareComponent 61 @ViewChild('videoShareModal') videoShareModal: VideoShareComponent
57 @ViewChild('videoSupportModal') videoSupportModal: VideoSupportComponent 62 @ViewChild('supportModal') supportModal: SupportModalComponent
58 @ViewChild('subscribeButton') subscribeButton: SubscribeButtonComponent 63 @ViewChild('subscribeButton') subscribeButton: SubscribeButtonComponent
59 @ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent 64 @ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent
60 65
@@ -195,6 +200,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
195 this.theaterEnabled = getStoredTheater() 200 this.theaterEnabled = getStoredTheater()
196 201
197 this.hooks.runAction('action:video-watch.init', 'video-watch') 202 this.hooks.runAction('action:video-watch.init', 'video-watch')
203
204 setTimeout(cleanupVideoWatch, 1500) // Run in timeout to ensure we're not blocking the UI
198 } 205 }
199 206
200 ngOnDestroy () { 207 ngOnDestroy () {
@@ -277,23 +284,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
277 } 284 }
278 285
279 showSupportModal () { 286 showSupportModal () {
280 // Check video was playing before opening support modal 287 this.supportModal.show()
281 const isVideoPlaying = this.isPlaying()
282
283 this.pausePlayer()
284
285 const modalRef = this.videoSupportModal.show()
286
287 modalRef.result.then(() => {
288 if (isVideoPlaying) {
289 this.resumePlayer()
290 }
291 })
292 } 288 }
293 289
294 showShareModal () { 290 showShareModal () {
295 this.pausePlayer()
296
297 this.videoShareModal.show(this.currentTime, this.videoWatchPlaylist.currentPlaylistPosition) 291 this.videoShareModal.show(this.currentTime, this.videoWatchPlaylist.currentPlaylistPosition)
298 } 292 }
299 293
@@ -301,6 +295,13 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
301 return this.authService.isLoggedIn() 295 return this.authService.isLoggedIn()
302 } 296 }
303 297
298 getVideoUrl () {
299 if (!this.video.url) {
300 return this.video.originInstanceUrl + VideoDetails.buildClientUrl(this.video.uuid)
301 }
302 return this.video.url
303 }
304
304 getVideoTags () { 305 getVideoTags () {
305 if (!this.video || Array.isArray(this.video.tags) === false) return [] 306 if (!this.video || Array.isArray(this.video.tags) === false) return []
306 307
@@ -316,10 +317,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
316 } 317 }
317 } 318 }
318 319
319 onModalOpened () {
320 this.pausePlayer()
321 }
322
323 onVideoRemoved () { 320 onVideoRemoved () {
324 this.redirectService.redirectToHomepage() 321 this.redirectService.redirectToHomepage()
325 } 322 }
@@ -396,6 +393,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
396 this.loadVideo(videoId) 393 this.loadVideo(videoId)
397 } 394 }
398 395
396 displayOtherVideosAsRow () {
397 // Use the same value as in the SASS file
398 return this.screenService.getWindowInnerWidth() <= 1100
399 }
400
399 private loadVideo (videoId: string) { 401 private loadVideo (videoId: string) {
400 // Video did not change 402 // Video did not change
401 if (this.video && this.video.uuid === videoId) return 403 if (this.video && this.video.uuid === videoId) return
@@ -570,7 +572,12 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
570 this.setOpenGraphTags() 572 this.setOpenGraphTags()
571 this.checkUserRating() 573 this.checkUserRating()
572 574
573 this.hooks.runAction('action:video-watch.video.loaded', 'video-watch', { videojs }) 575 const hookOptions = {
576 videojs,
577 video: this.video,
578 playlist: this.playlist
579 }
580 this.hooks.runAction('action:video-watch.video.loaded', 'video-watch', hookOptions)
574 } 581 }
575 582
576 private async buildPlayer (urlOptions: URLOptions) { 583 private async buildPlayer (urlOptions: URLOptions) {
@@ -768,9 +775,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
768 const getStartTime = () => { 775 const getStartTime = () => {
769 const byUrl = urlOptions.startTime !== undefined 776 const byUrl = urlOptions.startTime !== undefined
770 const byHistory = video.userHistory && (!this.playlist || urlOptions.resume !== undefined) 777 const byHistory = video.userHistory && (!this.playlist || urlOptions.resume !== undefined)
778 const byLocalStorage = getStoredVideoWatchHistory(video.uuid)
771 779
772 if (byUrl) return timeToInt(urlOptions.startTime) 780 if (byUrl) return timeToInt(urlOptions.startTime)
773 if (byHistory) return video.userHistory.currentTime 781 if (byHistory) return video.userHistory.currentTime
782 if (byLocalStorage) return byLocalStorage.duration
774 783
775 return 0 784 return 0
776 } 785 }
@@ -815,6 +824,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
815 ? this.videoService.getVideoViewUrl(video.uuid) 824 ? this.videoService.getVideoViewUrl(video.uuid)
816 : null, 825 : null,
817 embedUrl: video.embedUrl, 826 embedUrl: video.embedUrl,
827 embedTitle: video.name,
818 828
819 isLive: video.isLive, 829 isLive: video.isLive,
820 830
@@ -827,7 +837,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
827 837
828 serverUrl: environment.apiUrl, 838 serverUrl: environment.apiUrl,
829 839
830 videoCaptions: playerCaptions 840 videoCaptions: playerCaptions,
841
842 videoUUID: video.uuid
831 }, 843 },
832 844
833 webtorrent: { 845 webtorrent: {
@@ -867,24 +879,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
867 return { playerMode: mode, playerOptions: options } 879 return { playerMode: mode, playerOptions: options }
868 } 880 }
869 881
870 private pausePlayer () {
871 if (!this.player) return
872
873 this.player.pause()
874 }
875
876 private resumePlayer () {
877 if (!this.player) return
878
879 this.player.play()
880 }
881
882 private isPlaying () {
883 if (!this.player) return
884
885 return !this.player.paused()
886 }
887
888 private async subscribeToLiveEventsIfNeeded (oldVideo: VideoDetails, newVideo: VideoDetails) { 882 private async subscribeToLiveEventsIfNeeded (oldVideo: VideoDetails, newVideo: VideoDetails) {
889 if (!this.liveVideosSub) { 883 if (!this.liveVideosSub) {
890 this.liveVideosSub = this.buildLiveEventsSubscription() 884 this.liveVideosSub = this.buildLiveEventsSubscription()
diff --git a/client/src/app/+videos/+video-watch/video-watch.module.ts b/client/src/app/+videos/+video-watch/video-watch.module.ts
index fbda9b9c4..cf6afd852 100644
--- a/client/src/app/+videos/+video-watch/video-watch.module.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.module.ts
@@ -4,6 +4,7 @@ import { SharedGlobalIconModule } from '@app/shared/shared-icons'
4import { SharedMainModule } from '@app/shared/shared-main' 4import { SharedMainModule } from '@app/shared/shared-main'
5import { SharedModerationModule } from '@app/shared/shared-moderation' 5import { SharedModerationModule } from '@app/shared/shared-moderation'
6import { SharedShareModal } from '@app/shared/shared-share-modal' 6import { SharedShareModal } from '@app/shared/shared-share-modal'
7import { SharedSupportModal } from '@app/shared/shared-support-modal'
7import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription' 8import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription'
8import { SharedVideoModule } from '@app/shared/shared-video' 9import { SharedVideoModule } from '@app/shared/shared-video'
9import { SharedVideoCommentModule } from '@app/shared/shared-video-comment' 10import { SharedVideoCommentModule } from '@app/shared/shared-video-comment'
@@ -13,12 +14,14 @@ import { VideoCommentService } from '../../shared/shared-video-comment/video-com
13import { VideoCommentAddComponent } from './comment/video-comment-add.component' 14import { VideoCommentAddComponent } from './comment/video-comment-add.component'
14import { VideoCommentComponent } from './comment/video-comment.component' 15import { VideoCommentComponent } from './comment/video-comment.component'
15import { VideoCommentsComponent } from './comment/video-comments.component' 16import { VideoCommentsComponent } from './comment/video-comments.component'
16import { VideoSupportComponent } from './modal/video-support.component' 17import { PlayerStylesComponent } from './player-styles.component'
17import { RecommendationsModule } from './recommendations/recommendations.module' 18import { RecommendationsModule } from './recommendations/recommendations.module'
18import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive' 19import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive'
19import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' 20import { VideoWatchPlaylistComponent } from './video-watch-playlist.component'
20import { VideoWatchRoutingModule } from './video-watch-routing.module' 21import { VideoWatchRoutingModule } from './video-watch-routing.module'
21import { VideoWatchComponent } from './video-watch.component' 22import { VideoWatchComponent } from './video-watch.component'
23import { SharedAccountAvatarModule } from '../../shared/shared-account-avatar/shared-account-avatar.module'
24import { VideoAvatarChannelComponent } from './video-avatar-channel.component'
22 25
23@NgModule({ 26@NgModule({
24 imports: [ 27 imports: [
@@ -34,20 +37,25 @@ import { VideoWatchComponent } from './video-watch.component'
34 SharedGlobalIconModule, 37 SharedGlobalIconModule,
35 SharedVideoCommentModule, 38 SharedVideoCommentModule,
36 SharedShareModal, 39 SharedShareModal,
37 SharedVideoModule 40 SharedVideoModule,
41 SharedSupportModal,
42 SharedAccountAvatarModule
38 ], 43 ],
39 44
40 declarations: [ 45 declarations: [
41 VideoWatchComponent, 46 VideoWatchComponent,
42 VideoWatchPlaylistComponent, 47 VideoWatchPlaylistComponent,
43 48
44 VideoSupportComponent,
45 VideoCommentsComponent, 49 VideoCommentsComponent,
46 VideoCommentAddComponent, 50 VideoCommentAddComponent,
47 VideoCommentComponent, 51 VideoCommentComponent,
52 VideoAvatarChannelComponent,
53
54 VideoAvatarChannelComponent,
48 55
49 TimestampRouteTransformerDirective, 56 TimestampRouteTransformerDirective,
50 TimestampRouteTransformerDirective 57
58 PlayerStylesComponent
51 ], 59 ],
52 60
53 exports: [ 61 exports: [
diff --git a/client/src/app/+videos/video-list/overview/video-overview.component.html b/client/src/app/+videos/video-list/overview/video-overview.component.html
index ca986c634..639a96c43 100644
--- a/client/src/app/+videos/video-list/overview/video-overview.component.html
+++ b/client/src/app/+videos/video-list/overview/video-overview.component.html
@@ -14,7 +14,7 @@
14 </h1> 14 </h1>
15 15
16 <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)"> 16 <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)">
17 <my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="true"> 17 <my-video-miniature [video]="video" [user]="userMiniature" [displayVideoActions]="true">
18 </my-video-miniature> 18 </my-video-miniature>
19 </div> 19 </div>
20 </div> 20 </div>
@@ -25,7 +25,7 @@
25 </h2> 25 </h2>
26 26
27 <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)"> 27 <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)">
28 <my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="true"> 28 <my-video-miniature [video]="video" [user]="userMiniature" [displayVideoActions]="true">
29 </my-video-miniature> 29 </my-video-miniature>
30 </div> 30 </div>
31 </div> 31 </div>
@@ -40,7 +40,7 @@
40 </div> 40 </div>
41 41
42 <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)"> 42 <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)">
43 <my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="true"> 43 <my-video-miniature [video]="video" [user]="userMiniature" [displayVideoActions]="true">
44 </my-video-miniature> 44 </my-video-miniature>
45 </div> 45 </div>
46 </div> 46 </div>
diff --git a/client/src/app/+videos/video-list/overview/video-overview.component.scss b/client/src/app/+videos/video-list/overview/video-overview.component.scss
index c1d10188a..ec73c628c 100644
--- a/client/src/app/+videos/video-list/overview/video-overview.component.scss
+++ b/client/src/app/+videos/video-list/overview/video-overview.component.scss
@@ -8,9 +8,84 @@
8} 8}
9 9
10.margin-content { 10.margin-content {
11 @include fluid-videos-miniature-layout; 11 @include grid-videos-miniature-layout;
12} 12}
13 13
14.section { 14.section {
15 @include miniature-rows; 15 &:first-child {
16 padding-top: 30px;
17
18 .section-title {
19 border-top: none !important;
20 }
21 }
22
23 .section-title {
24 font-size: 24px;
25 font-weight: $font-semibold;
26 padding-top: 15px;
27 margin-bottom: 15px;
28 display: flex;
29 justify-content: space-between;
30
31 &:not(h2) {
32 border-top: 1px solid $separator-border-color;
33 }
34
35 a {
36 &:hover, &:focus:not(.focus-visible), &:active {
37 text-decoration: none;
38 outline: none;
39 }
40
41 color: pvar(--mainForegroundColor);
42 }
43 }
44
45 &.channel {
46 .section-title {
47 a {
48 display: flex;
49 width: fit-content;
50 align-items: center;
51
52 img {
53 @include channel-avatar(28px);
54
55 margin-right: 8px;
56 }
57 }
58
59 .followers {
60 color: pvar(--greyForegroundColor);
61 font-weight: normal;
62 font-size: 14px;
63 margin-left: 10px;
64 position: relative;
65 top: 2px;
66 }
67 }
68 }
69
70 .show-more {
71 position: relative;
72 top: -5px;
73 display: inline-block;
74 font-size: 16px;
75 text-transform: uppercase;
76 color: pvar(--greyForegroundColor);
77 margin-bottom: 10px;
78 font-weight: $font-semibold;
79 text-decoration: none;
80 }
81
82 @media screen and (max-width: $mobile-view) {
83 max-height: initial;
84 overflow: initial;
85
86 .section-title {
87 font-size: 17px;
88 margin-left: 10px;
89 }
90 }
16} 91}
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 e352a2b2c..6aabb93a5 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
@@ -7,7 +7,7 @@ import { HooksService } from '@app/core/plugins/hooks.service'
7import { immutableAssign } from '@app/helpers' 7import { immutableAssign } from '@app/helpers'
8import { VideoService } from '@app/shared/shared-main' 8import { VideoService } from '@app/shared/shared-main'
9import { UserSubscriptionService } from '@app/shared/shared-user-subscription' 9import { UserSubscriptionService } from '@app/shared/shared-user-subscription'
10import { AbstractVideoList, OwnerDisplayType } from '@app/shared/shared-video-miniature' 10import { AbstractVideoList } from '@app/shared/shared-video-miniature'
11import { FeedFormat, VideoSortField } from '@shared/models' 11import { FeedFormat, VideoSortField } from '@shared/models'
12import { environment } from '../../../environments/environment' 12import { environment } from '../../../environments/environment'
13import { copyToClipboard } from '../../../root-helpers/utils' 13import { copyToClipboard } from '../../../root-helpers/utils'
@@ -20,7 +20,6 @@ import { copyToClipboard } from '../../../root-helpers/utils'
20export class VideoUserSubscriptionsComponent extends AbstractVideoList implements OnInit, OnDestroy { 20export class VideoUserSubscriptionsComponent extends AbstractVideoList implements OnInit, OnDestroy {
21 titlePage: string 21 titlePage: string
22 sort = '-publishedAt' as VideoSortField 22 sort = '-publishedAt' as VideoSortField
23 ownerDisplayType: OwnerDisplayType = 'auto'
24 groupByDate = true 23 groupByDate = true
25 24
26 constructor ( 25 constructor (