From: Chocobozzz Date: Mon, 13 May 2019 09:18:24 +0000 (+0200) Subject: Move video watch playlist in its own component X-Git-Tag: v1.3.0-rc.1~16 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=72675ebe0181ab2389fb2f75a3b2acdca6e09c07;p=oweals%2Fpeertube.git Move video watch playlist in its own component --- diff --git a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss index f8a068cbc..cb7072d7f 100644 --- a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss +++ b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss @@ -2,6 +2,13 @@ @import '_mixins'; @import '_miniature'; +my-video-thumbnail { + @include thumbnail-size-component(130px, 72px); + + display: flex; // Avoids an issue with line-height that adds space below the element + margin-right: 10px; +} + .video { display: flex; align-items: center; @@ -44,13 +51,6 @@ } } - my-video-thumbnail { - @include thumbnail-size-component(130px, 72px); - - display: flex; // Avoids an issue with line-height that adds space below the element - margin-right: 10px; - } - .video-info { display: flex; flex-direction: column; diff --git a/client/src/app/shared/video/video-actions-dropdown.component.ts b/client/src/app/shared/video/video-actions-dropdown.component.ts index b2d77a9e6..c1da0eba6 100644 --- a/client/src/app/shared/video/video-actions-dropdown.component.ts +++ b/client/src/app/shared/video/video-actions-dropdown.component.ts @@ -1,4 +1,4 @@ -import { AfterContentInit, AfterViewInit, Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core' +import { AfterViewInit, Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core' import { I18n } from '@ngx-translate/i18n-polyfill' import { DropdownAction, DropdownButtonSize, DropdownDirection } from '@app/shared/buttons/action-dropdown.component' import { AuthService, ConfirmService, Notifier, ServerService } from '@app/core' @@ -133,6 +133,10 @@ export class VideoActionsDropdownComponent implements AfterViewInit, OnChanges { return this.video.isUnblacklistableBy(this.user) } + isVideoDownloadable () { + return this.video && this.video instanceof VideoDetails && this.video.downloadEnabled + } + /* Action handlers */ async unblacklistVideo () { @@ -202,7 +206,7 @@ export class VideoActionsDropdownComponent implements AfterViewInit, OnChanges { { label: this.i18n('Download'), handler: () => this.showDownloadModal(), - isDisplayed: () => this.displayOptions.download, + isDisplayed: () => this.displayOptions.download && this.isVideoDownloadable(), iconName: 'download' }, { diff --git a/client/src/app/videos/+video-watch/video-watch-playlist.component.html b/client/src/app/videos/+video-watch/video-watch-playlist.component.html new file mode 100644 index 000000000..c168a3130 --- /dev/null +++ b/client/src/app/videos/+video-watch/video-watch-playlist.component.html @@ -0,0 +1,25 @@ +
+
+
+ {{ playlist.displayName }} + + Unlisted + Private + Public +
+ +
+
{{ playlist.ownerBy }}
+
+ {{ currentPlaylistPosition }}{{ playlistPagination.totalItems }} +
+
+
+ +
+ +
+
diff --git a/client/src/app/videos/+video-watch/video-watch-playlist.component.scss b/client/src/app/videos/+video-watch/video-watch-playlist.component.scss new file mode 100644 index 000000000..5da55c2f8 --- /dev/null +++ b/client/src/app/videos/+video-watch/video-watch-playlist.component.scss @@ -0,0 +1,59 @@ +@import '_variables'; +@import '_mixins'; +@import '_bootstrap-variables'; +@import '_miniature'; + +.playlist { + min-width: 200px; + max-width: 470px; + height: 66vh; + background-color: var(--mainBackgroundColor); + overflow-y: auto; + border-bottom: 1px solid $separator-border-color; + + .playlist-info { + padding: 5px 30px; + background-color: #e4e4e4; + + .playlist-display-name { + font-size: 18px; + font-weight: $font-semibold; + margin-bottom: 5px; + } + + .playlist-by-index { + color: $grey-foreground-color; + display: flex; + + .playlist-by { + margin-right: 5px; + } + + .playlist-index span:first-child::after { + content: '/'; + margin: 0 3px; + } + } + } + + my-video-playlist-element-miniature { + /deep/ { + .video { + .position { + margin-right: 0; + } + + .video-info { + .video-info-name { + font-size: 15px; + } + } + } + + my-video-thumbnail { + @include thumbnail-size-component(90px, 50px); + } + } + } +} + diff --git a/client/src/app/videos/+video-watch/video-watch-playlist.component.ts b/client/src/app/videos/+video-watch/video-watch-playlist.component.ts new file mode 100644 index 000000000..3ac06c099 --- /dev/null +++ b/client/src/app/videos/+video-watch/video-watch-playlist.component.ts @@ -0,0 +1,111 @@ +import { Component, Input } from '@angular/core' +import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' +import { ComponentPagination } from '@app/shared/rest/component-pagination.model' +import { Video } from '@app/shared/video/video.model' +import { VideoDetails, VideoPlaylistPrivacy } from '@shared/models' +import { VideoService } from '@app/shared/video/video.service' +import { Router } from '@angular/router' +import { AuthService } from '@app/core' + +@Component({ + selector: 'my-video-watch-playlist', + templateUrl: './video-watch-playlist.component.html', + styleUrls: [ './video-watch-playlist.component.scss' ] +}) +export class VideoWatchPlaylistComponent { + @Input() video: VideoDetails + @Input() playlist: VideoPlaylist + + playlistVideos: Video[] = [] + playlistPagination: ComponentPagination = { + currentPage: 1, + itemsPerPage: 30, + totalItems: null + } + + noPlaylistVideos = false + currentPlaylistPosition = 1 + + constructor ( + private auth: AuthService, + private videoService: VideoService, + private router: Router + ) {} + + onPlaylistVideosNearOfBottom () { + // Last page + if (this.playlistPagination.totalItems <= (this.playlistPagination.currentPage * this.playlistPagination.itemsPerPage)) return + + this.playlistPagination.currentPage += 1 + this.loadPlaylistElements(this.playlist,false) + } + + onElementRemoved (video: Video) { + this.playlistVideos = this.playlistVideos.filter(v => v.id !== video.id) + + this.playlistPagination.totalItems-- + } + + isPlaylistOwned () { + return this.playlist.isLocal === true && this.playlist.ownerAccount.name === this.auth.getUser().username + } + + isUnlistedPlaylist () { + return this.playlist.privacy.id === VideoPlaylistPrivacy.UNLISTED + } + + isPrivatePlaylist () { + return this.playlist.privacy.id === VideoPlaylistPrivacy.PRIVATE + } + + isPublicPlaylist () { + return this.playlist.privacy.id === VideoPlaylistPrivacy.PUBLIC + } + + loadPlaylistElements (playlist: VideoPlaylist, redirectToFirst = false) { + this.videoService.getPlaylistVideos(playlist.uuid, this.playlistPagination) + .subscribe(({ totalVideos, videos }) => { + this.playlistVideos = this.playlistVideos.concat(videos) + this.playlistPagination.totalItems = totalVideos + + if (totalVideos === 0) { + this.noPlaylistVideos = true + return + } + + this.updatePlaylistIndex(this.video) + + if (redirectToFirst) { + const extras = { + queryParams: { videoId: this.playlistVideos[ 0 ].uuid }, + replaceUrl: true + } + this.router.navigate([], extras) + } + }) + } + + updatePlaylistIndex (video: VideoDetails) { + if (this.playlistVideos.length === 0 || !video) return + + for (const playlistVideo of this.playlistVideos) { + if (playlistVideo.id === video.id) { + this.currentPlaylistPosition = playlistVideo.playlistElement.position + return + } + } + + // Load more videos to find our video + this.onPlaylistVideosNearOfBottom() + } + + navigateToNextPlaylistVideo () { + if (this.currentPlaylistPosition < this.playlistPagination.totalItems) { + const next = this.playlistVideos.find(v => v.playlistElement.position === this.currentPlaylistPosition + 1) + + const start = next.playlistElement.startTimestamp + const stop = next.playlistElement.stopTimestamp + this.router.navigate([],{ queryParams: { videoId: next.uuid, start, stop } }) + } + } +} 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 7e9b89dd0..7da74b57e 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html @@ -9,31 +9,10 @@
-
-
-
- {{ playlist.displayName }} - - Unlisted - Private - Public -
- -
-
{{ playlist.ownerBy }}
-
- {{ currentPlaylistPosition }}{{ playlistPagination.totalItems }} -
-
-
- -
- -
-
+
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 d8113b666..8ca5c4118 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss @@ -15,10 +15,10 @@ $player-factor: 1.7; // 16/9 } @mixin playlist-below-player { - width: 100%; - height: auto; - max-height: 300px; - border-bottom: 1px solid $separator-border-color; + width: 100% !important; + height: auto !important; + max-height: 300px !important; + border-bottom: 1px solid $separator-border-color !important; } .root { @@ -37,7 +37,7 @@ $player-factor: 1.7; // 16/9 width: 100%; } - .playlist { + my-video-watch-playlist /deep/ .playlist { @include playlist-below-player; } } @@ -80,60 +80,6 @@ $player-factor: 1.7; // 16/9 } } - .playlist { - min-width: 200px; - max-width: 470px; - height: 66vh; - background-color: var(--mainBackgroundColor); - overflow-y: auto; - border-bottom: 1px solid $separator-border-color; - - .playlist-info { - padding: 5px 30px; - background-color: #e4e4e4; - - .playlist-display-name { - font-size: 18px; - font-weight: $font-semibold; - margin-bottom: 5px; - } - - .playlist-by-index { - color: $grey-foreground-color; - display: flex; - - .playlist-by { - margin-right: 5px; - } - - .playlist-index span:first-child::after { - content: '/'; - margin: 0 3px; - } - } - } - - my-video-playlist-element-miniature { - /deep/ { - .video { - .position { - margin-right: 0; - } - - .video-info { - .video-info-name { - font-size: 15px; - } - } - } - - my-video-thumbnail { - @include thumbnail-size-component(90px, 50px); - } - } - } - } - /deep/ .video-js { width: getPlayerWidth(66vh); height: 66vh; @@ -508,7 +454,7 @@ my-video-comments { flex-direction: column; justify-content: center; - .playlist { + my-video-watch-playlist /deep/ .playlist { @include playlist-below-player; } } 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 bce652210..0532e7de7 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts @@ -8,7 +8,7 @@ import { MetaService } from '@ngx-meta/core' import { Notifier, ServerService } from '@app/core' import { forkJoin, Subscription } from 'rxjs' import { Hotkey, HotkeysService } from 'angular2-hotkeys' -import { UserVideoRateType, VideoCaption, VideoPlaylistPrivacy, VideoPrivacy, VideoState } from '../../../../../shared' +import { UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '../../../../../shared' import { AuthService, ConfirmService } from '../../core' import { RestExtractor, VideoBlacklistService } from '../../shared' import { VideoDetails } from '../../shared/video/video-details.model' @@ -27,9 +27,9 @@ import { } from '../../../assets/player/peertube-player-manager' import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' -import { ComponentPagination } from '@app/shared/rest/component-pagination.model' import { Video } from '@app/shared/video/video.model' import { isWebRTCDisabled } from '../../../assets/player/utils' +import { VideoWatchPlaylistComponent } from '@app/videos/+video-watch/video-watch-playlist.component' @Component({ selector: 'my-video-watch', @@ -39,6 +39,7 @@ import { isWebRTCDisabled } from '../../../assets/player/utils' export class VideoWatchComponent implements OnInit, OnDestroy { private static LOCAL_STORAGE_PRIVACY_CONCERN_KEY = 'video-watch-privacy-concern' + @ViewChild('videoWatchPlaylist') videoWatchPlaylist: VideoWatchPlaylistComponent @ViewChild('videoShareModal') videoShareModal: VideoShareComponent @ViewChild('videoSupportModal') videoSupportModal: VideoSupportComponent @ViewChild('subscribeButton') subscribeButton: SubscribeButtonComponent @@ -51,14 +52,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { descriptionLoading = false playlist: VideoPlaylist = null - playlistVideos: Video[] = [] - playlistPagination: ComponentPagination = { - currentPage: 1, - itemsPerPage: 30, - totalItems: null - } - noPlaylistVideos = false - currentPlaylistPosition = 1 completeDescriptionShown = false completeVideoDescription: string @@ -230,10 +223,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { return this.video.tags } - isVideoRemovable () { - return this.video.isRemovableBy(this.authService.getUser()) - } - onVideoRemoved () { this.redirectService.redirectToHomepage() } @@ -247,10 +236,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { return this.video && this.video.state.id === VideoState.TO_TRANSCODE } - isVideoDownloadable () { - return this.video && this.video.downloadEnabled - } - isVideoToImport () { return this.video && this.video.state.id === VideoState.TO_IMPORT } @@ -263,36 +248,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { return video.isVideoNSFWForUser(this.user, this.serverService.getConfig()) } - isPlaylistOwned () { - return this.playlist.isLocal === true && this.playlist.ownerAccount.name === this.user.username - } - - isUnlistedPlaylist () { - return this.playlist.privacy.id === VideoPlaylistPrivacy.UNLISTED - } - - isPrivatePlaylist () { - return this.playlist.privacy.id === VideoPlaylistPrivacy.PRIVATE - } - - isPublicPlaylist () { - return this.playlist.privacy.id === VideoPlaylistPrivacy.PUBLIC - } - - onPlaylistVideosNearOfBottom () { - // Last page - if (this.playlistPagination.totalItems <= (this.playlistPagination.currentPage * this.playlistPagination.itemsPerPage)) return - - this.playlistPagination.currentPage += 1 - this.loadPlaylistElements(false) - } - - onElementRemoved (video: Video) { - this.playlistVideos = this.playlistVideos.filter(v => v.id !== video.id) - - this.playlistPagination.totalItems-- - } - private loadVideo (videoId: string) { // Video did not change if (this.video && this.video.uuid === videoId) return @@ -333,33 +288,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy { this.playlist = playlist const videoId = this.route.snapshot.queryParams['videoId'] - this.loadPlaylistElements(!videoId) + this.videoWatchPlaylist.loadPlaylistElements(playlist, !videoId) }) } - private loadPlaylistElements (redirectToFirst = false) { - this.videoService.getPlaylistVideos(this.playlist.uuid, this.playlistPagination) - .subscribe(({ totalVideos, videos }) => { - this.playlistVideos = this.playlistVideos.concat(videos) - this.playlistPagination.totalItems = totalVideos - - if (totalVideos === 0) { - this.noPlaylistVideos = true - return - } - - this.updatePlaylistIndex() - - if (redirectToFirst) { - const extras = { - queryParams: { videoId: this.playlistVideos[ 0 ].uuid }, - replaceUrl: true - } - this.router.navigate([], extras) - } - }) - } - private updateVideoDescription (description: string) { this.video.description = description this.setVideoDescriptionHTML() @@ -421,7 +353,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { this.remoteServerDown = false this.currentTime = undefined - this.updatePlaylistIndex() + this.videoWatchPlaylist.updatePlaylistIndex(video) let startTime = urlOptions.startTime || (this.video.userHistory ? this.video.userHistory.currentTime : 0) // If we are at the end of the video, reset the timer @@ -519,13 +451,13 @@ export class VideoWatchComponent implements OnInit, OnDestroy { this.player.one('ended', () => { if (this.playlist) { - this.zone.run(() => this.navigateToNextPlaylistVideo()) + this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo()) } }) this.player.one('stopped', () => { if (this.playlist) { - this.zone.run(() => this.navigateToNextPlaylistVideo()) + this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo()) } }) @@ -586,20 +518,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { this.setVideoLikesBarTooltipText() } - private updatePlaylistIndex () { - if (this.playlistVideos.length === 0 || !this.video) return - - for (const video of this.playlistVideos) { - if (video.id === this.video.id) { - this.currentPlaylistPosition = video.playlistElement.position - return - } - } - - // Load more videos to find our video - this.onPlaylistVideosNearOfBottom() - } - private setOpenGraphTags () { this.metaService.setTitle(this.video.name) @@ -639,14 +557,4 @@ export class VideoWatchComponent implements OnInit, OnDestroy { this.player = undefined } } - - private navigateToNextPlaylistVideo () { - if (this.currentPlaylistPosition < this.playlistPagination.totalItems) { - const next = this.playlistVideos.find(v => v.playlistElement.position === this.currentPlaylistPosition + 1) - - const start = next.playlistElement.startTimestamp - const stop = next.playlistElement.stopTimestamp - this.router.navigate([],{ queryParams: { videoId: next.uuid, start, stop } }) - } - } } 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 983350f52..67596a3da 100644 --- a/client/src/app/videos/+video-watch/video-watch.module.ts +++ b/client/src/app/videos/+video-watch/video-watch.module.ts @@ -11,6 +11,7 @@ import { VideoWatchComponent } from './video-watch.component' import { NgxQRCodeModule } from 'ngx-qrcode2' import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap' import { RecommendationsModule } from '@app/videos/recommendations/recommendations.module' +import { VideoWatchPlaylistComponent } from '@app/videos/+video-watch/video-watch-playlist.component' @NgModule({ imports: [ @@ -23,6 +24,7 @@ import { RecommendationsModule } from '@app/videos/recommendations/recommendatio declarations: [ VideoWatchComponent, + VideoWatchPlaylistComponent, VideoShareComponent, VideoSupportComponent,