diff --git a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html new file mode 100644 index 000000000..8fb244cc4 --- /dev/null +++ b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html @@ -0,0 +1,85 @@ +
+
+ + + + + + +
+ + +
+ +
+
+ + + + + + + + + + +
+ +
+ +
+
+ +
+
+
+ + + + + diff --git a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.scss b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.scss new file mode 100644 index 000000000..967d515e6 --- /dev/null +++ b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.scss @@ -0,0 +1,99 @@ +@use '_variables' as *; +@use '_mixins' as *; + +.video-actions { + height: 40px; // Align with the title + display: flex; + align-items: center; + + .action-button:not(:first-child), + .action-dropdown, + my-video-actions-dropdown { + @include margin-left(5px); + } + + ::ng-deep.action-button { + @include peertube-button; + @include button-with-icon(21px, 0, -1px); + + font-size: 100%; + font-weight: $font-semibold; + display: inline-block; + padding: 0 10px; + white-space: nowrap; + background-color: transparent !important; + color: pvar(--actionButtonColor); + text-transform: uppercase; + + &::after { + display: none; + } + + &:hover { + opacity: 0.9; + } + + &.action-button-support { + color: pvar(--supportButtonColor); + + my-global-icon { + @include apply-svg-color(pvar(--supportButtonColor)); + } + } + + &.action-button-support { + my-global-icon { + ::ng-deep path:first-child { + fill: pvar(--supportButtonHeartColor) !important; + } + } + } + + &.action-button-save { + my-global-icon { + top: 0 !important; + right: -1px; + } + } + + .icon-text { + @include margin-left(3px); + } + } +} + +.likes-dislikes-bar-outer-container { + position: relative; +} + +.likes-dislikes-bar-inner-container { + position: absolute; + height: 20px; +} + +.likes-dislikes-bar { + $likes-bar-height: 2px; + + height: $likes-bar-height; + margin-top: -$likes-bar-height; + + width: 120px; + background-color: #ccc; + position: relative; + top: 10px; + + .likes-bar { + height: 100%; + background-color: #909090; + + &.liked { + background-color: pvar(--activatedActionButtonColor); + } + } +} + +@media screen and (max-width: 450px) { + .action-button .icon-text { + display: none !important; + } +} diff --git a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.ts b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.ts new file mode 100644 index 000000000..e59238ffe --- /dev/null +++ b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.ts @@ -0,0 +1,93 @@ +import { Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core' +import { RedirectService, ScreenService } from '@app/core' +import { VideoDetails } from '@app/shared/shared-main' +import { VideoShareComponent } from '@app/shared/shared-share-modal' +import { SupportModalComponent } from '@app/shared/shared-support-modal' +import { VideoActionsDisplayType, VideoDownloadComponent } from '@app/shared/shared-video-miniature' +import { VideoPlaylist } from '@app/shared/shared-video-playlist' +import { UserVideoRateType, VideoCaption } from '@shared/models/videos' + +@Component({ + selector: 'my-action-buttons', + templateUrl: './action-buttons.component.html', + styleUrls: [ './action-buttons.component.scss' ] +}) +export class ActionButtonsComponent implements OnInit, OnChanges { + @ViewChild('videoShareModal') videoShareModal: VideoShareComponent + @ViewChild('supportModal') supportModal: SupportModalComponent + @ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent + + @Input() video: VideoDetails + @Input() videoCaptions: VideoCaption[] + @Input() playlist: VideoPlaylist + + @Input() isUserLoggedIn: boolean + + @Input() currentTime: number + @Input() currentPlaylistPosition: number + + likesBarTooltipText = '' + + tooltipSupport = '' + tooltipSaveToPlaylist = '' + + videoActionsOptions: VideoActionsDisplayType = { + playlist: false, + download: true, + update: true, + blacklist: true, + delete: true, + report: true, + duplicate: true, + mute: true, + liveInfo: true + } + + userRating: UserVideoRateType + + constructor ( + private screenService: ScreenService, + private redirectService: RedirectService + ) { } + + ngOnInit () { + // Hide the tooltips for unlogged users in mobile view, this adds confusion with the popover + if (this.isUserLoggedIn || !this.screenService.isInMobileView()) { + this.tooltipSupport = $localize`Support options for this video` + this.tooltipSaveToPlaylist = $localize`Save to playlist` + } + } + + ngOnChanges () { + this.setVideoLikesBarTooltipText() + } + + showDownloadModal () { + this.videoDownloadModal.show(this.video, this.videoCaptions) + } + + isVideoDownloadable () { + return this.video && this.video instanceof VideoDetails && this.video.downloadEnabled && !this.video.isLive + } + + showSupportModal () { + this.supportModal.show() + } + + showShareModal () { + this.videoShareModal.show(this.currentTime, this.currentPlaylistPosition) + } + + onRateUpdated (userRating: UserVideoRateType) { + this.userRating = userRating + this.setVideoLikesBarTooltipText() + } + + onVideoRemoved () { + this.redirectService.redirectToHomepage() + } + + private setVideoLikesBarTooltipText () { + this.likesBarTooltipText = `${this.video.likes} likes / ${this.video.dislikes} dislikes` + } +} diff --git a/client/src/app/+videos/+video-watch/shared/action-buttons/index.ts b/client/src/app/+videos/+video-watch/shared/action-buttons/index.ts new file mode 100644 index 000000000..3844dd12e --- /dev/null +++ b/client/src/app/+videos/+video-watch/shared/action-buttons/index.ts @@ -0,0 +1,2 @@ +export * from './action-buttons.component' +export * from './video-rate.component' diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-rate.component.html b/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.html similarity index 100% rename from client/src/app/+videos/+video-watch/shared/metadata/video-rate.component.html rename to client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.html diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-rate.component.scss b/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.scss similarity index 100% rename from client/src/app/+videos/+video-watch/shared/metadata/video-rate.component.scss rename to client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.scss diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-rate.component.ts b/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts similarity index 99% rename from client/src/app/+videos/+video-watch/shared/metadata/video-rate.component.ts rename to client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts index 89a666a62..ecb5a9281 100644 --- a/client/src/app/+videos/+video-watch/shared/metadata/video-rate.component.ts +++ b/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts @@ -31,7 +31,7 @@ export class VideoRateComponent implements OnInit, OnChanges, OnDestroy { private screenService: ScreenService ) { } - async ngOnInit () { + ngOnInit () { // Hide the tooltips for unlogged users in mobile view, this adds confusion with the popover if (this.isUserLoggedIn || !this.screenService.isInMobileView()) { this.tooltipLike = $localize`Like this video` diff --git a/client/src/app/+videos/+video-watch/shared/index.ts b/client/src/app/+videos/+video-watch/shared/index.ts index 6c5ff7e9b..069f862e2 100644 --- a/client/src/app/+videos/+video-watch/shared/index.ts +++ b/client/src/app/+videos/+video-watch/shared/index.ts @@ -1,3 +1,4 @@ +export * from './action-buttons' export * from './comment' export * from './information' export * from './metadata' diff --git a/client/src/app/+videos/+video-watch/shared/metadata/index.ts b/client/src/app/+videos/+video-watch/shared/metadata/index.ts index ba97f7011..7f7ee797b 100644 --- a/client/src/app/+videos/+video-watch/shared/metadata/index.ts +++ b/client/src/app/+videos/+video-watch/shared/metadata/index.ts @@ -1,3 +1,2 @@ export * from './video-avatar-channel.component' export * from './video-description.component' -export * from './video-rate.component' diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts index b554567d9..23d00d31a 100644 --- a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts +++ b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Inject, Input, LOCALE_ID, OnChanges, Output } from '@angular/core' +import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core' import { MarkdownService, Notifier } from '@app/core' import { VideoDetails, VideoService } from '@app/shared/shared-main' @@ -21,8 +21,7 @@ export class VideoDescriptionComponent implements OnChanges { constructor ( private videoService: VideoService, private notifier: Notifier, - private markdownService: MarkdownService, - @Inject(LOCALE_ID) private localeId: string + private markdownService: MarkdownService ) { } ngOnChanges () { 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 2380d5a98..a84bafa2a 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.html +++ b/client/src/app/+videos/+video-watch/video-watch.component.html @@ -74,90 +74,13 @@ -
-
- - - - - - -
- - -
- -
-
- - - - - - - - - - -
- -
-
-
- -
-
-
-
- -
-
-
+ -
@@ -264,9 +187,4 @@
- - - - - 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 e075fc57e..1d83fa139 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.scss +++ b/client/src/app/+videos/+video-watch/video-watch.component.scss @@ -151,6 +151,7 @@ @include peertube-word-wrap; @include margin-right(30px); + min-height: 40px; // Align with the action buttons font-size: 27px; font-weight: $font-semibold; @@ -211,106 +212,6 @@ @include margin-left(5px); } } - - .video-actions-rates { - @include margin-left(auto); - @include margin-right(0); - - margin-top: 0; - margin-bottom: 10px; - - align-items: start; - width: max-content; - - .video-actions { - height: 40px; // Align with the title - display: flex; - align-items: center; - - .action-button:not(:first-child), - .action-dropdown, - my-video-actions-dropdown { - @include margin-left(5px); - } - - ::ng-deep.action-button { - @include peertube-button; - @include button-with-icon(21px, 0, -1px); - - font-size: 100%; - font-weight: $font-semibold; - display: inline-block; - padding: 0 10px; - white-space: nowrap; - background-color: transparent !important; - color: pvar(--actionButtonColor); - text-transform: uppercase; - - &::after { - display: none; - } - - &:hover { - opacity: 0.9; - } - - &.action-button-support { - color: pvar(--supportButtonColor); - - my-global-icon { - @include apply-svg-color(pvar(--supportButtonColor)); - } - } - - &.action-button-support { - my-global-icon { - ::ng-deep path:first-child { - fill: pvar(--supportButtonHeartColor) !important; - } - } - } - - &.action-button-save { - my-global-icon { - top: 0 !important; - right: -1px; - } - } - - .icon-text { - @include margin-left(3px); - } - } - } - - .video-info-likes-dislikes-bar-outer-container { - position: relative; - } - - .video-info-likes-dislikes-bar-inner-container { - position: absolute; - height: 20px; - } - - .video-info-likes-dislikes-bar { - $likes-bar-height: 2px; - height: $likes-bar-height; - margin-top: -$likes-bar-height; - width: 120px; - background-color: #ccc; - position: relative; - top: 10px; - - .likes-bar { - height: 100%; - background-color: #909090; - - &.liked { - background-color: pvar(--activatedActionButtonColor); - } - } - } - } } .video-attributes { @@ -351,6 +252,18 @@ } } +my-action-buttons { + @include margin-left(auto); + @include margin-right(0); + + display: block; + margin-top: 0; + margin-bottom: 10px; + + align-items: start; + width: max-content; +} + my-recommended-videos { @include padding-left(15px); @@ -411,10 +324,6 @@ my-video-comments { @media screen and (max-width: 450px) { .video-bottom { - .action-button .icon-text { - display: none !important; - } - .video-info .video-info-first-row { .video-info-name { font-size: 18px; @@ -423,12 +332,12 @@ my-video-comments { .video-info-date-views { font-size: 14px; } - - .video-actions-rates { - margin-top: 10px; - } } } + + my-action-buttons { + margin-top: 10px; + } } 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 5a0109e64..ca20c2b85 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch.component.ts @@ -18,24 +18,13 @@ import { UserService } from '@app/core' import { HooksService } from '@app/core/plugins/hooks.service' -import { RedirectService } from '@app/core/routing/redirect.service' import { isXPercentInViewport, scrollToTop } from '@app/helpers' import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' -import { VideoShareComponent } from '@app/shared/shared-share-modal' -import { SupportModalComponent } from '@app/shared/shared-support-modal' import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' -import { VideoActionsDisplayType, VideoDownloadComponent } from '@app/shared/shared-video-miniature' +import { VideoActionsDisplayType } from '@app/shared/shared-video-miniature' import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' -import { - HTMLServerConfig, - PeerTubeProblemDocument, - ServerErrorCode, - UserVideoRateType, - VideoCaption, - VideoPrivacy, - VideoState -} from '@shared/models' +import { HTMLServerConfig, PeerTubeProblemDocument, ServerErrorCode, VideoCaption, VideoPrivacy, VideoState } from '@shared/models' import { cleanupVideoWatch, getStoredTheater, getStoredVideoWatchHistory } from '../../../assets/player/peertube-player-local-storage' import { CustomizationOptions, @@ -58,10 +47,7 @@ type URLOptions = CustomizationOptions & { playerMode: PlayerMode } }) export class VideoWatchComponent implements OnInit, OnDestroy { @ViewChild('videoWatchPlaylist', { static: true }) videoWatchPlaylist: VideoWatchPlaylistComponent - @ViewChild('videoShareModal') videoShareModal: VideoShareComponent - @ViewChild('supportModal') supportModal: SupportModalComponent @ViewChild('subscribeButton') subscribeButton: SubscribeButtonComponent - @ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent player: any playerElement: HTMLVideoElement @@ -95,8 +81,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { liveInfo: true } - userRating: UserVideoRateType - private nextVideoUuid = '' private nextVideoTitle = '' private currentTime: number @@ -124,7 +108,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { private restExtractor: RestExtractor, private notifier: Notifier, private zone: NgZone, - private redirectService: RedirectService, private videoCaptionService: VideoCaptionService, private hotkeysService: HotkeysService, private hooks: HooksService, @@ -203,20 +186,12 @@ export class VideoWatchComponent implements OnInit, OnDestroy { this.hotkeysService.remove(this.hotkeys) } - showDownloadModal () { - this.videoDownloadModal.show(this.video, this.videoCaptions) + getCurrentTime () { + return this.currentTime } - isVideoDownloadable () { - return this.video && this.video instanceof VideoDetails && this.video.downloadEnabled && !this.video.isLive - } - - showSupportModal () { - this.supportModal.show() - } - - showShareModal () { - this.videoShareModal.show(this.currentTime, this.videoWatchPlaylist.currentPlaylistPosition) + getCurrentPlaylistPosition () { + return this.videoWatchPlaylist.currentPlaylistPosition } isUserLoggedIn () { @@ -245,10 +220,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { } } - onVideoRemoved () { - this.redirectService.redirectToHomepage() - } - isVideoToTranscode () { return this.video && this.video.state.id === VideoState.TO_TRANSCODE } @@ -261,10 +232,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { return this.video && this.video.scheduledUpdate !== undefined } - isLive () { - return !!(this.video?.isLive) - } - isWaitingForLive () { return this.video?.state.id === VideoState.WAITING_FOR_LIVE } @@ -311,11 +278,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { this.loadVideo(videoId) } - onRateUpdated (userRating: UserVideoRateType) { - this.userRating = userRating - this.setVideoLikesBarTooltipText() - } - displayOtherVideosAsRow () { // Use the same value as in the SASS file return this.screenService.getWindowInnerWidth() <= 1100 @@ -421,10 +383,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { }) } - private setVideoLikesBarTooltipText () { - this.likesBarTooltipText = `${this.video.likes} likes / ${this.video.dislikes} dislikes` - } - private handleError (err: any) { const errorMessage: string = typeof err === 'string' ? err : err.message if (!errorMessage) return @@ -467,8 +425,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { this.buildPlayer(urlOptions) .catch(err => console.error('Cannot build the player', err)) - this.setVideoLikesBarTooltipText() - this.setOpenGraphTags() const hookOptions = { 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 c1f40d785..602525342 100644 --- a/client/src/app/+videos/+video-watch/video-watch.module.ts +++ b/client/src/app/+videos/+video-watch/video-watch.module.ts @@ -19,6 +19,7 @@ import { VideoDescriptionComponent, VideoRateComponent, VideoWatchPlaylistComponent, + ActionButtonsComponent, PrivacyConcernsComponent } from './shared' import { VideoCommentAddComponent } from './shared/comment/video-comment-add.component' @@ -53,6 +54,7 @@ import { VideoWatchComponent } from './video-watch.component' VideoRateComponent, VideoDescriptionComponent, PrivacyConcernsComponent, + ActionButtonsComponent, VideoCommentsComponent, VideoCommentAddComponent,