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"
|
||||
[(videosModel)]="videos"
|
||||
[miniatureDisplayOptions]="miniatureDisplayOptions"
|
||||
[titlePage]="titlePage"
|
||||
[getVideosObservableFunction]="getVideosObservableFunction"
|
||||
>
|
||||
<ng-template ptTemplate="globalButtons">
|
||||
<span class="action-button action-button-unblacklist-selection" (click)="removeSelectedVideosFromBlacklist()">
|
||||
<my-global-icon iconName="tick"></my-global-icon>
|
||||
<ng-container i18n>Unblacklist</ng-container>
|
||||
</span>
|
||||
</ng-template>
|
||||
|
||||
<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)]="checkedVideos[video.id]"></my-peertube-checkbox>
|
||||
</div>
|
||||
<ng-template ptTemplate="rowButtons" let-video>
|
||||
<my-button i18n-label label="Unblacklist" icon="tick" (click)="removeVideoFromBlacklist(video)"></my-button>
|
||||
</ng-template>
|
||||
|
||||
<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()">
|
||||
<my-global-icon iconName="tick"></my-global-icon>
|
||||
<ng-container i18n>Unblacklist</ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<my-button
|
||||
*ngIf="isInSelectionMode() === false"
|
||||
i18n-label
|
||||
label="Unblacklist"
|
||||
icon="tick"
|
||||
(click)="removeVideoFromBlacklist(video)"
|
||||
></my-button>
|
||||
</div>
|
||||
</div>
|
||||
</my-videos-selection>
|
||||
|
|
|
@ -1,67 +1,14 @@
|
|||
@import '_variables';
|
||||
@import '_mixins';
|
||||
|
||||
.action-selection-mode {
|
||||
width: 194px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
.action-button-unblacklist-selection {
|
||||
display: inline-block;
|
||||
|
||||
.action-selection-mode-child {
|
||||
position: fixed;
|
||||
@include peertube-button;
|
||||
@include orange-button;
|
||||
@include button-with-icon(21px);
|
||||
|
||||
.action-button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.action-button-cancel-selection {
|
||||
@include peertube-button;
|
||||
@include grey-button;
|
||||
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.action-button-unblacklist-selection {
|
||||
@include peertube-button;
|
||||
@include orange-button;
|
||||
@include button-with-icon(21px);
|
||||
|
||||
my-global-icon {
|
||||
@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;
|
||||
}
|
||||
my-global-icon {
|
||||
@include apply-svg-color(#fff);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,23 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||
import { Component } from '@angular/core'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
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 { AuthService, Notifier, ServerService } from '@app/core'
|
||||
import { Video } from '@shared/models'
|
||||
import { VideoBlacklistService } from '@app/shared'
|
||||
import { immutableAssign } from '@app/shared/misc/utils'
|
||||
import { ScreenService } from '@app/shared/misc/screen.service'
|
||||
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({
|
||||
selector: 'my-video-auto-blacklist-list',
|
||||
templateUrl: './video-auto-blacklist-list.component.html',
|
||||
styleUrls: [ './video-auto-blacklist-list.component.scss' ]
|
||||
})
|
||||
export class VideoAutoBlacklistListComponent extends AbstractVideoList implements OnInit, OnDestroy {
|
||||
export class VideoAutoBlacklistListComponent {
|
||||
titlePage: string
|
||||
checkedVideos: { [ id: number ]: boolean } = {}
|
||||
pagination: ComponentPagination = {
|
||||
currentPage: 1,
|
||||
itemsPerPage: 5,
|
||||
totalItems: null
|
||||
}
|
||||
|
||||
selection: SelectionType = {}
|
||||
miniatureDisplayOptions: MiniatureDisplayOptions = {
|
||||
date: true,
|
||||
views: false,
|
||||
|
@ -34,6 +28,13 @@ export class VideoAutoBlacklistListComponent extends AbstractVideoList implement
|
|||
blacklistInfo: false,
|
||||
nsfw: true
|
||||
}
|
||||
pagination: ComponentPagination = {
|
||||
currentPage: 1,
|
||||
itemsPerPage: 5,
|
||||
totalItems: null
|
||||
}
|
||||
videos: Video[] = []
|
||||
getVideosObservableFunction = this.getVideosObservable.bind(this)
|
||||
|
||||
constructor (
|
||||
protected router: Router,
|
||||
|
@ -45,42 +46,21 @@ export class VideoAutoBlacklistListComponent extends AbstractVideoList implement
|
|||
private i18n: I18n,
|
||||
private videoBlacklistService: VideoBlacklistService
|
||||
) {
|
||||
super()
|
||||
|
||||
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) {
|
||||
const newPagination = immutableAssign(this.pagination, { currentPage: page })
|
||||
|
||||
return this.videoBlacklistService.getAutoBlacklistedAsVideoList(newPagination)
|
||||
}
|
||||
|
||||
generateSyndicationList () {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
removeVideoFromBlacklist (entry: Video) {
|
||||
this.videoBlacklistService.removeVideoFromBlacklist(entry.id).subscribe(
|
||||
() => {
|
||||
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)
|
||||
|
@ -88,16 +68,16 @@ export class VideoAutoBlacklistListComponent extends AbstractVideoList implement
|
|||
}
|
||||
|
||||
removeSelectedVideosFromBlacklist () {
|
||||
const toReleaseVideosIds = Object.keys(this.checkedVideos)
|
||||
.filter(k => this.checkedVideos[ k ] === true)
|
||||
const toReleaseVideosIds = Object.keys(this.selection)
|
||||
.filter(k => this.selection[ k ] === true)
|
||||
.map(k => parseInt(k, 10))
|
||||
|
||||
this.videoBlacklistService.removeVideoFromBlacklist(toReleaseVideosIds).subscribe(
|
||||
() => {
|
||||
this.notifier.success(this.i18n('{{num}} videos removed from blacklist.', { num: toReleaseVideosIds.length }))
|
||||
|
||||
this.abortSelectionMode()
|
||||
this.reloadVideos()
|
||||
this.selection = {}
|
||||
this.videos = this.videos.filter(v => toReleaseVideosIds.includes(v.id) === false)
|
||||
},
|
||||
|
||||
error => this.notifier.error(error.message)
|
||||
|
|
|
@ -1,39 +1,30 @@
|
|||
<div i18n *ngIf="pagination.totalItems === 0">No results.</div>
|
||||
<my-videos-selection
|
||||
[(selection)]="selection"
|
||||
[(videosModel)]="videos"
|
||||
[miniatureDisplayOptions]="miniatureDisplayOptions"
|
||||
[titlePage]="titlePage"
|
||||
[getVideosObservableFunction]="getVideosObservableFunction"
|
||||
#videosSelection
|
||||
>
|
||||
<ng-template ptTemplate="globalButtons">
|
||||
<span class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()">
|
||||
<my-global-icon iconName="delete"></my-global-icon>
|
||||
<ng-container i18n>Delete</ng-container>
|
||||
</span>
|
||||
</ng-template>
|
||||
|
||||
<div myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" 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)]="checkedVideos[video.id]"></my-peertube-checkbox>
|
||||
</div>
|
||||
<ng-template ptTemplate="rowButtons" let-video>
|
||||
<my-delete-button (click)="deleteVideo(video)"></my-delete-button>
|
||||
|
||||
<my-video-miniature [video]="video" [displayOptions]="miniatureDisplayOptions" [displayAsRow]="true"></my-video-miniature>
|
||||
<my-edit-button [routerLink]="[ '/videos', 'update', video.uuid ]"></my-edit-button>
|
||||
|
||||
<!-- 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>
|
||||
<my-button i18n-label label="Change ownership"
|
||||
className="action-button-change-ownership"
|
||||
icon="im-with-her"
|
||||
(click)="changeOwnership($event, video)"
|
||||
></my-button>
|
||||
</ng-template>
|
||||
</my-videos-selection>
|
||||
|
||||
<span class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()">
|
||||
<my-global-icon iconName="delete"></my-global-icon>
|
||||
<ng-container i18n>Delete</ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="video-buttons" *ngIf="isInSelectionMode() === false">
|
||||
<my-delete-button (click)="deleteVideo(video)"></my-delete-button>
|
||||
|
||||
<my-edit-button [routerLink]="[ '/videos', 'update', video.uuid ]"></my-edit-button>
|
||||
|
||||
<my-button i18n-label label="Change ownership"
|
||||
className="action-button-change-ownership"
|
||||
icon="im-with-her"
|
||||
(click)="changeOwnership($event, video)"
|
||||
></my-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<my-video-change-ownership #videoChangeOwnershipModal></my-video-change-ownership>
|
||||
|
|
|
@ -1,75 +1,19 @@
|
|||
@import '_variables';
|
||||
@import '_mixins';
|
||||
|
||||
.action-selection-mode {
|
||||
width: 174px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
.action-button-delete-selection {
|
||||
display: inline-block;
|
||||
|
||||
.action-selection-mode-child {
|
||||
position: fixed;
|
||||
@include peertube-button;
|
||||
@include orange-button;
|
||||
@include button-with-icon(21px);
|
||||
|
||||
.action-button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.action-button-cancel-selection {
|
||||
@include peertube-button;
|
||||
@include grey-button;
|
||||
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.action-button-delete-selection {
|
||||
@include peertube-button;
|
||||
@include orange-button;
|
||||
@include button-with-icon(21px);
|
||||
|
||||
my-global-icon {
|
||||
@include apply-svg-color(#fff);
|
||||
}
|
||||
}
|
||||
my-global-icon {
|
||||
@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;
|
||||
}
|
||||
|
||||
.video-buttons {
|
||||
min-width: 190px;
|
||||
|
||||
*:not(:last-child) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
my-delete-button,
|
||||
my-edit-button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
|
|
@ -1,31 +1,33 @@
|
|||
import { concat, Observable } from 'rxjs'
|
||||
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 { immutableAssign } from '@app/shared/misc/utils'
|
||||
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
|
||||
import { Notifier, ServerService } from '@app/core'
|
||||
import { AuthService } from '../../core/auth'
|
||||
import { ConfirmService } from '../../core/confirm'
|
||||
import { AbstractVideoList } from '../../shared/video/abstract-video-list'
|
||||
import { Video } from '../../shared/video/video.model'
|
||||
import { VideoService } from '../../shared/video/video.service'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { VideoPrivacy, VideoState } from '../../../../../shared/models/videos'
|
||||
import { ScreenService } from '@app/shared/misc/screen.service'
|
||||
import { VideoChangeOwnershipComponent } from './video-change-ownership/video-change-ownership.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({
|
||||
selector: 'my-account-videos',
|
||||
templateUrl: './my-account-videos.component.html',
|
||||
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
|
||||
|
||||
titlePage: string
|
||||
checkedVideos: { [ id: number ]: boolean } = {}
|
||||
selection: SelectionType = {}
|
||||
pagination: ComponentPagination = {
|
||||
currentPage: 1,
|
||||
itemsPerPage: 5,
|
||||
|
@ -40,6 +42,8 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
|
|||
state: true,
|
||||
blacklistInfo: true
|
||||
}
|
||||
videos: Video[] = []
|
||||
getVideosObservableFunction = this.getVideosObservable.bind(this)
|
||||
|
||||
constructor (
|
||||
protected router: Router,
|
||||
|
@ -50,43 +54,28 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
|
|||
protected screenService: ScreenService,
|
||||
private i18n: I18n,
|
||||
private confirmService: ConfirmService,
|
||||
private videoService: VideoService,
|
||||
@Inject(LOCALE_ID) private localeId: string
|
||||
private videoService: VideoService
|
||||
) {
|
||||
super()
|
||||
|
||||
this.titlePage = this.i18n('My videos')
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
super.ngOnInit()
|
||||
disableForReuse () {
|
||||
this.videosSelection.disableForReuse()
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
super.ngOnDestroy()
|
||||
enabledForReuse () {
|
||||
this.videosSelection.enabledForReuse()
|
||||
}
|
||||
|
||||
abortSelectionMode () {
|
||||
this.checkedVideos = {}
|
||||
}
|
||||
|
||||
isInSelectionMode () {
|
||||
return Object.keys(this.checkedVideos).some(k => this.checkedVideos[ k ] === true)
|
||||
}
|
||||
|
||||
getVideosObservable (page: number) {
|
||||
getVideosObservable (page: number, sort: VideoSortField) {
|
||||
const newPagination = immutableAssign(this.pagination, { currentPage: page })
|
||||
|
||||
return this.videoService.getMyVideos(newPagination, this.sort)
|
||||
}
|
||||
|
||||
generateSyndicationList () {
|
||||
throw new Error('Method not implemented.')
|
||||
return this.videoService.getMyVideos(newPagination, sort)
|
||||
}
|
||||
|
||||
async deleteSelectedVideos () {
|
||||
const toDeleteVideosIds = Object.keys(this.checkedVideos)
|
||||
.filter(k => this.checkedVideos[ k ] === true)
|
||||
const toDeleteVideosIds = Object.keys(this.selection)
|
||||
.filter(k => this.selection[ k ] === true)
|
||||
.map(k => parseInt(k, 10))
|
||||
|
||||
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.abortSelectionMode()
|
||||
this.selection = {}
|
||||
},
|
||||
|
||||
err => this.notifier.error(err.message)
|
||||
|
@ -127,7 +116,7 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
|
|||
.subscribe(
|
||||
() => {
|
||||
this.notifier.success(this.i18n('Video {{videoName}} deleted.', { videoName: video.name }))
|
||||
this.reloadVideos()
|
||||
this.removeVideoFromArray(video.id)
|
||||
},
|
||||
|
||||
error => this.notifier.error(error.message)
|
||||
|
@ -139,27 +128,6 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
|
|||
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) {
|
||||
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 { DeleteButtonComponent } from './buttons/delete-button.component'
|
||||
import { EditButtonComponent } from './buttons/edit-button.component'
|
||||
import { FromNowPipe } from './misc/from-now.pipe'
|
||||
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 { UserService } from './users'
|
||||
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 { TimestampInputComponent } from '@app/shared/forms/timestamp-input.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({
|
||||
imports: [
|
||||
|
@ -107,6 +109,7 @@ import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playli
|
|||
VideoPlaylistMiniatureComponent,
|
||||
VideoAddToPlaylistComponent,
|
||||
VideoPlaylistElementMiniatureComponent,
|
||||
VideosSelectionComponent,
|
||||
|
||||
FeedComponent,
|
||||
|
||||
|
@ -114,10 +117,12 @@ import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playli
|
|||
DeleteButtonComponent,
|
||||
EditButtonComponent,
|
||||
|
||||
ActionDropdownComponent,
|
||||
NumberFormatterPipe,
|
||||
ObjectLengthPipe,
|
||||
FromNowPipe,
|
||||
PeerTubeTemplateDirective,
|
||||
|
||||
ActionDropdownComponent,
|
||||
MarkdownTextareaComponent,
|
||||
InfiniteScrollerDirective,
|
||||
TextareaAutoResizeDirective,
|
||||
|
@ -166,6 +171,7 @@ import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playli
|
|||
VideoPlaylistMiniatureComponent,
|
||||
VideoAddToPlaylistComponent,
|
||||
VideoPlaylistElementMiniatureComponent,
|
||||
VideosSelectionComponent,
|
||||
|
||||
FeedComponent,
|
||||
|
||||
|
@ -197,7 +203,8 @@ import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playli
|
|||
|
||||
NumberFormatterPipe,
|
||||
ObjectLengthPipe,
|
||||
FromNowPipe
|
||||
FromNowPipe,
|
||||
PeerTubeTemplateDirective
|
||||
],
|
||||
|
||||
providers: [
|
||||
|
|
|
@ -102,6 +102,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
|
|||
({ videos, totalVideos }) => {
|
||||
this.pagination.totalItems = totalVideos
|
||||
this.videos = this.videos.concat(videos)
|
||||
|
||||
this.onMoreVideos()
|
||||
},
|
||||
|
||||
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')
|
||||
}
|
||||
|
||||
// On videos hook for children that want to do something
|
||||
protected onMoreVideos () { /* empty */ }
|
||||
|
||||
protected loadRouteParams (routeParams: { [ key: string ]: any }) {
|
||||
this.sort = routeParams[ 'sort' ] as VideoSortField || this.defaultSort
|
||||
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