diff options
Diffstat (limited to 'client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.ts')
-rw-r--r-- | client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.ts | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.ts b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.ts new file mode 100644 index 000000000..a8fdf6e29 --- /dev/null +++ b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.ts | |||
@@ -0,0 +1,197 @@ | |||
1 | import { Subject, Subscription } from 'rxjs' | ||
2 | import { CdkDragDrop } from '@angular/cdk/drag-drop' | ||
3 | import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core' | ||
4 | import { ActivatedRoute, Router } from '@angular/router' | ||
5 | import { ComponentPagination, ConfirmService, Notifier, ScreenService } from '@app/core' | ||
6 | import { DropdownAction } from '@app/shared/shared-main' | ||
7 | import { VideoShareComponent } from '@app/shared/shared-share-modal' | ||
8 | import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist' | ||
9 | import { VideoPlaylistType } from '@shared/models' | ||
10 | |||
11 | @Component({ | ||
12 | templateUrl: './my-video-playlist-elements.component.html', | ||
13 | styleUrls: [ './my-video-playlist-elements.component.scss' ] | ||
14 | }) | ||
15 | export class MyVideoPlaylistElementsComponent implements OnInit, OnDestroy { | ||
16 | @ViewChild('videoShareModal') videoShareModal: VideoShareComponent | ||
17 | |||
18 | playlistElements: VideoPlaylistElement[] = [] | ||
19 | playlist: VideoPlaylist | ||
20 | |||
21 | playlistActions: DropdownAction<VideoPlaylist>[][] = [] | ||
22 | |||
23 | pagination: ComponentPagination = { | ||
24 | currentPage: 1, | ||
25 | itemsPerPage: 10, | ||
26 | totalItems: null | ||
27 | } | ||
28 | |||
29 | onDataSubject = new Subject<any[]>() | ||
30 | |||
31 | private videoPlaylistId: string | number | ||
32 | private paramsSub: Subscription | ||
33 | |||
34 | constructor ( | ||
35 | private notifier: Notifier, | ||
36 | private router: Router, | ||
37 | private confirmService: ConfirmService, | ||
38 | private route: ActivatedRoute, | ||
39 | private screenService: ScreenService, | ||
40 | private videoPlaylistService: VideoPlaylistService | ||
41 | ) {} | ||
42 | |||
43 | ngOnInit () { | ||
44 | this.playlistActions = [ | ||
45 | [ | ||
46 | { | ||
47 | label: $localize`Update playlist`, | ||
48 | iconName: 'edit', | ||
49 | linkBuilder: playlist => [ '/my-library', 'video-playlists', 'update', playlist.uuid ] | ||
50 | }, | ||
51 | { | ||
52 | label: $localize`Delete playlist`, | ||
53 | iconName: 'delete', | ||
54 | handler: playlist => this.deleteVideoPlaylist(playlist) | ||
55 | } | ||
56 | ] | ||
57 | ] | ||
58 | |||
59 | this.paramsSub = this.route.params.subscribe(routeParams => { | ||
60 | this.videoPlaylistId = routeParams[ 'videoPlaylistId' ] | ||
61 | this.loadElements() | ||
62 | |||
63 | this.loadPlaylistInfo() | ||
64 | }) | ||
65 | } | ||
66 | |||
67 | ngOnDestroy () { | ||
68 | if (this.paramsSub) this.paramsSub.unsubscribe() | ||
69 | } | ||
70 | |||
71 | drop (event: CdkDragDrop<any>) { | ||
72 | const previousIndex = event.previousIndex | ||
73 | const newIndex = event.currentIndex | ||
74 | |||
75 | if (previousIndex === newIndex) return | ||
76 | |||
77 | const oldPosition = this.playlistElements[previousIndex].position | ||
78 | let insertAfter = this.playlistElements[newIndex].position | ||
79 | |||
80 | if (oldPosition > insertAfter) insertAfter-- | ||
81 | |||
82 | const element = this.playlistElements[previousIndex] | ||
83 | |||
84 | this.playlistElements.splice(previousIndex, 1) | ||
85 | this.playlistElements.splice(newIndex, 0, element) | ||
86 | |||
87 | this.videoPlaylistService.reorderPlaylist(this.playlist.id, oldPosition, insertAfter) | ||
88 | .subscribe( | ||
89 | () => { | ||
90 | this.reorderClientPositions() | ||
91 | }, | ||
92 | |||
93 | err => this.notifier.error(err.message) | ||
94 | ) | ||
95 | } | ||
96 | |||
97 | onElementRemoved (element: VideoPlaylistElement) { | ||
98 | const oldFirst = this.findFirst() | ||
99 | |||
100 | this.playlistElements = this.playlistElements.filter(v => v.id !== element.id) | ||
101 | this.reorderClientPositions(oldFirst) | ||
102 | } | ||
103 | |||
104 | onNearOfBottom () { | ||
105 | // Last page | ||
106 | if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return | ||
107 | |||
108 | this.pagination.currentPage += 1 | ||
109 | this.loadElements() | ||
110 | } | ||
111 | |||
112 | trackByFn (index: number, elem: VideoPlaylistElement) { | ||
113 | return elem.id | ||
114 | } | ||
115 | |||
116 | isRegularPlaylist (playlist: VideoPlaylist) { | ||
117 | return playlist?.type.id === VideoPlaylistType.REGULAR | ||
118 | } | ||
119 | |||
120 | showShareModal () { | ||
121 | this.videoShareModal.show() | ||
122 | } | ||
123 | |||
124 | async deleteVideoPlaylist (videoPlaylist: VideoPlaylist) { | ||
125 | const res = await this.confirmService.confirm( | ||
126 | $localize`Do you really want to delete ${videoPlaylist.displayName}?`, | ||
127 | $localize`Delete` | ||
128 | ) | ||
129 | if (res === false) return | ||
130 | |||
131 | this.videoPlaylistService.removeVideoPlaylist(videoPlaylist) | ||
132 | .subscribe( | ||
133 | () => { | ||
134 | this.router.navigate([ '/my-library', 'video-playlists' ]) | ||
135 | this.notifier.success($localize`Playlist ${videoPlaylist.displayName} deleted.`) | ||
136 | }, | ||
137 | |||
138 | error => this.notifier.error(error.message) | ||
139 | ) | ||
140 | } | ||
141 | |||
142 | /** | ||
143 | * Returns null to not have drag and drop delay. | ||
144 | * In small views, where elements are about 100% wide, | ||
145 | * we add a delay to prevent unwanted drag&drop. | ||
146 | * | ||
147 | * @see {@link https://github.com/Chocobozzz/PeerTube/issues/2078} | ||
148 | * | ||
149 | * @returns {null|number} Null for no delay, or a number in milliseconds. | ||
150 | */ | ||
151 | getDragStartDelay (): null | number { | ||
152 | if (this.screenService.isInTouchScreen()) { | ||
153 | return 500 | ||
154 | } | ||
155 | |||
156 | return null | ||
157 | } | ||
158 | |||
159 | private loadElements () { | ||
160 | this.videoPlaylistService.getPlaylistVideos(this.videoPlaylistId, this.pagination) | ||
161 | .subscribe(({ total, data }) => { | ||
162 | this.playlistElements = this.playlistElements.concat(data) | ||
163 | this.pagination.totalItems = total | ||
164 | |||
165 | this.onDataSubject.next(data) | ||
166 | }) | ||
167 | } | ||
168 | |||
169 | private loadPlaylistInfo () { | ||
170 | this.videoPlaylistService.getVideoPlaylist(this.videoPlaylistId) | ||
171 | .subscribe(playlist => { | ||
172 | this.playlist = playlist | ||
173 | }) | ||
174 | } | ||
175 | |||
176 | private reorderClientPositions (first?: VideoPlaylistElement) { | ||
177 | if (this.playlistElements.length === 0) return | ||
178 | |||
179 | const oldFirst = first || this.findFirst() | ||
180 | let i = 1 | ||
181 | |||
182 | for (const element of this.playlistElements) { | ||
183 | element.position = i | ||
184 | i++ | ||
185 | } | ||
186 | |||
187 | // Reload playlist thumbnail if the first element changed | ||
188 | const newFirst = this.findFirst() | ||
189 | if (oldFirst && newFirst && oldFirst.id !== newFirst.id) { | ||
190 | this.playlist.refreshThumbnail() | ||
191 | } | ||
192 | } | ||
193 | |||
194 | private findFirst () { | ||
195 | return this.playlistElements.find(e => e.position === 1) | ||
196 | } | ||
197 | } | ||