Refactor videos selection components
This commit is contained in:
parent
9ba1d64b1a
commit
693263e936
|
@ -1,33 +1,19 @@
|
||||||
<div i18n *ngIf="pagination.totalItems === 0">No results.</div>
|
<my-videos-selection
|
||||||
|
[(selection)]="selection"
|
||||||
<div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" class="videos">
|
[(videosModel)]="videos"
|
||||||
<div class="video" *ngFor="let video of videos; let i = index">
|
[miniatureDisplayOptions]="miniatureDisplayOptions"
|
||||||
<div class="checkbox-container">
|
[titlePage]="titlePage"
|
||||||
<my-peertube-checkbox [inputName]="'video-check-' + video.id" [(ngModel)]="checkedVideos[video.id]"></my-peertube-checkbox>
|
[getVideosObservableFunction]="getVideosObservableFunction"
|
||||||
</div>
|
>
|
||||||
|
<ng-template ptTemplate="globalButtons">
|
||||||
<my-video-miniature [video]="video" [displayAsRow]="true" [displayOptions]="miniatureDisplayOptions"></my-video-miniature>
|
|
||||||
|
|
||||||
<!-- Display only once -->
|
|
||||||
<div class="action-selection-mode" *ngIf="isInSelectionMode() === true && i === 0">
|
|
||||||
<div class="action-selection-mode-child">
|
|
||||||
<span i18n class="action-button action-button-cancel-selection" (click)="abortSelectionMode()">
|
|
||||||
Cancel
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="action-button action-button-unblacklist-selection" (click)="removeSelectedVideosFromBlacklist()">
|
<span class="action-button action-button-unblacklist-selection" (click)="removeSelectedVideosFromBlacklist()">
|
||||||
<my-global-icon iconName="tick"></my-global-icon>
|
<my-global-icon iconName="tick"></my-global-icon>
|
||||||
<ng-container i18n>Unblacklist</ng-container>
|
<ng-container i18n>Unblacklist</ng-container>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</ng-template>
|
||||||
</div>
|
|
||||||
|
|
||||||
<my-button
|
<ng-template ptTemplate="rowButtons" let-video>
|
||||||
*ngIf="isInSelectionMode() === false"
|
<my-button i18n-label label="Unblacklist" icon="tick" (click)="removeVideoFromBlacklist(video)"></my-button>
|
||||||
i18n-label
|
</ng-template>
|
||||||
label="Unblacklist"
|
|
||||||
icon="tick"
|
</my-videos-selection>
|
||||||
(click)="removeVideoFromBlacklist(video)"
|
|
||||||
></my-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,26 +1,9 @@
|
||||||
@import '_variables';
|
@import '_variables';
|
||||||
@import '_mixins';
|
@import '_mixins';
|
||||||
|
|
||||||
.action-selection-mode {
|
.action-button-unblacklist-selection {
|
||||||
width: 194px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
.action-selection-mode-child {
|
|
||||||
position: fixed;
|
|
||||||
|
|
||||||
.action-button {
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
|
||||||
|
|
||||||
.action-button-cancel-selection {
|
|
||||||
@include peertube-button;
|
|
||||||
@include grey-button;
|
|
||||||
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-button-unblacklist-selection {
|
|
||||||
@include peertube-button;
|
@include peertube-button;
|
||||||
@include orange-button;
|
@include orange-button;
|
||||||
@include button-with-icon(21px);
|
@include button-with-icon(21px);
|
||||||
|
@ -28,40 +11,4 @@
|
||||||
my-global-icon {
|
my-global-icon {
|
||||||
@include apply-svg-color(#fff);
|
@include apply-svg-color(#fff);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.video {
|
|
||||||
@include row-blocks;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
margin-top: 47px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-right: 20px;
|
|
||||||
margin-left: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
my-video-miniature {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: $small-view) {
|
|
||||||
.video {
|
|
||||||
flex-direction: column;
|
|
||||||
height: auto;
|
|
||||||
|
|
||||||
.checkbox-container {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
my-button {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,23 @@
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import { AbstractVideoList } from '@app/shared/video/abstract-video-list'
|
|
||||||
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
|
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
|
||||||
import { AuthService, Notifier, ServerService } from '@app/core'
|
import { AuthService, Notifier, ServerService } from '@app/core'
|
||||||
import { Video } from '@shared/models'
|
|
||||||
import { VideoBlacklistService } from '@app/shared'
|
import { VideoBlacklistService } from '@app/shared'
|
||||||
import { immutableAssign } from '@app/shared/misc/utils'
|
import { immutableAssign } from '@app/shared/misc/utils'
|
||||||
import { ScreenService } from '@app/shared/misc/screen.service'
|
import { ScreenService } from '@app/shared/misc/screen.service'
|
||||||
import { MiniatureDisplayOptions } from '@app/shared/video/video-miniature.component'
|
import { MiniatureDisplayOptions } from '@app/shared/video/video-miniature.component'
|
||||||
|
import { SelectionType } from '@app/shared/video/videos-selection.component'
|
||||||
|
import { Video } from '@app/shared/video/video.model'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-auto-blacklist-list',
|
selector: 'my-video-auto-blacklist-list',
|
||||||
templateUrl: './video-auto-blacklist-list.component.html',
|
templateUrl: './video-auto-blacklist-list.component.html',
|
||||||
styleUrls: [ './video-auto-blacklist-list.component.scss' ]
|
styleUrls: [ './video-auto-blacklist-list.component.scss' ]
|
||||||
})
|
})
|
||||||
export class VideoAutoBlacklistListComponent extends AbstractVideoList implements OnInit, OnDestroy {
|
export class VideoAutoBlacklistListComponent {
|
||||||
titlePage: string
|
titlePage: string
|
||||||
checkedVideos: { [ id: number ]: boolean } = {}
|
selection: SelectionType = {}
|
||||||
pagination: ComponentPagination = {
|
|
||||||
currentPage: 1,
|
|
||||||
itemsPerPage: 5,
|
|
||||||
totalItems: null
|
|
||||||
}
|
|
||||||
|
|
||||||
miniatureDisplayOptions: MiniatureDisplayOptions = {
|
miniatureDisplayOptions: MiniatureDisplayOptions = {
|
||||||
date: true,
|
date: true,
|
||||||
views: false,
|
views: false,
|
||||||
|
@ -34,6 +28,13 @@ export class VideoAutoBlacklistListComponent extends AbstractVideoList implement
|
||||||
blacklistInfo: false,
|
blacklistInfo: false,
|
||||||
nsfw: true
|
nsfw: true
|
||||||
}
|
}
|
||||||
|
pagination: ComponentPagination = {
|
||||||
|
currentPage: 1,
|
||||||
|
itemsPerPage: 5,
|
||||||
|
totalItems: null
|
||||||
|
}
|
||||||
|
videos: Video[] = []
|
||||||
|
getVideosObservableFunction = this.getVideosObservable.bind(this)
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
|
@ -45,42 +46,21 @@ export class VideoAutoBlacklistListComponent extends AbstractVideoList implement
|
||||||
private i18n: I18n,
|
private i18n: I18n,
|
||||||
private videoBlacklistService: VideoBlacklistService
|
private videoBlacklistService: VideoBlacklistService
|
||||||
) {
|
) {
|
||||||
super()
|
|
||||||
|
|
||||||
this.titlePage = this.i18n('Auto-blacklisted videos')
|
this.titlePage = this.i18n('Auto-blacklisted videos')
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
|
||||||
super.ngOnInit()
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy () {
|
|
||||||
super.ngOnDestroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
abortSelectionMode () {
|
|
||||||
this.checkedVideos = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
isInSelectionMode () {
|
|
||||||
return Object.keys(this.checkedVideos).some(k => this.checkedVideos[k] === true)
|
|
||||||
}
|
|
||||||
|
|
||||||
getVideosObservable (page: number) {
|
getVideosObservable (page: number) {
|
||||||
const newPagination = immutableAssign(this.pagination, { currentPage: page })
|
const newPagination = immutableAssign(this.pagination, { currentPage: page })
|
||||||
|
|
||||||
return this.videoBlacklistService.getAutoBlacklistedAsVideoList(newPagination)
|
return this.videoBlacklistService.getAutoBlacklistedAsVideoList(newPagination)
|
||||||
}
|
}
|
||||||
|
|
||||||
generateSyndicationList () {
|
|
||||||
throw new Error('Method not implemented.')
|
|
||||||
}
|
|
||||||
|
|
||||||
removeVideoFromBlacklist (entry: Video) {
|
removeVideoFromBlacklist (entry: Video) {
|
||||||
this.videoBlacklistService.removeVideoFromBlacklist(entry.id).subscribe(
|
this.videoBlacklistService.removeVideoFromBlacklist(entry.id).subscribe(
|
||||||
() => {
|
() => {
|
||||||
this.notifier.success(this.i18n('Video {{name}} removed from blacklist.', { name: entry.name }))
|
this.notifier.success(this.i18n('Video {{name}} removed from blacklist.', { name: entry.name }))
|
||||||
this.reloadVideos()
|
|
||||||
|
this.videos = this.videos.filter(v => v.id !== entry.id)
|
||||||
},
|
},
|
||||||
|
|
||||||
error => this.notifier.error(error.message)
|
error => this.notifier.error(error.message)
|
||||||
|
@ -88,16 +68,16 @@ export class VideoAutoBlacklistListComponent extends AbstractVideoList implement
|
||||||
}
|
}
|
||||||
|
|
||||||
removeSelectedVideosFromBlacklist () {
|
removeSelectedVideosFromBlacklist () {
|
||||||
const toReleaseVideosIds = Object.keys(this.checkedVideos)
|
const toReleaseVideosIds = Object.keys(this.selection)
|
||||||
.filter(k => this.checkedVideos[ k ] === true)
|
.filter(k => this.selection[ k ] === true)
|
||||||
.map(k => parseInt(k, 10))
|
.map(k => parseInt(k, 10))
|
||||||
|
|
||||||
this.videoBlacklistService.removeVideoFromBlacklist(toReleaseVideosIds).subscribe(
|
this.videoBlacklistService.removeVideoFromBlacklist(toReleaseVideosIds).subscribe(
|
||||||
() => {
|
() => {
|
||||||
this.notifier.success(this.i18n('{{num}} videos removed from blacklist.', { num: toReleaseVideosIds.length }))
|
this.notifier.success(this.i18n('{{num}} videos removed from blacklist.', { num: toReleaseVideosIds.length }))
|
||||||
|
|
||||||
this.abortSelectionMode()
|
this.selection = {}
|
||||||
this.reloadVideos()
|
this.videos = this.videos.filter(v => toReleaseVideosIds.includes(v.id) === false)
|
||||||
},
|
},
|
||||||
|
|
||||||
error => this.notifier.error(error.message)
|
error => this.notifier.error(error.message)
|
||||||
|
|
|
@ -1,28 +1,19 @@
|
||||||
<div i18n *ngIf="pagination.totalItems === 0">No results.</div>
|
<my-videos-selection
|
||||||
|
[(selection)]="selection"
|
||||||
<div myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" class="videos">
|
[(videosModel)]="videos"
|
||||||
<div class="video" *ngFor="let video of videos; let i = index">
|
[miniatureDisplayOptions]="miniatureDisplayOptions"
|
||||||
<div class="checkbox-container">
|
[titlePage]="titlePage"
|
||||||
<my-peertube-checkbox [inputName]="'video-check-' + video.id" [(ngModel)]="checkedVideos[video.id]"></my-peertube-checkbox>
|
[getVideosObservableFunction]="getVideosObservableFunction"
|
||||||
</div>
|
#videosSelection
|
||||||
|
>
|
||||||
<my-video-miniature [video]="video" [displayOptions]="miniatureDisplayOptions" [displayAsRow]="true"></my-video-miniature>
|
<ng-template ptTemplate="globalButtons">
|
||||||
|
|
||||||
<!-- Display only once -->
|
|
||||||
<div class="action-selection-mode" *ngIf="isInSelectionMode() === true && i === 0">
|
|
||||||
<div class="action-selection-mode-child">
|
|
||||||
<span i18n class="action-button action-button-cancel-selection" (click)="abortSelectionMode()">
|
|
||||||
Cancel
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()">
|
<span class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()">
|
||||||
<my-global-icon iconName="delete"></my-global-icon>
|
<my-global-icon iconName="delete"></my-global-icon>
|
||||||
<ng-container i18n>Delete</ng-container>
|
<ng-container i18n>Delete</ng-container>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</ng-template>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="video-buttons" *ngIf="isInSelectionMode() === false">
|
<ng-template ptTemplate="rowButtons" let-video>
|
||||||
<my-delete-button (click)="deleteVideo(video)"></my-delete-button>
|
<my-delete-button (click)="deleteVideo(video)"></my-delete-button>
|
||||||
|
|
||||||
<my-edit-button [routerLink]="[ '/videos', 'update', video.uuid ]"></my-edit-button>
|
<my-edit-button [routerLink]="[ '/videos', 'update', video.uuid ]"></my-edit-button>
|
||||||
|
@ -32,8 +23,8 @@
|
||||||
icon="im-with-her"
|
icon="im-with-her"
|
||||||
(click)="changeOwnership($event, video)"
|
(click)="changeOwnership($event, video)"
|
||||||
></my-button>
|
></my-button>
|
||||||
</div>
|
</ng-template>
|
||||||
</div>
|
</my-videos-selection>
|
||||||
</div>
|
|
||||||
|
|
||||||
<my-video-change-ownership #videoChangeOwnershipModal></my-video-change-ownership>
|
<my-video-change-ownership #videoChangeOwnershipModal></my-video-change-ownership>
|
||||||
|
|
|
@ -1,26 +1,9 @@
|
||||||
@import '_variables';
|
@import '_variables';
|
||||||
@import '_mixins';
|
@import '_mixins';
|
||||||
|
|
||||||
.action-selection-mode {
|
.action-button-delete-selection {
|
||||||
width: 174px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
.action-selection-mode-child {
|
|
||||||
position: fixed;
|
|
||||||
|
|
||||||
.action-button {
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
|
||||||
|
|
||||||
.action-button-cancel-selection {
|
|
||||||
@include peertube-button;
|
|
||||||
@include grey-button;
|
|
||||||
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-button-delete-selection {
|
|
||||||
@include peertube-button;
|
@include peertube-button;
|
||||||
@include orange-button;
|
@include orange-button;
|
||||||
@include button-with-icon(21px);
|
@include button-with-icon(21px);
|
||||||
|
@ -28,48 +11,9 @@
|
||||||
my-global-icon {
|
my-global-icon {
|
||||||
@include apply-svg-color(#fff);
|
@include apply-svg-color(#fff);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.video {
|
my-delete-button,
|
||||||
@include row-blocks;
|
my-edit-button {
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
margin-top: 47px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-right: 20px;
|
|
||||||
margin-left: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
my-video-miniature {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-buttons {
|
|
||||||
min-width: 190px;
|
|
||||||
|
|
||||||
*:not(:last-child) {
|
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: $small-view) {
|
|
||||||
.video {
|
|
||||||
flex-direction: column;
|
|
||||||
height: auto;
|
|
||||||
|
|
||||||
.checkbox-container {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-buttons {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,33 @@
|
||||||
import { concat, Observable } from 'rxjs'
|
import { concat, Observable } from 'rxjs'
|
||||||
import { tap, toArray } from 'rxjs/operators'
|
import { tap, toArray } from 'rxjs/operators'
|
||||||
import { Component, Inject, LOCALE_ID, OnDestroy, OnInit, ViewChild } from '@angular/core'
|
import { Component, ViewChild } from '@angular/core'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import { immutableAssign } from '@app/shared/misc/utils'
|
import { immutableAssign } from '@app/shared/misc/utils'
|
||||||
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
|
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
|
||||||
import { Notifier, ServerService } from '@app/core'
|
import { Notifier, ServerService } from '@app/core'
|
||||||
import { AuthService } from '../../core/auth'
|
import { AuthService } from '../../core/auth'
|
||||||
import { ConfirmService } from '../../core/confirm'
|
import { ConfirmService } from '../../core/confirm'
|
||||||
import { AbstractVideoList } from '../../shared/video/abstract-video-list'
|
|
||||||
import { Video } from '../../shared/video/video.model'
|
import { Video } from '../../shared/video/video.model'
|
||||||
import { VideoService } from '../../shared/video/video.service'
|
import { VideoService } from '../../shared/video/video.service'
|
||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { VideoPrivacy, VideoState } from '../../../../../shared/models/videos'
|
|
||||||
import { ScreenService } from '@app/shared/misc/screen.service'
|
import { ScreenService } from '@app/shared/misc/screen.service'
|
||||||
import { VideoChangeOwnershipComponent } from './video-change-ownership/video-change-ownership.component'
|
import { VideoChangeOwnershipComponent } from './video-change-ownership/video-change-ownership.component'
|
||||||
import { MiniatureDisplayOptions } from '@app/shared/video/video-miniature.component'
|
import { MiniatureDisplayOptions } from '@app/shared/video/video-miniature.component'
|
||||||
|
import { SelectionType, VideosSelectionComponent } from '@app/shared/video/videos-selection.component'
|
||||||
|
import { VideoSortField } from '@app/shared/video/sort-field.type'
|
||||||
|
import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-account-videos',
|
selector: 'my-account-videos',
|
||||||
templateUrl: './my-account-videos.component.html',
|
templateUrl: './my-account-videos.component.html',
|
||||||
styleUrls: [ './my-account-videos.component.scss' ]
|
styleUrls: [ './my-account-videos.component.scss' ]
|
||||||
})
|
})
|
||||||
export class MyAccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy {
|
export class MyAccountVideosComponent implements DisableForReuseHook {
|
||||||
|
@ViewChild('videosSelection') videosSelection: VideosSelectionComponent
|
||||||
@ViewChild('videoChangeOwnershipModal') videoChangeOwnershipModal: VideoChangeOwnershipComponent
|
@ViewChild('videoChangeOwnershipModal') videoChangeOwnershipModal: VideoChangeOwnershipComponent
|
||||||
|
|
||||||
titlePage: string
|
titlePage: string
|
||||||
checkedVideos: { [ id: number ]: boolean } = {}
|
selection: SelectionType = {}
|
||||||
pagination: ComponentPagination = {
|
pagination: ComponentPagination = {
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
itemsPerPage: 5,
|
itemsPerPage: 5,
|
||||||
|
@ -40,6 +42,8 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
|
||||||
state: true,
|
state: true,
|
||||||
blacklistInfo: true
|
blacklistInfo: true
|
||||||
}
|
}
|
||||||
|
videos: Video[] = []
|
||||||
|
getVideosObservableFunction = this.getVideosObservable.bind(this)
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
|
@ -50,43 +54,28 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
|
||||||
protected screenService: ScreenService,
|
protected screenService: ScreenService,
|
||||||
private i18n: I18n,
|
private i18n: I18n,
|
||||||
private confirmService: ConfirmService,
|
private confirmService: ConfirmService,
|
||||||
private videoService: VideoService,
|
private videoService: VideoService
|
||||||
@Inject(LOCALE_ID) private localeId: string
|
|
||||||
) {
|
) {
|
||||||
super()
|
|
||||||
|
|
||||||
this.titlePage = this.i18n('My videos')
|
this.titlePage = this.i18n('My videos')
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
disableForReuse () {
|
||||||
super.ngOnInit()
|
this.videosSelection.disableForReuse()
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy () {
|
enabledForReuse () {
|
||||||
super.ngOnDestroy()
|
this.videosSelection.enabledForReuse()
|
||||||
}
|
}
|
||||||
|
|
||||||
abortSelectionMode () {
|
getVideosObservable (page: number, sort: VideoSortField) {
|
||||||
this.checkedVideos = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
isInSelectionMode () {
|
|
||||||
return Object.keys(this.checkedVideos).some(k => this.checkedVideos[ k ] === true)
|
|
||||||
}
|
|
||||||
|
|
||||||
getVideosObservable (page: number) {
|
|
||||||
const newPagination = immutableAssign(this.pagination, { currentPage: page })
|
const newPagination = immutableAssign(this.pagination, { currentPage: page })
|
||||||
|
|
||||||
return this.videoService.getMyVideos(newPagination, this.sort)
|
return this.videoService.getMyVideos(newPagination, sort)
|
||||||
}
|
|
||||||
|
|
||||||
generateSyndicationList () {
|
|
||||||
throw new Error('Method not implemented.')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteSelectedVideos () {
|
async deleteSelectedVideos () {
|
||||||
const toDeleteVideosIds = Object.keys(this.checkedVideos)
|
const toDeleteVideosIds = Object.keys(this.selection)
|
||||||
.filter(k => this.checkedVideos[ k ] === true)
|
.filter(k => this.selection[ k ] === true)
|
||||||
.map(k => parseInt(k, 10))
|
.map(k => parseInt(k, 10))
|
||||||
|
|
||||||
const res = await this.confirmService.confirm(
|
const res = await this.confirmService.confirm(
|
||||||
|
@ -109,7 +98,7 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
|
||||||
() => {
|
() => {
|
||||||
this.notifier.success(this.i18n('{{deleteLength}} videos deleted.', { deleteLength: toDeleteVideosIds.length }))
|
this.notifier.success(this.i18n('{{deleteLength}} videos deleted.', { deleteLength: toDeleteVideosIds.length }))
|
||||||
|
|
||||||
this.abortSelectionMode()
|
this.selection = {}
|
||||||
},
|
},
|
||||||
|
|
||||||
err => this.notifier.error(err.message)
|
err => this.notifier.error(err.message)
|
||||||
|
@ -127,7 +116,7 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
|
||||||
.subscribe(
|
.subscribe(
|
||||||
() => {
|
() => {
|
||||||
this.notifier.success(this.i18n('Video {{videoName}} deleted.', { videoName: video.name }))
|
this.notifier.success(this.i18n('Video {{videoName}} deleted.', { videoName: video.name }))
|
||||||
this.reloadVideos()
|
this.removeVideoFromArray(video.id)
|
||||||
},
|
},
|
||||||
|
|
||||||
error => this.notifier.error(error.message)
|
error => this.notifier.error(error.message)
|
||||||
|
@ -139,27 +128,6 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
|
||||||
this.videoChangeOwnershipModal.show(video)
|
this.videoChangeOwnershipModal.show(video)
|
||||||
}
|
}
|
||||||
|
|
||||||
getStateLabel (video: Video) {
|
|
||||||
let suffix: string
|
|
||||||
|
|
||||||
if (video.privacy.id !== VideoPrivacy.PRIVATE && video.state.id === VideoState.PUBLISHED) {
|
|
||||||
suffix = this.i18n('Published')
|
|
||||||
} else if (video.scheduledUpdate) {
|
|
||||||
const updateAt = new Date(video.scheduledUpdate.updateAt.toString()).toLocaleString(this.localeId)
|
|
||||||
suffix = this.i18n('Publication scheduled on ') + updateAt
|
|
||||||
} else if (video.state.id === VideoState.TO_TRANSCODE && video.waitTranscoding === true) {
|
|
||||||
suffix = this.i18n('Waiting transcoding')
|
|
||||||
} else if (video.state.id === VideoState.TO_TRANSCODE) {
|
|
||||||
suffix = this.i18n('To transcode')
|
|
||||||
} else if (video.state.id === VideoState.TO_IMPORT) {
|
|
||||||
suffix = this.i18n('To import')
|
|
||||||
} else {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
return ' - ' + suffix
|
|
||||||
}
|
|
||||||
|
|
||||||
private removeVideoFromArray (id: number) {
|
private removeVideoFromArray (id: number) {
|
||||||
this.videos = this.videos.filter(v => v.id !== id)
|
this.videos = this.videos.filter(v => v.id !== id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Directive, Input, TemplateRef } from '@angular/core'
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[ptTemplate]'
|
||||||
|
})
|
||||||
|
export class PeerTubeTemplateDirective {
|
||||||
|
@Input('ptTemplate') name: string
|
||||||
|
|
||||||
|
constructor (public template: TemplateRef<any>) {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,10 +14,7 @@ import { AUTH_INTERCEPTOR_PROVIDER } from './auth'
|
||||||
import { ButtonComponent } from './buttons/button.component'
|
import { ButtonComponent } from './buttons/button.component'
|
||||||
import { DeleteButtonComponent } from './buttons/delete-button.component'
|
import { DeleteButtonComponent } from './buttons/delete-button.component'
|
||||||
import { EditButtonComponent } from './buttons/edit-button.component'
|
import { EditButtonComponent } from './buttons/edit-button.component'
|
||||||
import { FromNowPipe } from './misc/from-now.pipe'
|
|
||||||
import { LoaderComponent } from './misc/loader.component'
|
import { LoaderComponent } from './misc/loader.component'
|
||||||
import { NumberFormatterPipe } from './misc/number-formatter.pipe'
|
|
||||||
import { ObjectLengthPipe } from './misc/object-length.pipe'
|
|
||||||
import { RestExtractor, RestService } from './rest'
|
import { RestExtractor, RestService } from './rest'
|
||||||
import { UserService } from './users'
|
import { UserService } from './users'
|
||||||
import { VideoAbuseService } from './video-abuse'
|
import { VideoAbuseService } from './video-abuse'
|
||||||
|
@ -78,6 +75,11 @@ import { VideoPlaylistMiniatureComponent } from '@app/shared/video-playlist/vide
|
||||||
import { VideoAddToPlaylistComponent } from '@app/shared/video-playlist/video-add-to-playlist.component'
|
import { VideoAddToPlaylistComponent } from '@app/shared/video-playlist/video-add-to-playlist.component'
|
||||||
import { TimestampInputComponent } from '@app/shared/forms/timestamp-input.component'
|
import { TimestampInputComponent } from '@app/shared/forms/timestamp-input.component'
|
||||||
import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playlist/video-playlist-element-miniature.component'
|
import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playlist/video-playlist-element-miniature.component'
|
||||||
|
import { VideosSelectionComponent } from '@app/shared/video/videos-selection.component'
|
||||||
|
import { NumberFormatterPipe } from '@app/shared/angular/number-formatter.pipe'
|
||||||
|
import { ObjectLengthPipe } from '@app/shared/angular/object-length.pipe'
|
||||||
|
import { FromNowPipe } from '@app/shared/angular/from-now.pipe'
|
||||||
|
import { PeerTubeTemplateDirective } from '@app/shared/angular/peertube-template.directive'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -107,6 +109,7 @@ import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playli
|
||||||
VideoPlaylistMiniatureComponent,
|
VideoPlaylistMiniatureComponent,
|
||||||
VideoAddToPlaylistComponent,
|
VideoAddToPlaylistComponent,
|
||||||
VideoPlaylistElementMiniatureComponent,
|
VideoPlaylistElementMiniatureComponent,
|
||||||
|
VideosSelectionComponent,
|
||||||
|
|
||||||
FeedComponent,
|
FeedComponent,
|
||||||
|
|
||||||
|
@ -114,10 +117,12 @@ import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playli
|
||||||
DeleteButtonComponent,
|
DeleteButtonComponent,
|
||||||
EditButtonComponent,
|
EditButtonComponent,
|
||||||
|
|
||||||
ActionDropdownComponent,
|
|
||||||
NumberFormatterPipe,
|
NumberFormatterPipe,
|
||||||
ObjectLengthPipe,
|
ObjectLengthPipe,
|
||||||
FromNowPipe,
|
FromNowPipe,
|
||||||
|
PeerTubeTemplateDirective,
|
||||||
|
|
||||||
|
ActionDropdownComponent,
|
||||||
MarkdownTextareaComponent,
|
MarkdownTextareaComponent,
|
||||||
InfiniteScrollerDirective,
|
InfiniteScrollerDirective,
|
||||||
TextareaAutoResizeDirective,
|
TextareaAutoResizeDirective,
|
||||||
|
@ -166,6 +171,7 @@ import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playli
|
||||||
VideoPlaylistMiniatureComponent,
|
VideoPlaylistMiniatureComponent,
|
||||||
VideoAddToPlaylistComponent,
|
VideoAddToPlaylistComponent,
|
||||||
VideoPlaylistElementMiniatureComponent,
|
VideoPlaylistElementMiniatureComponent,
|
||||||
|
VideosSelectionComponent,
|
||||||
|
|
||||||
FeedComponent,
|
FeedComponent,
|
||||||
|
|
||||||
|
@ -197,7 +203,8 @@ import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playli
|
||||||
|
|
||||||
NumberFormatterPipe,
|
NumberFormatterPipe,
|
||||||
ObjectLengthPipe,
|
ObjectLengthPipe,
|
||||||
FromNowPipe
|
FromNowPipe,
|
||||||
|
PeerTubeTemplateDirective
|
||||||
],
|
],
|
||||||
|
|
||||||
providers: [
|
providers: [
|
||||||
|
|
|
@ -102,6 +102,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
|
||||||
({ videos, totalVideos }) => {
|
({ videos, totalVideos }) => {
|
||||||
this.pagination.totalItems = totalVideos
|
this.pagination.totalItems = totalVideos
|
||||||
this.videos = this.videos.concat(videos)
|
this.videos = this.videos.concat(videos)
|
||||||
|
|
||||||
|
this.onMoreVideos()
|
||||||
},
|
},
|
||||||
|
|
||||||
error => this.notifier.error(error.message)
|
error => this.notifier.error(error.message)
|
||||||
|
@ -118,6 +120,9 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
|
||||||
throw new Error('toggleModerationDisplay is not implemented')
|
throw new Error('toggleModerationDisplay is not implemented')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On videos hook for children that want to do something
|
||||||
|
protected onMoreVideos () { /* empty */ }
|
||||||
|
|
||||||
protected loadRouteParams (routeParams: { [ key: string ]: any }) {
|
protected loadRouteParams (routeParams: { [ key: string ]: any }) {
|
||||||
this.sort = routeParams[ 'sort' ] as VideoSortField || this.defaultSort
|
this.sort = routeParams[ 'sort' ] as VideoSortField || this.defaultSort
|
||||||
this.categoryOneOf = routeParams[ 'categoryOneOf' ]
|
this.categoryOneOf = routeParams[ 'categoryOneOf' ]
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<div class="no-results" i18n *ngIf="pagination.totalItems === 0">No results.</div>
|
||||||
|
|
||||||
|
<div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" class="videos">
|
||||||
|
<div class="video" *ngFor="let video of videos; let i = index">
|
||||||
|
<div class="checkbox-container">
|
||||||
|
<my-peertube-checkbox [inputName]="'video-check-' + video.id" [(ngModel)]="_selection[video.id]"></my-peertube-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<my-video-miniature [video]="video" [displayAsRow]="true" [displayOptions]="miniatureDisplayOptions"></my-video-miniature>
|
||||||
|
|
||||||
|
<!-- Display only once -->
|
||||||
|
<div class="action-selection-mode" *ngIf="isInSelectionMode() === true && i === 0">
|
||||||
|
<div class="action-selection-mode-child">
|
||||||
|
<span i18n class="action-button action-button-cancel-selection" (click)="abortSelectionMode()">
|
||||||
|
Cancel
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<ng-container *ngTemplateOutlet="globalButtonsTemplate"></ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-container *ngIf="isInSelectionMode() === false">
|
||||||
|
<ng-container *ngTemplateOutlet="rowButtonsTemplate; context: {$implicit: video}"></ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,57 @@
|
||||||
|
@import '_variables';
|
||||||
|
@import '_mixins';
|
||||||
|
|
||||||
|
.action-selection-mode {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
.action-selection-mode-child {
|
||||||
|
position: fixed;
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button-cancel-selection {
|
||||||
|
@include peertube-button;
|
||||||
|
@include grey-button;
|
||||||
|
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.video {
|
||||||
|
@include row-blocks;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 47px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 20px;
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
my-video-miniature {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: $small-view) {
|
||||||
|
.video {
|
||||||
|
flex-direction: column;
|
||||||
|
height: auto;
|
||||||
|
|
||||||
|
.checkbox-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
my-button {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
import {
|
||||||
|
AfterContentInit,
|
||||||
|
Component,
|
||||||
|
ContentChildren,
|
||||||
|
EventEmitter,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
Output,
|
||||||
|
QueryList,
|
||||||
|
TemplateRef
|
||||||
|
} from '@angular/core'
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
|
import { AbstractVideoList } from '@app/shared/video/abstract-video-list'
|
||||||
|
import { AuthService, Notifier, ServerService } from '@app/core'
|
||||||
|
import { ScreenService } from '@app/shared/misc/screen.service'
|
||||||
|
import { MiniatureDisplayOptions } from '@app/shared/video/video-miniature.component'
|
||||||
|
import { Observable } from 'rxjs'
|
||||||
|
import { Video } from '@app/shared/video/video.model'
|
||||||
|
import { PeerTubeTemplateDirective } from '@app/shared/angular/peertube-template.directive'
|
||||||
|
import { VideoSortField } from '@app/shared/video/sort-field.type'
|
||||||
|
|
||||||
|
export type SelectionType = { [ id: number ]: boolean }
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-videos-selection',
|
||||||
|
templateUrl: './videos-selection.component.html',
|
||||||
|
styleUrls: [ './videos-selection.component.scss' ]
|
||||||
|
})
|
||||||
|
export class VideosSelectionComponent extends AbstractVideoList implements OnInit, OnDestroy, AfterContentInit {
|
||||||
|
@Input() titlePage: string
|
||||||
|
@Input() miniatureDisplayOptions: MiniatureDisplayOptions
|
||||||
|
@Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<{ videos: Video[], totalVideos: number }>
|
||||||
|
@ContentChildren(PeerTubeTemplateDirective) templates: QueryList<PeerTubeTemplateDirective>
|
||||||
|
|
||||||
|
@Output() selectionChange = new EventEmitter<SelectionType>()
|
||||||
|
@Output() videosModelChange = new EventEmitter<Video[]>()
|
||||||
|
|
||||||
|
_selection: SelectionType = {}
|
||||||
|
|
||||||
|
rowButtonsTemplate: TemplateRef<any>
|
||||||
|
globalButtonsTemplate: TemplateRef<any>
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
protected router: Router,
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
protected notifier: Notifier,
|
||||||
|
protected authService: AuthService,
|
||||||
|
protected screenService: ScreenService,
|
||||||
|
protected serverService: ServerService
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterContentInit () {
|
||||||
|
{
|
||||||
|
const t = this.templates.find(t => t.name === 'rowButtons')
|
||||||
|
if (t) this.rowButtonsTemplate = t.template
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const t = this.templates.find(t => t.name === 'globalButtons')
|
||||||
|
if (t) this.globalButtonsTemplate = t.template
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input() get selection () {
|
||||||
|
return this._selection
|
||||||
|
}
|
||||||
|
|
||||||
|
set selection (selection: SelectionType) {
|
||||||
|
this._selection = selection
|
||||||
|
this.selectionChange.emit(this._selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input() get videosModel () {
|
||||||
|
return this.videos
|
||||||
|
}
|
||||||
|
|
||||||
|
set videosModel (videos: Video[]) {
|
||||||
|
this.videos = videos
|
||||||
|
this.videosModelChange.emit(this.videos)
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
super.ngOnInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy () {
|
||||||
|
super.ngOnDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
getVideosObservable (page: number) {
|
||||||
|
return this.getVideosObservableFunction(page, this.sort)
|
||||||
|
}
|
||||||
|
|
||||||
|
abortSelectionMode () {
|
||||||
|
this._selection = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
isInSelectionMode () {
|
||||||
|
return Object.keys(this._selection).some(k => this._selection[ k ] === true)
|
||||||
|
}
|
||||||
|
|
||||||
|
generateSyndicationList () {
|
||||||
|
throw new Error('Method not implemented.')
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onMoreVideos () {
|
||||||
|
this.videosModel = this.videos
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue