diff options
author | Chocobozzz <florian.bigard@gmail.com> | 2017-12-06 15:07:17 +0100 |
---|---|---|
committer | Chocobozzz <florian.bigard@gmail.com> | 2017-12-06 15:07:17 +0100 |
commit | ce0e281d46a7b574dcccb47958743656532bd312 (patch) | |
tree | b2d26aef176f39ebdf37e5e7c28d6f6fb905bf10 /client/src/app/account/account-videos | |
parent | 7d763d97497df1bbf7a01f61aa916d99a1338a33 (diff) | |
download | PeerTube-ce0e281d46a7b574dcccb47958743656532bd312.tar.gz PeerTube-ce0e281d46a7b574dcccb47958743656532bd312.tar.zst PeerTube-ce0e281d46a7b574dcccb47958743656532bd312.zip |
Client bulk delete
Diffstat (limited to 'client/src/app/account/account-videos')
3 files changed, 140 insertions, 47 deletions
diff --git a/client/src/app/account/account-videos/account-videos.component.html b/client/src/app/account/account-videos/account-videos.component.html index 30db69429..030c2f19c 100644 --- a/client/src/app/account/account-videos/account-videos.component.html +++ b/client/src/app/account/account-videos/account-videos.component.html | |||
@@ -5,7 +5,9 @@ | |||
5 | (scrolled)="onNearOfBottom()" | 5 | (scrolled)="onNearOfBottom()" |
6 | (scrolledUp)="onNearOfTop()" | 6 | (scrolledUp)="onNearOfTop()" |
7 | > | 7 | > |
8 | <div class="video" *ngFor="let video of videos"> | 8 | <div class="video" *ngFor="let video of videos; let i = index"> |
9 | <input type="checkbox" [(ngModel)]="checkedVideos[video.id]" /> | ||
10 | |||
9 | <my-video-thumbnail [video]="video"></my-video-thumbnail> | 11 | <my-video-thumbnail [video]="video"></my-video-thumbnail> |
10 | 12 | ||
11 | <div class="video-info"> | 13 | <div class="video-info"> |
@@ -13,14 +15,30 @@ | |||
13 | <span class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span> | 15 | <span class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span> |
14 | </div> | 16 | </div> |
15 | 17 | ||
16 | <a class="action-button action-button-delete" (click)="deleteVideo(video)"> | 18 | <!-- Display only once --> |
17 | <span class="icon icon-delete"></span> | 19 | <div class="action-selection-mode" *ngIf="isInSelectionMode() === true && i === 0"> |
18 | Delete | 20 | <div class="action-selection-mode-child"> |
19 | </a> | 21 | <span class="action-button" (click)="abortSelectionMode()"> |
22 | Cancel | ||
23 | </span> | ||
24 | |||
25 | <span class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()"> | ||
26 | <span class="icon icon-delete-white"></span> | ||
27 | Delete | ||
28 | </span> | ||
29 | </div> | ||
30 | </div> | ||
31 | |||
32 | <ng-template [ngIf]="isInSelectionMode() === false"> | ||
33 | <span class="action-button action-button-delete" (click)="deleteVideo(video)"> | ||
34 | <span class="icon icon-delete-grey"></span> | ||
35 | Delete | ||
36 | </span> | ||
20 | 37 | ||
21 | <a class="action-button" [routerLink]="[ '/videos', video.id, '/edit' ]"> | 38 | <a class="action-button" [routerLink]="[ '/videos', 'edit', video.uuid ]"> |
22 | <span class="icon icon-edit"></span> | 39 | <span class="icon icon-edit"></span> |
23 | Edit | 40 | Edit |
24 | </a> | 41 | </a> |
42 | </ng-template> | ||
25 | </div> | 43 | </div> |
26 | </div> | 44 | </div> |
diff --git a/client/src/app/account/account-videos/account-videos.component.scss b/client/src/app/account/account-videos/account-videos.component.scss index e7fe662b1..e76e3f4e5 100644 --- a/client/src/app/account/account-videos/account-videos.component.scss +++ b/client/src/app/account/account-videos/account-videos.component.scss | |||
@@ -1,8 +1,74 @@ | |||
1 | .action-selection-mode { | ||
2 | width: 174px; | ||
3 | |||
4 | .action-selection-mode-child { | ||
5 | position: fixed; | ||
6 | } | ||
7 | } | ||
8 | |||
9 | .action-button { | ||
10 | @include peertube-button-link; | ||
11 | |||
12 | font-size: 15px; | ||
13 | font-weight: $font-semibold; | ||
14 | color: #585858; | ||
15 | background-color: #E5E5E5; | ||
16 | |||
17 | &:hover { | ||
18 | background-color: #EFEFEF; | ||
19 | } | ||
20 | |||
21 | &.action-button-delete { | ||
22 | margin-right: 10px; | ||
23 | } | ||
24 | |||
25 | &.action-button-delete-selection { | ||
26 | background-color: $orange-color; | ||
27 | color: #fff; | ||
28 | |||
29 | &:hover { | ||
30 | background-color: $orange-hoover-color; | ||
31 | } | ||
32 | } | ||
33 | |||
34 | .icon { | ||
35 | display: inline-block; | ||
36 | background-repeat: no-repeat; | ||
37 | background-size: contain; | ||
38 | width: 21px; | ||
39 | height: 21px; | ||
40 | vertical-align: middle; | ||
41 | position: relative; | ||
42 | top: -2px; | ||
43 | |||
44 | &.icon-edit { | ||
45 | background-image: url('../../../assets/images/account/edit.svg'); | ||
46 | } | ||
47 | |||
48 | &.icon-delete-grey { | ||
49 | background-image: url('../../../assets/images/account/delete-grey.svg'); | ||
50 | } | ||
51 | |||
52 | &.icon-delete-white { | ||
53 | background-image: url('../../../assets/images/account/delete-white.svg'); | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | |||
1 | .video { | 58 | .video { |
2 | display: flex; | 59 | display: flex; |
3 | height: 130px; | 60 | height: 130px; |
4 | padding-bottom: 20px; | 61 | padding-bottom: 20px; |
5 | 62 | ||
63 | input[type=checkbox] { | ||
64 | margin-right: 20px; | ||
65 | outline: 0; | ||
66 | } | ||
67 | |||
68 | &:first-child { | ||
69 | margin-top: 47px; | ||
70 | } | ||
71 | |||
6 | &:not(:last-child) { | 72 | &:not(:last-child) { |
7 | margin-bottom: 20px; | 73 | margin-bottom: 20px; |
8 | border-bottom: 1px solid #C6C6C6; | 74 | border-bottom: 1px solid #C6C6C6; |
@@ -24,40 +90,4 @@ | |||
24 | font-size: 13px; | 90 | font-size: 13px; |
25 | } | 91 | } |
26 | } | 92 | } |
27 | |||
28 | .action-button { | ||
29 | @include peertube-button-link; | ||
30 | |||
31 | font-size: 15px; | ||
32 | font-weight: $font-semibold; | ||
33 | color: #585858; | ||
34 | background-color: #E5E5E5; | ||
35 | |||
36 | &:hover { | ||
37 | background-color: #EFEFEF; | ||
38 | } | ||
39 | |||
40 | &.action-button-delete { | ||
41 | margin-right: 10px; | ||
42 | } | ||
43 | |||
44 | .icon.icon-edit, .icon.icon-delete { | ||
45 | display: inline-block; | ||
46 | background-repeat: no-repeat; | ||
47 | background-size: contain; | ||
48 | width: 21px; | ||
49 | height: 21px; | ||
50 | vertical-align: middle; | ||
51 | position: relative; | ||
52 | top: -2px; | ||
53 | |||
54 | &.icon-edit { | ||
55 | background-image: url('../../../assets/images/account/edit.svg'); | ||
56 | } | ||
57 | |||
58 | &.icon-delete { | ||
59 | background-image: url('../../../assets/images/account/delete.svg'); | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | } | 93 | } |
diff --git a/client/src/app/account/account-videos/account-videos.component.ts b/client/src/app/account/account-videos/account-videos.component.ts index 9c2cc2404..5f12cfce0 100644 --- a/client/src/app/account/account-videos/account-videos.component.ts +++ b/client/src/app/account/account-videos/account-videos.component.ts | |||
@@ -1,6 +1,9 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
2 | import { ActivatedRoute, Router } from '@angular/router' | 2 | import { ActivatedRoute, Router } from '@angular/router' |
3 | import { NotificationsService } from 'angular2-notifications' | 3 | import { NotificationsService } from 'angular2-notifications' |
4 | import 'rxjs/add/observable/from' | ||
5 | import 'rxjs/add/operator/concatAll' | ||
6 | import { Observable } from 'rxjs/Observable' | ||
4 | import { ConfirmService } from '../../core/confirm' | 7 | import { ConfirmService } from '../../core/confirm' |
5 | import { AbstractVideoList } from '../../shared/video/abstract-video-list' | 8 | import { AbstractVideoList } from '../../shared/video/abstract-video-list' |
6 | import { Video } from '../../shared/video/video.model' | 9 | import { Video } from '../../shared/video/video.model' |
@@ -14,6 +17,7 @@ import { VideoService } from '../../shared/video/video.service' | |||
14 | export class AccountVideosComponent extends AbstractVideoList implements OnInit { | 17 | export class AccountVideosComponent extends AbstractVideoList implements OnInit { |
15 | titlePage = 'My videos' | 18 | titlePage = 'My videos' |
16 | currentRoute = '/account/videos' | 19 | currentRoute = '/account/videos' |
20 | checkedVideos: { [ id: number ]: boolean } = {} | ||
17 | 21 | ||
18 | constructor (protected router: Router, | 22 | constructor (protected router: Router, |
19 | protected route: ActivatedRoute, | 23 | protected route: ActivatedRoute, |
@@ -27,10 +31,47 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit | |||
27 | super.ngOnInit() | 31 | super.ngOnInit() |
28 | } | 32 | } |
29 | 33 | ||
34 | abortSelectionMode () { | ||
35 | this.checkedVideos = {} | ||
36 | } | ||
37 | |||
38 | isInSelectionMode () { | ||
39 | return Object.keys(this.checkedVideos).some(k => this.checkedVideos[k] === true) | ||
40 | } | ||
41 | |||
30 | getVideosObservable () { | 42 | getVideosObservable () { |
31 | return this.videoService.getMyVideos(this.pagination, this.sort) | 43 | return this.videoService.getMyVideos(this.pagination, this.sort) |
32 | } | 44 | } |
33 | 45 | ||
46 | deleteSelectedVideos () { | ||
47 | const toDeleteVideosIds = Object.keys(this.checkedVideos) | ||
48 | .filter(k => this.checkedVideos[k] === true) | ||
49 | .map(k => parseInt(k, 10)) | ||
50 | |||
51 | this.confirmService.confirm(`Do you really want to delete ${toDeleteVideosIds.length} videos?`, 'Delete').subscribe( | ||
52 | res => { | ||
53 | if (res === false) return | ||
54 | |||
55 | const observables: Observable<any>[] = [] | ||
56 | for (const videoId of toDeleteVideosIds) { | ||
57 | const o = this.videoService | ||
58 | .removeVideo(videoId) | ||
59 | .do(() => this.spliceVideosById(videoId)) | ||
60 | |||
61 | observables.push(o) | ||
62 | } | ||
63 | |||
64 | Observable.from(observables) | ||
65 | .concatAll() | ||
66 | .subscribe( | ||
67 | res => this.notificationsService.success('Success', `${toDeleteVideosIds.length} videos deleted.`), | ||
68 | |||
69 | err => this.notificationsService.error('Error', err.text) | ||
70 | ) | ||
71 | } | ||
72 | ) | ||
73 | } | ||
74 | |||
34 | deleteVideo (video: Video) { | 75 | deleteVideo (video: Video) { |
35 | this.confirmService.confirm(`Do you really want to delete ${video.name}?`, 'Delete').subscribe( | 76 | this.confirmService.confirm(`Do you really want to delete ${video.name}?`, 'Delete').subscribe( |
36 | res => { | 77 | res => { |
@@ -40,8 +81,7 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit | |||
40 | .subscribe( | 81 | .subscribe( |
41 | status => { | 82 | status => { |
42 | this.notificationsService.success('Success', `Video ${video.name} deleted.`) | 83 | this.notificationsService.success('Success', `Video ${video.name} deleted.`) |
43 | const index = this.videos.findIndex(v => v.id === video.id) | 84 | this.spliceVideosById(video.id) |
44 | this.videos.splice(index, 1) | ||
45 | }, | 85 | }, |
46 | 86 | ||
47 | error => this.notificationsService.error('Error', error.text) | 87 | error => this.notificationsService.error('Error', error.text) |
@@ -49,4 +89,9 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit | |||
49 | } | 89 | } |
50 | ) | 90 | ) |
51 | } | 91 | } |
92 | |||
93 | private spliceVideosById (id: number) { | ||
94 | const index = this.videos.findIndex(v => v.id === id) | ||
95 | this.videos.splice(index, 1) | ||
96 | } | ||
52 | } | 97 | } |