From 82f443de1aba70ce75c72a4a7f669385600ab3c6 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 7 Aug 2020 16:29:30 +0200 Subject: Add buttons in playlist page To delete/edit/share the playlist --- ...-account-video-playlist-elements.component.html | 27 ++- ...-account-video-playlist-elements.component.scss | 16 ++ ...my-account-video-playlist-elements.component.ts | 64 +++++- client/src/app/+my-account/my-account.module.ts | 4 +- .../+video-watch/modal/video-share.component.html | 232 --------------------- .../+video-watch/modal/video-share.component.scss | 79 ------- .../+video-watch/modal/video-share.component.ts | 152 -------------- .../+video-watch/video-watch.component.html | 3 +- .../+videos/+video-watch/video-watch.component.ts | 6 +- .../app/+videos/+video-watch/video-watch.module.ts | 10 +- client/src/app/shared/shared-share-modal/index.ts | 3 + .../shared-share-modal.module.ts | 27 +++ .../shared-share-modal/video-share.component.html | 232 +++++++++++++++++++++ .../shared-share-modal/video-share.component.scss | 79 +++++++ .../shared-share-modal/video-share.component.ts | 148 +++++++++++++ client/src/sass/application.scss | 1 + 16 files changed, 604 insertions(+), 479 deletions(-) delete mode 100644 client/src/app/+videos/+video-watch/modal/video-share.component.html delete mode 100644 client/src/app/+videos/+video-watch/modal/video-share.component.scss delete mode 100644 client/src/app/+videos/+video-watch/modal/video-share.component.ts create mode 100644 client/src/app/shared/shared-share-modal/index.ts create mode 100644 client/src/app/shared/shared-share-modal/shared-share-modal.module.ts create mode 100644 client/src/app/shared/shared-share-modal/video-share.component.html create mode 100644 client/src/app/shared/shared-share-modal/video-share.component.scss create mode 100644 client/src/app/shared/shared-share-modal/video-share.component.ts (limited to 'client') diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.html b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.html index 2bfdf5c43..09b4c8a1b 100644 --- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.html +++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.html @@ -5,10 +5,33 @@ *ngIf="playlist" [playlist]="playlist" [toManage]="false" [displayChannel]="true" [displayDescription]="true" [displayPrivacy]="true" > + +
+ + + +
+
-
No videos in this playlist.
+
+
No videos in this playlist.
+ +
+ Browse videos on PeerTube to add them in your playlist. +
+ +
+ See the documentation for more information. +
+
+ + diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.scss b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.scss index 4531e475a..3e9b57c35 100644 --- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.scss +++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.scss @@ -10,7 +10,9 @@ padding: 10px; display: flex; + flex-direction: column; justify-content: center; + align-items: center; /* fix ellipsis dots background color */ ::ng-deep .miniature-name::after { @@ -18,6 +20,20 @@ } } +.playlist-buttons { + display:flex; + margin: 30px 0 10px 0; + + .share-button { + @include peertube-button; + @include button-with-icon(17px, 3px, -1px); + @include grey-button; + @include apply-svg-color(pvar(--actionButtonColor)); + + margin-right: 10px; + } +} + // Thanks Angular CDK <3 https://material.angular.io/cdk/drag-drop/examples .cdk-drag-preview { box-sizing: border-box; diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts index 0add81c38..e278d9ed2 100644 --- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts +++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts @@ -1,9 +1,13 @@ import { Subject, Subscription } from 'rxjs' import { CdkDragDrop } from '@angular/cdk/drag-drop' -import { Component, OnDestroy, OnInit } from '@angular/core' -import { ActivatedRoute } from '@angular/router' -import { ComponentPagination, Notifier, ScreenService } from '@app/core' +import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core' +import { ActivatedRoute, Router } from '@angular/router' +import { ComponentPagination, ConfirmService, Notifier, ScreenService } from '@app/core' +import { DropdownAction } from '@app/shared/shared-main' +import { VideoShareComponent } from '@app/shared/shared-share-modal' import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist' +import { I18n } from '@ngx-translate/i18n-polyfill' +import { VideoPlaylistType } from '@shared/models' @Component({ selector: 'my-account-video-playlist-elements', @@ -11,9 +15,13 @@ import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/ styleUrls: [ './my-account-video-playlist-elements.component.scss' ] }) export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestroy { + @ViewChild('videoShareModal') videoShareModal: VideoShareComponent + playlistElements: VideoPlaylistElement[] = [] playlist: VideoPlaylist + playlistActions: DropdownAction[][] = [] + pagination: ComponentPagination = { currentPage: 1, itemsPerPage: 10, @@ -27,12 +35,30 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro constructor ( private notifier: Notifier, + private i18n: I18n, + private router: Router, + private confirmService: ConfirmService, private route: ActivatedRoute, private screenService: ScreenService, private videoPlaylistService: VideoPlaylistService ) {} ngOnInit () { + this.playlistActions = [ + [ + { + label: this.i18n('Update playlist'), + iconName: 'edit', + linkBuilder: playlist => [ '/my-account', 'video-playlists', 'update', playlist.uuid ] + }, + { + label: this.i18n('Delete playlist'), + iconName: 'delete', + handler: playlist => this.deleteVideoPlaylist(playlist) + } + ] + ] + this.paramsSub = this.route.params.subscribe(routeParams => { this.videoPlaylistId = routeParams[ 'videoPlaylistId' ] this.loadElements() @@ -90,6 +116,38 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro return elem.id } + isRegularPlaylist (playlist: VideoPlaylist) { + return playlist?.type.id === VideoPlaylistType.REGULAR + } + + showShareModal () { + this.videoShareModal.show() + } + + async deleteVideoPlaylist (videoPlaylist: VideoPlaylist) { + const res = await this.confirmService.confirm( + this.i18n( + 'Do you really want to delete {{playlistDisplayName}}?', + { playlistDisplayName: videoPlaylist.displayName } + ), + this.i18n('Delete') + ) + if (res === false) return + + this.videoPlaylistService.removeVideoPlaylist(videoPlaylist) + .subscribe( + () => { + this.router.navigate([ '/my-account', 'video-playlists' ]) + + this.notifier.success( + this.i18n('Playlist {{playlistDisplayName}} deleted.', { playlistDisplayName: videoPlaylist.displayName }) + ) + }, + + error => this.notifier.error(error.message) + ) + } + /** * Returns null to not have drag and drop delay. * In small views, where elements are about 100% wide, diff --git a/client/src/app/+my-account/my-account.module.ts b/client/src/app/+my-account/my-account.module.ts index bf5a4fc8a..5f7ed4d2f 100644 --- a/client/src/app/+my-account/my-account.module.ts +++ b/client/src/app/+my-account/my-account.module.ts @@ -8,6 +8,7 @@ import { SharedFormModule } from '@app/shared/shared-forms' import { SharedGlobalIconModule } from '@app/shared/shared-icons' import { SharedMainModule } from '@app/shared/shared-main' import { SharedModerationModule } from '@app/shared/shared-moderation' +import { SharedShareModal } from '@app/shared/shared-share-modal' import { SharedUserInterfaceSettingsModule } from '@app/shared/shared-user-settings' import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription/shared-user-subscription.module' import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature' @@ -53,7 +54,8 @@ import { MyAccountComponent } from './my-account.component' SharedVideoPlaylistModule, SharedUserInterfaceSettingsModule, SharedGlobalIconModule, - SharedAbuseListModule + SharedAbuseListModule, + SharedShareModal ], declarations: [ diff --git a/client/src/app/+videos/+video-watch/modal/video-share.component.html b/client/src/app/+videos/+video-watch/modal/video-share.component.html deleted file mode 100644 index 946e8d8ca..000000000 --- a/client/src/app/+videos/+video-watch/modal/video-share.component.html +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - - diff --git a/client/src/app/+videos/+video-watch/modal/video-share.component.scss b/client/src/app/+videos/+video-watch/modal/video-share.component.scss deleted file mode 100644 index 091d4dc3b..000000000 --- a/client/src/app/+videos/+video-watch/modal/video-share.component.scss +++ /dev/null @@ -1,79 +0,0 @@ -@import '_mixins'; -@import '_variables'; - -my-input-readonly-copy { - width: 100%; -} - -.title-page.title-page-single { - margin-top: 0; -} - -.playlist { - margin-bottom: 50px; -} - -.peertube-select-container { - @include peertube-select-container(200px); -} - -.qr-code-group { - text-align: center; -} - -.nav-content { - margin-top: 30px; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; -} - -.alert { - margin-top: 20px; -} - -.filters { - margin-top: 30px; - - .advanced-filters-button { - display: flex; - justify-content: center; - align-items: center; - margin-top: 20px; - font-size: 16px; - font-weight: $font-semibold; - cursor: pointer; - - .glyphicon { - margin-right: 5px; - } - } - - .form-group { - margin-bottom: 0; - height: 34px; - display: flex; - align-items: center; - } - - .video-caption-block { - display: flex; - align-items: center; - - .peertube-select-container { - margin-left: 10px; - } - } - - .start-at, - .stop-at { - width: 300px; - display: flex; - align-items: center; - - my-timestamp-input { - margin-left: 10px; - } - } -} diff --git a/client/src/app/+videos/+video-watch/modal/video-share.component.ts b/client/src/app/+videos/+video-watch/modal/video-share.component.ts deleted file mode 100644 index d9171fe0e..000000000 --- a/client/src/app/+videos/+video-watch/modal/video-share.component.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { Component, ElementRef, Input, ViewChild } from '@angular/core' -import { buildVideoOrPlaylistEmbed, buildVideoLink, buildPlaylistLink } from '../../../../assets/player/utils' -import { NgbModal } from '@ng-bootstrap/ng-bootstrap' -import { VideoCaption } from '@shared/models' -import { VideoDetails } from '@app/shared/shared-main' -import { VideoPlaylist } from '@app/shared/shared-video-playlist' - -type Customizations = { - startAtCheckbox: boolean - startAt: number - - stopAtCheckbox: boolean - stopAt: number - - subtitleCheckbox: boolean - subtitle: string - - loop: boolean - autoplay: boolean - muted: boolean - title: boolean - warningTitle: boolean - controls: boolean - peertubeLink: boolean -} - -type TabId = 'url' | 'qrcode' | 'embed' - -@Component({ - selector: 'my-video-share', - templateUrl: './video-share.component.html', - styleUrls: [ './video-share.component.scss' ] -}) -export class VideoShareComponent { - @ViewChild('modal', { static: true }) modal: ElementRef - - @Input() video: VideoDetails = null - @Input() videoCaptions: VideoCaption[] = [] - @Input() playlist: VideoPlaylist = null - - activeVideoId: TabId = 'url' - activePlaylistId: TabId = 'url' - - customizations: Customizations - isAdvancedCustomizationCollapsed = true - includeVideoInPlaylist = false - - private playlistPosition: number = null - - constructor (private modalService: NgbModal) { } - - show (currentVideoTimestamp?: number, currentPlaylistPosition?: number) { - let subtitle: string - if (this.videoCaptions.length !== 0) { - subtitle = this.videoCaptions[0].language.id - } - - this.customizations = { - startAtCheckbox: false, - startAt: currentVideoTimestamp ? Math.floor(currentVideoTimestamp) : 0, - - stopAtCheckbox: false, - stopAt: this.video.duration, - - subtitleCheckbox: false, - subtitle, - - loop: false, - autoplay: false, - muted: false, - - // Embed options - title: true, - warningTitle: true, - controls: true, - peertubeLink: true - } - - this.playlistPosition = currentPlaylistPosition - - this.modalService.open(this.modal, { centered: true }) - } - - getVideoIframeCode () { - const options = this.getVideoOptions(this.video.embedUrl) - - const embedUrl = buildVideoLink(options) - return buildVideoOrPlaylistEmbed(embedUrl) - } - - getPlaylistIframeCode () { - const options = this.getPlaylistOptions(this.playlist.embedUrl) - - const embedUrl = buildPlaylistLink(options) - return buildVideoOrPlaylistEmbed(embedUrl) - } - - getVideoUrl () { - const baseUrl = window.location.origin + '/videos/watch/' + this.video.uuid - const options = this.getVideoOptions(baseUrl) - - return buildVideoLink(options) - } - - getPlaylistUrl () { - const base = window.location.origin + '/videos/watch/playlist/' + this.playlist.uuid - - if (!this.includeVideoInPlaylist) return base - - return base + '?videoId=' + this.video.uuid - } - - notSecure () { - return window.location.protocol === 'http:' - } - - isVideoInEmbedTab () { - return this.activeVideoId === 'embed' - } - - hasPlaylist () { - return !!this.playlist - } - - private getPlaylistOptions (baseUrl?: string) { - return { - baseUrl, - - playlistPosition: this.playlistPosition || undefined - } - } - - private getVideoOptions (baseUrl?: string) { - return { - baseUrl, - - startTime: this.customizations.startAtCheckbox ? this.customizations.startAt : undefined, - stopTime: this.customizations.stopAtCheckbox ? this.customizations.stopAt : undefined, - - subtitle: this.customizations.subtitleCheckbox ? this.customizations.subtitle : undefined, - - loop: this.customizations.loop, - autoplay: this.customizations.autoplay, - muted: this.customizations.muted, - - title: this.customizations.title, - warningTitle: this.customizations.warningTitle, - controls: this.customizations.controls, - peertubeLink: this.customizations.peertubeLink - } - } -} 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 2588b9af5..4279437d2 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.html +++ b/client/src/app/+videos/+video-watch/video-watch.component.html @@ -116,9 +116,10 @@ DOWNLOAD - + + + + + + + + diff --git a/client/src/app/shared/shared-share-modal/video-share.component.scss b/client/src/app/shared/shared-share-modal/video-share.component.scss new file mode 100644 index 000000000..091d4dc3b --- /dev/null +++ b/client/src/app/shared/shared-share-modal/video-share.component.scss @@ -0,0 +1,79 @@ +@import '_mixins'; +@import '_variables'; + +my-input-readonly-copy { + width: 100%; +} + +.title-page.title-page-single { + margin-top: 0; +} + +.playlist { + margin-bottom: 50px; +} + +.peertube-select-container { + @include peertube-select-container(200px); +} + +.qr-code-group { + text-align: center; +} + +.nav-content { + margin-top: 30px; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.alert { + margin-top: 20px; +} + +.filters { + margin-top: 30px; + + .advanced-filters-button { + display: flex; + justify-content: center; + align-items: center; + margin-top: 20px; + font-size: 16px; + font-weight: $font-semibold; + cursor: pointer; + + .glyphicon { + margin-right: 5px; + } + } + + .form-group { + margin-bottom: 0; + height: 34px; + display: flex; + align-items: center; + } + + .video-caption-block { + display: flex; + align-items: center; + + .peertube-select-container { + margin-left: 10px; + } + } + + .start-at, + .stop-at { + width: 300px; + display: flex; + align-items: center; + + my-timestamp-input { + margin-left: 10px; + } + } +} diff --git a/client/src/app/shared/shared-share-modal/video-share.component.ts b/client/src/app/shared/shared-share-modal/video-share.component.ts new file mode 100644 index 000000000..8d8e8a3a5 --- /dev/null +++ b/client/src/app/shared/shared-share-modal/video-share.component.ts @@ -0,0 +1,148 @@ +import { Component, ElementRef, Input, ViewChild } from '@angular/core' +import { VideoDetails } from '@app/shared/shared-main' +import { VideoPlaylist } from '@app/shared/shared-video-playlist' +import { NgbModal } from '@ng-bootstrap/ng-bootstrap' +import { VideoCaption } from '@shared/models' +import { buildPlaylistLink, buildVideoLink, buildVideoOrPlaylistEmbed } from '../../../assets/player/utils' + +type Customizations = { + startAtCheckbox: boolean + startAt: number + + stopAtCheckbox: boolean + stopAt: number + + subtitleCheckbox: boolean + subtitle: string + + loop: boolean + autoplay: boolean + muted: boolean + title: boolean + warningTitle: boolean + controls: boolean + peertubeLink: boolean +} + +type TabId = 'url' | 'qrcode' | 'embed' + +@Component({ + selector: 'my-video-share', + templateUrl: './video-share.component.html', + styleUrls: [ './video-share.component.scss' ] +}) +export class VideoShareComponent { + @ViewChild('modal', { static: true }) modal: ElementRef + + @Input() video: VideoDetails = null + @Input() videoCaptions: VideoCaption[] = [] + @Input() playlist: VideoPlaylist = null + + activeVideoId: TabId = 'url' + activePlaylistId: TabId = 'url' + + customizations: Customizations + isAdvancedCustomizationCollapsed = true + includeVideoInPlaylist = false + + private playlistPosition: number = null + + constructor (private modalService: NgbModal) { } + + show (currentVideoTimestamp?: number, currentPlaylistPosition?: number) { + let subtitle: string + if (this.videoCaptions && this.videoCaptions.length !== 0) { + subtitle = this.videoCaptions[0].language.id + } + + this.customizations = { + startAtCheckbox: false, + startAt: currentVideoTimestamp ? Math.floor(currentVideoTimestamp) : 0, + + stopAtCheckbox: false, + stopAt: this.video?.duration, + + subtitleCheckbox: false, + subtitle, + + loop: false, + autoplay: false, + muted: false, + + // Embed options + title: true, + warningTitle: true, + controls: true, + peertubeLink: true + } + + this.playlistPosition = currentPlaylistPosition + + this.modalService.open(this.modal, { centered: true }) + } + + getVideoIframeCode () { + const options = this.getVideoOptions(this.video.embedUrl) + + const embedUrl = buildVideoLink(options) + return buildVideoOrPlaylistEmbed(embedUrl) + } + + getPlaylistIframeCode () { + const options = this.getPlaylistOptions(this.playlist.embedUrl) + + const embedUrl = buildPlaylistLink(options) + return buildVideoOrPlaylistEmbed(embedUrl) + } + + getVideoUrl () { + const baseUrl = window.location.origin + '/videos/watch/' + this.video.uuid + const options = this.getVideoOptions(baseUrl) + + return buildVideoLink(options) + } + + getPlaylistUrl () { + const base = window.location.origin + '/videos/watch/playlist/' + this.playlist.uuid + + if (!this.includeVideoInPlaylist) return base + + return base + '?videoId=' + this.video.uuid + } + + notSecure () { + return window.location.protocol === 'http:' + } + + isVideoInEmbedTab () { + return this.activeVideoId === 'embed' + } + + private getPlaylistOptions (baseUrl?: string) { + return { + baseUrl, + + playlistPosition: this.playlistPosition || undefined + } + } + + private getVideoOptions (baseUrl?: string) { + return { + baseUrl, + + startTime: this.customizations.startAtCheckbox ? this.customizations.startAt : undefined, + stopTime: this.customizations.stopAtCheckbox ? this.customizations.stopAt : undefined, + + subtitle: this.customizations.subtitleCheckbox ? this.customizations.subtitle : undefined, + + loop: this.customizations.loop, + autoplay: this.customizations.autoplay, + muted: this.customizations.muted, + + title: this.customizations.title, + warningTitle: this.customizations.warningTitle, + controls: this.customizations.controls, + peertubeLink: this.customizations.peertubeLink + } + } +} diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index cdf0ee992..f4e4d8977 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -282,6 +282,7 @@ table { max-height: 500px; display: flex; + flex-direction: column; align-items: center; justify-content: center; font-size: 16px; -- cgit v1.2.3