diff options
author | Chocobozzz <me@florianbigard.com> | 2019-03-13 14:18:58 +0100 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2019-03-18 11:17:59 +0100 |
commit | e2f01c47e08d26a30ad47068d195b3d21d0df8a1 (patch) | |
tree | 21f18ed462d313bfb4ba7a1b5221fdb6b2c35bc1 /client/src/app/+my-account | |
parent | 15e9d5ca39e0b792f61453fbf3885a0fc446afa7 (diff) | |
download | PeerTube-e2f01c47e08d26a30ad47068d195b3d21d0df8a1.tar.gz PeerTube-e2f01c47e08d26a30ad47068d195b3d21d0df8a1.tar.zst PeerTube-e2f01c47e08d26a30ad47068d195b3d21d0df8a1.zip |
Playlist support in watch page
Diffstat (limited to 'client/src/app/+my-account')
3 files changed, 6 insertions, 247 deletions
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 67a8b1a91..bc26e198e 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,60 +5,7 @@ | |||
5 | cdkDropList (cdkDropListDropped)="drop($event)" | 5 | cdkDropList (cdkDropListDropped)="drop($event)" |
6 | > | 6 | > |
7 | <div class="video" *ngFor="let video of videos" cdkDrag (cdkDragMoved)="onDragMove($event)"> | 7 | <div class="video" *ngFor="let video of videos" cdkDrag (cdkDragMoved)="onDragMove($event)"> |
8 | <div class="position">{{ video.playlistElement.position }}</div> | 8 | <my-video-playlist-element-miniature [video]="video" [playlist]="playlist" [owned]="true" (elementRemoved)="onElementRemoved($event)"> |
9 | 9 | </my-video-playlist-element-miniature> | |
10 | <my-video-thumbnail [video]="video" [nsfw]="isVideoBlur(video)"></my-video-thumbnail> | ||
11 | |||
12 | <div class="video-info"> | ||
13 | <a tabindex="-1" class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a> | ||
14 | <a tabindex="-1" class="video-info-account" [routerLink]="[ '/accounts', video.byAccount ]">{{ video.byAccount }}</a> | ||
15 | <span tabindex="-1" class="video-info-timestamp">{{ formatTimestamp(video)}}</span> | ||
16 | </div> | ||
17 | |||
18 | <div class="more" ngbDropdown #moreDropdown="ngbDropdown" placement="bottom-right" (openChange)="onDropdownOpenChange()" autoClose="outside"> | ||
19 | <my-global-icon iconName="more-vertical" ngbDropdownToggle role="button" class="icon-more"></my-global-icon> | ||
20 | |||
21 | <div ngbDropdownMenu> | ||
22 | <div class="dropdown-item" (click)="toggleDisplayTimestampsOptions($event, video)"> | ||
23 | <my-global-icon iconName="edit"></my-global-icon> <ng-container i18n>Edit starts/stops at</ng-container> | ||
24 | </div> | ||
25 | |||
26 | <div class="timestamp-options" *ngIf="displayTimestampOptions"> | ||
27 | <div> | ||
28 | <my-peertube-checkbox | ||
29 | inputName="startAt" [(ngModel)]="timestampOptions.startTimestampEnabled" | ||
30 | i18n-labelText labelText="Start at" | ||
31 | ></my-peertube-checkbox> | ||
32 | |||
33 | <my-timestamp-input | ||
34 | [timestamp]="timestampOptions.startTimestamp" | ||
35 | [maxTimestamp]="video.duration" | ||
36 | [disabled]="!timestampOptions.startTimestampEnabled" | ||
37 | [(ngModel)]="timestampOptions.startTimestamp" | ||
38 | ></my-timestamp-input> | ||
39 | </div> | ||
40 | |||
41 | <div> | ||
42 | <my-peertube-checkbox | ||
43 | inputName="stopAt" [(ngModel)]="timestampOptions.stopTimestampEnabled" | ||
44 | i18n-labelText labelText="Stop at" | ||
45 | ></my-peertube-checkbox> | ||
46 | |||
47 | <my-timestamp-input | ||
48 | [timestamp]="timestampOptions.stopTimestamp" | ||
49 | [maxTimestamp]="video.duration" | ||
50 | [disabled]="!timestampOptions.stopTimestampEnabled" | ||
51 | [(ngModel)]="timestampOptions.stopTimestamp" | ||
52 | ></my-timestamp-input> | ||
53 | </div> | ||
54 | |||
55 | <input type="submit" i18n-value value="Save" (click)="updateTimestamps(video)"> | ||
56 | </div> | ||
57 | |||
58 | <span class="dropdown-item" (click)="removeFromPlaylist(video)"> | ||
59 | <my-global-icon iconName="delete"></my-global-icon> <ng-container i18n>Delete from {{playlist?.displayName}}</ng-container> | ||
60 | </span> | ||
61 | </div> | ||
62 | </div> | ||
63 | </div> | 10 | </div> |
64 | </div> | 11 | </div> |
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 4ac89d08f..b05af0490 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 | |||
@@ -2,100 +2,6 @@ | |||
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | @import '_miniature'; | 3 | @import '_miniature'; |
4 | 4 | ||
5 | .video, .cdk-drag-preview { | ||
6 | display: flex; | ||
7 | align-items: center; | ||
8 | background-color: var(--mainBackgroundColor); | ||
9 | cursor: pointer; | ||
10 | padding: 10px; | ||
11 | border-bottom: 1px solid $separator-border-color; | ||
12 | |||
13 | &:hover { | ||
14 | background-color: rgba(0, 0, 0, 0.05); | ||
15 | |||
16 | .more { | ||
17 | display: block; | ||
18 | } | ||
19 | } | ||
20 | |||
21 | .position { | ||
22 | font-weight: $font-semibold; | ||
23 | margin-right: 10px; | ||
24 | color: $grey-foreground-color; | ||
25 | min-width: 20px; | ||
26 | } | ||
27 | |||
28 | my-video-thumbnail { | ||
29 | display: flex; // Avoids an issue with line-height that adds space below the element | ||
30 | margin-right: 10px; | ||
31 | |||
32 | /deep/ .video-thumbnail { | ||
33 | @include miniature-thumbnail(130px, 72px); | ||
34 | } | ||
35 | } | ||
36 | |||
37 | .video-info { | ||
38 | display: flex; | ||
39 | flex-direction: column; | ||
40 | |||
41 | a { | ||
42 | @include disable-default-a-behaviour; | ||
43 | |||
44 | color: var(--mainForegroundColor); | ||
45 | } | ||
46 | |||
47 | .video-info-name { | ||
48 | font-size: 18px; | ||
49 | font-weight: $font-semibold; | ||
50 | } | ||
51 | |||
52 | .video-info-account, .video-info-timestamp { | ||
53 | color: $grey-foreground-color; | ||
54 | } | ||
55 | } | ||
56 | |||
57 | .more { | ||
58 | justify-self: flex-end; | ||
59 | margin-left: auto; | ||
60 | cursor: pointer; | ||
61 | display: none; | ||
62 | |||
63 | &.show { | ||
64 | display: block; | ||
65 | } | ||
66 | |||
67 | .icon-more { | ||
68 | @include apply-svg-color($grey-foreground-color); | ||
69 | |||
70 | &::after { | ||
71 | border: none; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | .dropdown-item { | ||
76 | @include dropdown-with-icon-item; | ||
77 | } | ||
78 | |||
79 | .timestamp-options { | ||
80 | padding-top: 0; | ||
81 | padding-left: 35px; | ||
82 | margin-bottom: 15px; | ||
83 | |||
84 | > div { | ||
85 | display: flex; | ||
86 | align-items: center; | ||
87 | } | ||
88 | |||
89 | input { | ||
90 | @include peertube-button; | ||
91 | @include orange-button; | ||
92 | |||
93 | margin-top: 10px; | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | |||
99 | // Thanks Angular CDK <3 https://material.angular.io/cdk/drag-drop/examples | 5 | // Thanks Angular CDK <3 https://material.angular.io/cdk/drag-drop/examples |
100 | .cdk-drag-preview { | 6 | .cdk-drag-preview { |
101 | box-sizing: border-box; | 7 | 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 4076a3721..dcf470be3 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,4 +1,4 @@ | |||
1 | import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core' | 1 | import { Component, OnDestroy, OnInit } from '@angular/core' |
2 | import { Notifier, ServerService } from '@app/core' | 2 | import { Notifier, ServerService } from '@app/core' |
3 | import { AuthService } from '../../core/auth' | 3 | import { AuthService } from '../../core/auth' |
4 | import { ConfirmService } from '../../core/confirm' | 4 | import { ConfirmService } from '../../core/confirm' |
@@ -10,9 +10,6 @@ import { VideoService } from '@app/shared/video/video.service' | |||
10 | import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' | 10 | import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' |
11 | import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' | 11 | import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' |
12 | import { I18n } from '@ngx-translate/i18n-polyfill' | 12 | import { I18n } from '@ngx-translate/i18n-polyfill' |
13 | import { secondsToTime } from '../../../assets/player/utils' | ||
14 | import { VideoPlaylistElementUpdate } from '@shared/models' | ||
15 | import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap' | ||
16 | import { CdkDragDrop, CdkDragMove } from '@angular/cdk/drag-drop' | 13 | import { CdkDragDrop, CdkDragMove } from '@angular/cdk/drag-drop' |
17 | import { throttleTime } from 'rxjs/operators' | 14 | import { throttleTime } from 'rxjs/operators' |
18 | 15 | ||
@@ -22,8 +19,6 @@ import { throttleTime } from 'rxjs/operators' | |||
22 | styleUrls: [ './my-account-video-playlist-elements.component.scss' ] | 19 | styleUrls: [ './my-account-video-playlist-elements.component.scss' ] |
23 | }) | 20 | }) |
24 | export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestroy { | 21 | export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestroy { |
25 | @ViewChild('moreDropdown') moreDropdown: NgbDropdown | ||
26 | |||
27 | videos: Video[] = [] | 22 | videos: Video[] = [] |
28 | playlist: VideoPlaylist | 23 | playlist: VideoPlaylist |
29 | 24 | ||
@@ -33,15 +28,6 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro | |||
33 | totalItems: null | 28 | totalItems: null |
34 | } | 29 | } |
35 | 30 | ||
36 | displayTimestampOptions = false | ||
37 | |||
38 | timestampOptions: { | ||
39 | startTimestampEnabled: boolean | ||
40 | startTimestamp: number | ||
41 | stopTimestampEnabled: boolean | ||
42 | stopTimestamp: number | ||
43 | } = {} as any | ||
44 | |||
45 | private videoPlaylistId: string | number | 31 | private videoPlaylistId: string | number |
46 | private paramsSub: Subscription | 32 | private paramsSub: Subscription |
47 | private dragMoveSubject = new Subject<number>() | 33 | private dragMoveSubject = new Subject<number>() |
@@ -124,45 +110,9 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro | |||
124 | // } | 110 | // } |
125 | } | 111 | } |
126 | 112 | ||
127 | isVideoBlur (video: Video) { | 113 | onElementRemoved (video: Video) { |
128 | return video.isVideoNSFWForUser(this.authService.getUser(), this.serverService.getConfig()) | 114 | this.videos = this.videos.filter(v => v.id !== video.id) |
129 | } | 115 | this.reorderClientPositions() |
130 | |||
131 | removeFromPlaylist (video: Video) { | ||
132 | this.videoPlaylistService.removeVideoFromPlaylist(this.playlist.id, video.id) | ||
133 | .subscribe( | ||
134 | () => { | ||
135 | this.notifier.success(this.i18n('Video removed from {{name}}', { name: this.playlist.displayName })) | ||
136 | |||
137 | this.videos = this.videos.filter(v => v.id !== video.id) | ||
138 | this.reorderClientPositions() | ||
139 | }, | ||
140 | |||
141 | err => this.notifier.error(err.message) | ||
142 | ) | ||
143 | |||
144 | this.moreDropdown.close() | ||
145 | } | ||
146 | |||
147 | updateTimestamps (video: Video) { | ||
148 | const body: VideoPlaylistElementUpdate = {} | ||
149 | |||
150 | body.startTimestamp = this.timestampOptions.startTimestampEnabled ? this.timestampOptions.startTimestamp : null | ||
151 | body.stopTimestamp = this.timestampOptions.stopTimestampEnabled ? this.timestampOptions.stopTimestamp : null | ||
152 | |||
153 | this.videoPlaylistService.updateVideoOfPlaylist(this.playlist.id, video.id, body) | ||
154 | .subscribe( | ||
155 | () => { | ||
156 | this.notifier.success(this.i18n('Timestamps updated')) | ||
157 | |||
158 | video.playlistElement.startTimestamp = body.startTimestamp | ||
159 | video.playlistElement.stopTimestamp = body.stopTimestamp | ||
160 | }, | ||
161 | |||
162 | err => this.notifier.error(err.message) | ||
163 | ) | ||
164 | |||
165 | this.moreDropdown.close() | ||
166 | } | 116 | } |
167 | 117 | ||
168 | onNearOfBottom () { | 118 | onNearOfBottom () { |
@@ -173,50 +123,6 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro | |||
173 | this.loadElements() | 123 | this.loadElements() |
174 | } | 124 | } |
175 | 125 | ||
176 | formatTimestamp (video: Video) { | ||
177 | const start = video.playlistElement.startTimestamp | ||
178 | const stop = video.playlistElement.stopTimestamp | ||
179 | |||
180 | const startFormatted = secondsToTime(start, true, ':') | ||
181 | const stopFormatted = secondsToTime(stop, true, ':') | ||
182 | |||
183 | if (start === null && stop === null) return '' | ||
184 | |||
185 | if (start !== null && stop === null) return this.i18n('Starts at ') + startFormatted | ||
186 | if (start === null && stop !== null) return this.i18n('Stops at ') + stopFormatted | ||
187 | |||
188 | return this.i18n('Starts at ') + startFormatted + this.i18n(' and stops at ') + stopFormatted | ||
189 | } | ||
190 | |||
191 | onDropdownOpenChange () { | ||
192 | this.displayTimestampOptions = false | ||
193 | } | ||
194 | |||
195 | toggleDisplayTimestampsOptions (event: Event, video: Video) { | ||
196 | event.preventDefault() | ||
197 | |||
198 | this.displayTimestampOptions = !this.displayTimestampOptions | ||
199 | |||
200 | if (this.displayTimestampOptions === true) { | ||
201 | this.timestampOptions = { | ||
202 | startTimestampEnabled: false, | ||
203 | stopTimestampEnabled: false, | ||
204 | startTimestamp: 0, | ||
205 | stopTimestamp: video.duration | ||
206 | } | ||
207 | |||
208 | if (video.playlistElement.startTimestamp) { | ||
209 | this.timestampOptions.startTimestampEnabled = true | ||
210 | this.timestampOptions.startTimestamp = video.playlistElement.startTimestamp | ||
211 | } | ||
212 | |||
213 | if (video.playlistElement.stopTimestamp) { | ||
214 | this.timestampOptions.stopTimestampEnabled = true | ||
215 | this.timestampOptions.stopTimestamp = video.playlistElement.stopTimestamp | ||
216 | } | ||
217 | } | ||
218 | } | ||
219 | |||
220 | private loadElements () { | 126 | private loadElements () { |
221 | this.videoService.getPlaylistVideos(this.videoPlaylistId, this.pagination) | 127 | this.videoService.getPlaylistVideos(this.videoPlaylistId, this.pagination) |
222 | .subscribe(({ totalVideos, videos }) => { | 128 | .subscribe(({ totalVideos, videos }) => { |