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