Client bulk delete
authorChocobozzz <florian.bigard@gmail.com>
Wed, 6 Dec 2017 14:07:17 +0000 (15:07 +0100)
committerChocobozzz <florian.bigard@gmail.com>
Wed, 6 Dec 2017 14:07:17 +0000 (15:07 +0100)
client/src/app/account/account-videos/account-videos.component.html
client/src/app/account/account-videos/account-videos.component.scss
client/src/app/account/account-videos/account-videos.component.ts
client/src/assets/images/account/delete-grey.svg [new file with mode: 0644]
client/src/assets/images/account/delete-white.svg [new file with mode: 0644]
client/src/assets/images/account/delete.svg [deleted file]
client/src/sass/_mixins.scss
client/src/sass/_variables.scss

index 30db694294775e320f6f14371c1a584f1afc711f..030c2f19c6244a39c10b7e2799b04de126f8e4e9 100644 (file)
@@ -5,7 +5,9 @@
   (scrolled)="onNearOfBottom()"
   (scrolledUp)="onNearOfTop()"
 >
-  <div class="video" *ngFor="let video of videos">
+  <div class="video" *ngFor="let video of videos; let i = index">
+    <input type="checkbox" [(ngModel)]="checkedVideos[video.id]" />
+
     <my-video-thumbnail [video]="video"></my-video-thumbnail>
 
     <div class="video-info">
       <span class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span>
     </div>
 
-    <a class="action-button action-button-delete" (click)="deleteVideo(video)">
-      <span class="icon icon-delete"></span>
-      Delete
-    </a>
+    <!-- Display only once -->
+    <div class="action-selection-mode" *ngIf="isInSelectionMode() === true && i === 0">
+      <div class="action-selection-mode-child">
+        <span class="action-button" (click)="abortSelectionMode()">
+          Cancel
+        </span>
+
+            <span class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()">
+          <span class="icon icon-delete-white"></span>
+          Delete
+        </span>
+      </div>
+    </div>
+
+    <ng-template [ngIf]="isInSelectionMode() === false">
+      <span class="action-button action-button-delete" (click)="deleteVideo(video)">
+        <span class="icon icon-delete-grey"></span>
+        Delete
+      </span>
 
-    <a class="action-button" [routerLink]="[ '/videos', video.id, '/edit' ]">
-      <span class="icon icon-edit"></span>
-      Edit
-    </a>
+      <a class="action-button" [routerLink]="[ '/videos', 'edit', video.uuid ]">
+        <span class="icon icon-edit"></span>
+        Edit
+      </a>
+    </ng-template>
   </div>
 </div>
index e7fe662b1ce29376eb270c047bd9bdcb6860585f..e76e3f4e589981f5631c9578e65ff163f2a59355 100644 (file)
@@ -1,8 +1,74 @@
+.action-selection-mode {
+  width: 174px;
+
+  .action-selection-mode-child {
+    position: fixed;
+  }
+}
+
+.action-button {
+  @include peertube-button-link;
+
+  font-size: 15px;
+  font-weight: $font-semibold;
+  color: #585858;
+  background-color: #E5E5E5;
+
+  &:hover {
+    background-color: #EFEFEF;
+  }
+
+  &.action-button-delete {
+    margin-right: 10px;
+  }
+
+  &.action-button-delete-selection {
+    background-color: $orange-color;
+    color: #fff;
+
+    &:hover {
+      background-color: $orange-hoover-color;
+    }
+  }
+
+  .icon {
+    display: inline-block;
+    background-repeat: no-repeat;
+    background-size: contain;
+    width: 21px;
+    height: 21px;
+    vertical-align: middle;
+    position: relative;
+    top: -2px;
+
+    &.icon-edit {
+      background-image: url('../../../assets/images/account/edit.svg');
+    }
+
+    &.icon-delete-grey {
+      background-image: url('../../../assets/images/account/delete-grey.svg');
+    }
+
+    &.icon-delete-white {
+      background-image: url('../../../assets/images/account/delete-white.svg');
+    }
+  }
+}
+
 .video {
   display: flex;
   height: 130px;
   padding-bottom: 20px;
 
+  input[type=checkbox] {
+    margin-right: 20px;
+    outline: 0;
+  }
+
+  &:first-child {
+    margin-top: 47px;
+  }
+
   &:not(:last-child) {
     margin-bottom: 20px;
     border-bottom: 1px solid #C6C6C6;
       font-size: 13px;
     }
   }
-
-  .action-button {
-    @include peertube-button-link;
-
-    font-size: 15px;
-    font-weight: $font-semibold;
-    color: #585858;
-    background-color: #E5E5E5;
-
-    &:hover {
-      background-color: #EFEFEF;
-    }
-
-    &.action-button-delete {
-      margin-right: 10px;
-    }
-
-    .icon.icon-edit, .icon.icon-delete {
-      display: inline-block;
-      background-repeat: no-repeat;
-      background-size: contain;
-      width: 21px;
-      height: 21px;
-      vertical-align: middle;
-      position: relative;
-      top: -2px;
-
-      &.icon-edit {
-        background-image: url('../../../assets/images/account/edit.svg');
-      }
-
-      &.icon-delete {
-        background-image: url('../../../assets/images/account/delete.svg');
-      }
-    }
-  }
 }
index 9c2cc24045a06adcaf6f8a1b9df64a04b96b3b9e..5f12cfce0c270cfa51d5a21ca72c856b6de4ae5f 100644 (file)
@@ -1,6 +1,9 @@
 import { Component, OnInit } from '@angular/core'
 import { ActivatedRoute, Router } from '@angular/router'
 import { NotificationsService } from 'angular2-notifications'
+import 'rxjs/add/observable/from'
+import 'rxjs/add/operator/concatAll'
+import { Observable } from 'rxjs/Observable'
 import { ConfirmService } from '../../core/confirm'
 import { AbstractVideoList } from '../../shared/video/abstract-video-list'
 import { Video } from '../../shared/video/video.model'
@@ -14,6 +17,7 @@ import { VideoService } from '../../shared/video/video.service'
 export class AccountVideosComponent extends AbstractVideoList implements OnInit {
   titlePage = 'My videos'
   currentRoute = '/account/videos'
+  checkedVideos: { [ id: number ]: boolean } = {}
 
   constructor (protected router: Router,
                protected route: ActivatedRoute,
@@ -27,10 +31,47 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit
     super.ngOnInit()
   }
 
+  abortSelectionMode () {
+    this.checkedVideos = {}
+  }
+
+  isInSelectionMode () {
+    return Object.keys(this.checkedVideos).some(k => this.checkedVideos[k] === true)
+  }
+
   getVideosObservable () {
     return this.videoService.getMyVideos(this.pagination, this.sort)
   }
 
+  deleteSelectedVideos () {
+    const toDeleteVideosIds = Object.keys(this.checkedVideos)
+      .filter(k => this.checkedVideos[k] === true)
+      .map(k => parseInt(k, 10))
+
+    this.confirmService.confirm(`Do you really want to delete ${toDeleteVideosIds.length} videos?`, 'Delete').subscribe(
+      res => {
+        if (res === false) return
+
+        const observables: Observable<any>[] = []
+        for (const videoId of toDeleteVideosIds) {
+          const o = this.videoService
+            .removeVideo(videoId)
+            .do(() => this.spliceVideosById(videoId))
+
+          observables.push(o)
+        }
+
+        Observable.from(observables)
+          .concatAll()
+          .subscribe(
+            res => this.notificationsService.success('Success', `${toDeleteVideosIds.length} videos deleted.`),
+
+          err => this.notificationsService.error('Error', err.text)
+          )
+      }
+    )
+  }
+
   deleteVideo (video: Video) {
     this.confirmService.confirm(`Do you really want to delete ${video.name}?`, 'Delete').subscribe(
       res => {
@@ -40,8 +81,7 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit
           .subscribe(
             status => {
               this.notificationsService.success('Success', `Video ${video.name} deleted.`)
-              const index = this.videos.findIndex(v => v.id === video.id)
-              this.videos.splice(index, 1)
+              this.spliceVideosById(video.id)
             },
 
             error => this.notificationsService.error('Error', error.text)
@@ -49,4 +89,9 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit
       }
     )
   }
+
+  private spliceVideosById (id: number) {
+    const index = this.videos.findIndex(v => v.id === id)
+    this.videos.splice(index, 1)
+  }
 }
diff --git a/client/src/assets/images/account/delete-grey.svg b/client/src/assets/images/account/delete-grey.svg
new file mode 100644 (file)
index 0000000..67e9e2c
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Artboard-4" transform="translate(-224.000000, -159.000000)">
+            <g id="25" transform="translate(224.000000, 159.000000)">
+                <path d="M5,7 L5,20.0081158 C5,21.1082031 5.89706013,22 7.00585866,22 L16.9941413,22 C18.1019465,22 19,21.1066027 19,20.0081158 L19,7" id="Path-296" stroke="#585858" stroke-width="2"></path>
+                <rect id="Rectangle-424" fill="#585858" x="2" y="4" width="20" height="2" rx="1"></rect>
+                <path d="M9,10.9970301 C9,10.4463856 9.44386482,10 10,10 C10.5522847,10 11,10.4530363 11,10.9970301 L11,17.0029699 C11,17.5536144 10.5561352,18 10,18 C9.44771525,18 9,17.5469637 9,17.0029699 L9,10.9970301 Z M13,10.9970301 C13,10.4463856 13.4438648,10 14,10 C14.5522847,10 15,10.4530363 15,10.9970301 L15,17.0029699 C15,17.5536144 14.5561352,18 14,18 C13.4477153,18 13,17.5469637 13,17.0029699 L13,10.9970301 Z" id="Combined-Shape" fill="#585858"></path>
+                <path d="M9,5 L9,2.99895656 C9,2.44724809 9.45097518,2 9.99077797,2 L14.009222,2 C14.5564136,2 15,2.44266033 15,2.99895656 L15,5" id="Path-33" stroke="#585858" stroke-width="2" stroke-linejoin="round"></path>
+            </g>
+        </g>
+    </g>
+</svg>
diff --git a/client/src/assets/images/account/delete-white.svg b/client/src/assets/images/account/delete-white.svg
new file mode 100644 (file)
index 0000000..9c52de5
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Artboard-4" transform="translate(-224.000000, -159.000000)">
+            <g id="25" transform="translate(224.000000, 159.000000)">
+                <path d="M5,7 L5,20.0081158 C5,21.1082031 5.89706013,22 7.00585866,22 L16.9941413,22 C18.1019465,22 19,21.1066027 19,20.0081158 L19,7" id="Path-296" stroke="#ffffff" stroke-width="2"></path>
+                <rect id="Rectangle-424" fill="#ffffff" x="2" y="4" width="20" height="2" rx="1"></rect>
+                <path d="M9,10.9970301 C9,10.4463856 9.44386482,10 10,10 C10.5522847,10 11,10.4530363 11,10.9970301 L11,17.0029699 C11,17.5536144 10.5561352,18 10,18 C9.44771525,18 9,17.5469637 9,17.0029699 L9,10.9970301 Z M13,10.9970301 C13,10.4463856 13.4438648,10 14,10 C14.5522847,10 15,10.4530363 15,10.9970301 L15,17.0029699 C15,17.5536144 14.5561352,18 14,18 C13.4477153,18 13,17.5469637 13,17.0029699 L13,10.9970301 Z" id="Combined-Shape" fill="#ffffff"></path>
+                <path d="M9,5 L9,2.99895656 C9,2.44724809 9.45097518,2 9.99077797,2 L14.009222,2 C14.5564136,2 15,2.44266033 15,2.99895656 L15,5" id="Path-33" stroke="#ffffff" stroke-width="2" stroke-linejoin="round"></path>
+            </g>
+        </g>
+    </g>
+</svg>
diff --git a/client/src/assets/images/account/delete.svg b/client/src/assets/images/account/delete.svg
deleted file mode 100644 (file)
index 67e9e2c..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <defs></defs>
-    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="Artboard-4" transform="translate(-224.000000, -159.000000)">
-            <g id="25" transform="translate(224.000000, 159.000000)">
-                <path d="M5,7 L5,20.0081158 C5,21.1082031 5.89706013,22 7.00585866,22 L16.9941413,22 C18.1019465,22 19,21.1066027 19,20.0081158 L19,7" id="Path-296" stroke="#585858" stroke-width="2"></path>
-                <rect id="Rectangle-424" fill="#585858" x="2" y="4" width="20" height="2" rx="1"></rect>
-                <path d="M9,10.9970301 C9,10.4463856 9.44386482,10 10,10 C10.5522847,10 11,10.4530363 11,10.9970301 L11,17.0029699 C11,17.5536144 10.5561352,18 10,18 C9.44771525,18 9,17.5469637 9,17.0029699 L9,10.9970301 Z M13,10.9970301 C13,10.4463856 13.4438648,10 14,10 C14.5522847,10 15,10.4530363 15,10.9970301 L15,17.0029699 C15,17.5536144 14.5561352,18 14,18 C13.4477153,18 13,17.5469637 13,17.0029699 L13,10.9970301 Z" id="Combined-Shape" fill="#585858"></path>
-                <path d="M9,5 L9,2.99895656 C9,2.44724809 9.45097518,2 9.99077797,2 L14.009222,2 C14.5564136,2 15,2.44266033 15,2.99895656 L15,5" id="Path-33" stroke="#585858" stroke-width="2" stroke-linejoin="round"></path>
-            </g>
-        </g>
-    </g>
-</svg>
index 7f1063414de9f298ac9f601d805557e6ad12dd25..6a18f7a76d31d77f4552f25fed97b0d32eb8d28d 100644 (file)
   cursor: pointer;
 
   &:hover {
-    background-color: #F97D46;
+    background-color: $orange-hoover-color;
   }
 }
 
 @mixin peertube-button-link {
   display: inline-block;
 
-  @include peertube-button;
   @include disable-default-a-behaviour;
+  @include peertube-button;
 }
 
 @mixin avatar ($size) {
index 81dafdc196145f9bde514f55d1d01cbe7dee2e7f..cc1cee75be5e9553b7c38983a245711f9b8c689f 100644 (file)
@@ -4,6 +4,7 @@ $font-bold: 700;
 
 $grey-color: #555;
 $orange-color: #F1680D;
+$orange-hoover-color: #F97D46;
 
 $black-background: #000;
 $grey-background: #f6f2f2;