aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/shared')
-rw-r--r--client/src/app/shared/angular/from-now.pipe.ts (renamed from client/src/app/shared/misc/from-now.pipe.ts)0
-rw-r--r--client/src/app/shared/angular/number-formatter.pipe.ts (renamed from client/src/app/shared/misc/number-formatter.pipe.ts)0
-rw-r--r--client/src/app/shared/angular/object-length.pipe.ts (renamed from client/src/app/shared/misc/object-length.pipe.ts)0
-rw-r--r--client/src/app/shared/angular/peertube-template.directive.ts12
-rw-r--r--client/src/app/shared/shared.module.ts17
-rw-r--r--client/src/app/shared/video/abstract-video-list.ts5
-rw-r--r--client/src/app/shared/video/videos-selection.component.html26
-rw-r--r--client/src/app/shared/video/videos-selection.component.scss57
-rw-r--r--client/src/app/shared/video/videos-selection.component.ts112
9 files changed, 224 insertions, 5 deletions
diff --git a/client/src/app/shared/misc/from-now.pipe.ts b/client/src/app/shared/angular/from-now.pipe.ts
index 3a9a76411..3a9a76411 100644
--- a/client/src/app/shared/misc/from-now.pipe.ts
+++ b/client/src/app/shared/angular/from-now.pipe.ts
diff --git a/client/src/app/shared/misc/number-formatter.pipe.ts b/client/src/app/shared/angular/number-formatter.pipe.ts
index 8a0756a36..8a0756a36 100644
--- a/client/src/app/shared/misc/number-formatter.pipe.ts
+++ b/client/src/app/shared/angular/number-formatter.pipe.ts
diff --git a/client/src/app/shared/misc/object-length.pipe.ts b/client/src/app/shared/angular/object-length.pipe.ts
index 84d182052..84d182052 100644
--- a/client/src/app/shared/misc/object-length.pipe.ts
+++ b/client/src/app/shared/angular/object-length.pipe.ts
diff --git a/client/src/app/shared/angular/peertube-template.directive.ts b/client/src/app/shared/angular/peertube-template.directive.ts
new file mode 100644
index 000000000..a514b6057
--- /dev/null
+++ b/client/src/app/shared/angular/peertube-template.directive.ts
@@ -0,0 +1,12 @@
1import { Directive, Input, TemplateRef } from '@angular/core'
2
3@Directive({
4 selector: '[ptTemplate]'
5})
6export class PeerTubeTemplateDirective {
7 @Input('ptTemplate') name: string
8
9 constructor (public template: TemplateRef<any>) {
10 // empty
11 }
12}
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index 3647fc786..68225b457 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -14,10 +14,7 @@ import { AUTH_INTERCEPTOR_PROVIDER } from './auth'
14import { ButtonComponent } from './buttons/button.component' 14import { ButtonComponent } from './buttons/button.component'
15import { DeleteButtonComponent } from './buttons/delete-button.component' 15import { DeleteButtonComponent } from './buttons/delete-button.component'
16import { EditButtonComponent } from './buttons/edit-button.component' 16import { EditButtonComponent } from './buttons/edit-button.component'
17import { FromNowPipe } from './misc/from-now.pipe'
18import { LoaderComponent } from './misc/loader.component' 17import { LoaderComponent } from './misc/loader.component'
19import { NumberFormatterPipe } from './misc/number-formatter.pipe'
20import { ObjectLengthPipe } from './misc/object-length.pipe'
21import { RestExtractor, RestService } from './rest' 18import { RestExtractor, RestService } from './rest'
22import { UserService } from './users' 19import { UserService } from './users'
23import { VideoAbuseService } from './video-abuse' 20import { VideoAbuseService } from './video-abuse'
@@ -78,6 +75,11 @@ import { VideoPlaylistMiniatureComponent } from '@app/shared/video-playlist/vide
78import { VideoAddToPlaylistComponent } from '@app/shared/video-playlist/video-add-to-playlist.component' 75import { VideoAddToPlaylistComponent } from '@app/shared/video-playlist/video-add-to-playlist.component'
79import { TimestampInputComponent } from '@app/shared/forms/timestamp-input.component' 76import { TimestampInputComponent } from '@app/shared/forms/timestamp-input.component'
80import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playlist/video-playlist-element-miniature.component' 77import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playlist/video-playlist-element-miniature.component'
78import { VideosSelectionComponent } from '@app/shared/video/videos-selection.component'
79import { NumberFormatterPipe } from '@app/shared/angular/number-formatter.pipe'
80import { ObjectLengthPipe } from '@app/shared/angular/object-length.pipe'
81import { FromNowPipe } from '@app/shared/angular/from-now.pipe'
82import { PeerTubeTemplateDirective } from '@app/shared/angular/peertube-template.directive'
81 83
82@NgModule({ 84@NgModule({
83 imports: [ 85 imports: [
@@ -107,6 +109,7 @@ import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playli
107 VideoPlaylistMiniatureComponent, 109 VideoPlaylistMiniatureComponent,
108 VideoAddToPlaylistComponent, 110 VideoAddToPlaylistComponent,
109 VideoPlaylistElementMiniatureComponent, 111 VideoPlaylistElementMiniatureComponent,
112 VideosSelectionComponent,
110 113
111 FeedComponent, 114 FeedComponent,
112 115
@@ -114,10 +117,12 @@ import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playli
114 DeleteButtonComponent, 117 DeleteButtonComponent,
115 EditButtonComponent, 118 EditButtonComponent,
116 119
117 ActionDropdownComponent,
118 NumberFormatterPipe, 120 NumberFormatterPipe,
119 ObjectLengthPipe, 121 ObjectLengthPipe,
120 FromNowPipe, 122 FromNowPipe,
123 PeerTubeTemplateDirective,
124
125 ActionDropdownComponent,
121 MarkdownTextareaComponent, 126 MarkdownTextareaComponent,
122 InfiniteScrollerDirective, 127 InfiniteScrollerDirective,
123 TextareaAutoResizeDirective, 128 TextareaAutoResizeDirective,
@@ -166,6 +171,7 @@ import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playli
166 VideoPlaylistMiniatureComponent, 171 VideoPlaylistMiniatureComponent,
167 VideoAddToPlaylistComponent, 172 VideoAddToPlaylistComponent,
168 VideoPlaylistElementMiniatureComponent, 173 VideoPlaylistElementMiniatureComponent,
174 VideosSelectionComponent,
169 175
170 FeedComponent, 176 FeedComponent,
171 177
@@ -197,7 +203,8 @@ import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playli
197 203
198 NumberFormatterPipe, 204 NumberFormatterPipe,
199 ObjectLengthPipe, 205 ObjectLengthPipe,
200 FromNowPipe 206 FromNowPipe,
207 PeerTubeTemplateDirective
201 ], 208 ],
202 209
203 providers: [ 210 providers: [
diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts
index 467f629ea..099650129 100644
--- a/client/src/app/shared/video/abstract-video-list.ts
+++ b/client/src/app/shared/video/abstract-video-list.ts
@@ -102,6 +102,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
102 ({ videos, totalVideos }) => { 102 ({ videos, totalVideos }) => {
103 this.pagination.totalItems = totalVideos 103 this.pagination.totalItems = totalVideos
104 this.videos = this.videos.concat(videos) 104 this.videos = this.videos.concat(videos)
105
106 this.onMoreVideos()
105 }, 107 },
106 108
107 error => this.notifier.error(error.message) 109 error => this.notifier.error(error.message)
@@ -118,6 +120,9 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
118 throw new Error('toggleModerationDisplay is not implemented') 120 throw new Error('toggleModerationDisplay is not implemented')
119 } 121 }
120 122
123 // On videos hook for children that want to do something
124 protected onMoreVideos () { /* empty */ }
125
121 protected loadRouteParams (routeParams: { [ key: string ]: any }) { 126 protected loadRouteParams (routeParams: { [ key: string ]: any }) {
122 this.sort = routeParams[ 'sort' ] as VideoSortField || this.defaultSort 127 this.sort = routeParams[ 'sort' ] as VideoSortField || this.defaultSort
123 this.categoryOneOf = routeParams[ 'categoryOneOf' ] 128 this.categoryOneOf = routeParams[ 'categoryOneOf' ]
diff --git a/client/src/app/shared/video/videos-selection.component.html b/client/src/app/shared/video/videos-selection.component.html
new file mode 100644
index 000000000..6f3401b4b
--- /dev/null
+++ b/client/src/app/shared/video/videos-selection.component.html
@@ -0,0 +1,26 @@
1<div class="no-results" i18n *ngIf="pagination.totalItems === 0">No results.</div>
2
3<div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" class="videos">
4 <div class="video" *ngFor="let video of videos; let i = index">
5 <div class="checkbox-container">
6 <my-peertube-checkbox [inputName]="'video-check-' + video.id" [(ngModel)]="_selection[video.id]"></my-peertube-checkbox>
7 </div>
8
9 <my-video-miniature [video]="video" [displayAsRow]="true" [displayOptions]="miniatureDisplayOptions"></my-video-miniature>
10
11 <!-- Display only once -->
12 <div class="action-selection-mode" *ngIf="isInSelectionMode() === true && i === 0">
13 <div class="action-selection-mode-child">
14 <span i18n class="action-button action-button-cancel-selection" (click)="abortSelectionMode()">
15 Cancel
16 </span>
17
18 <ng-container *ngTemplateOutlet="globalButtonsTemplate"></ng-container>
19 </div>
20 </div>
21
22 <ng-container *ngIf="isInSelectionMode() === false">
23 <ng-container *ngTemplateOutlet="rowButtonsTemplate; context: {$implicit: video}"></ng-container>
24 </ng-container>
25 </div>
26</div>
diff --git a/client/src/app/shared/video/videos-selection.component.scss b/client/src/app/shared/video/videos-selection.component.scss
new file mode 100644
index 000000000..d3cbabf23
--- /dev/null
+++ b/client/src/app/shared/video/videos-selection.component.scss
@@ -0,0 +1,57 @@
1@import '_variables';
2@import '_mixins';
3
4.action-selection-mode {
5 display: flex;
6 justify-content: flex-end;
7 flex-grow: 1;
8
9 .action-selection-mode-child {
10 position: fixed;
11
12 .action-button {
13 display: inline-block;
14 }
15
16 .action-button-cancel-selection {
17 @include peertube-button;
18 @include grey-button;
19
20 margin-right: 10px;
21 }
22 }
23}
24
25.video {
26 @include row-blocks;
27
28 &:first-child {
29 margin-top: 47px;
30 }
31
32 .checkbox-container {
33 display: flex;
34 align-items: center;
35 margin-right: 20px;
36 margin-left: 12px;
37 }
38
39 my-video-miniature {
40 flex-grow: 1;
41 }
42}
43
44@media screen and (max-width: $small-view) {
45 .video {
46 flex-direction: column;
47 height: auto;
48
49 .checkbox-container {
50 display: none;
51 }
52
53 my-button {
54 margin-top: 10px;
55 }
56 }
57}
diff --git a/client/src/app/shared/video/videos-selection.component.ts b/client/src/app/shared/video/videos-selection.component.ts
new file mode 100644
index 000000000..b6bedafd8
--- /dev/null
+++ b/client/src/app/shared/video/videos-selection.component.ts
@@ -0,0 +1,112 @@
1import {
2 AfterContentInit,
3 Component,
4 ContentChildren,
5 EventEmitter,
6 Input,
7 OnDestroy,
8 OnInit,
9 Output,
10 QueryList,
11 TemplateRef
12} from '@angular/core'
13import { ActivatedRoute, Router } from '@angular/router'
14import { AbstractVideoList } from '@app/shared/video/abstract-video-list'
15import { AuthService, Notifier, ServerService } from '@app/core'
16import { ScreenService } from '@app/shared/misc/screen.service'
17import { MiniatureDisplayOptions } from '@app/shared/video/video-miniature.component'
18import { Observable } from 'rxjs'
19import { Video } from '@app/shared/video/video.model'
20import { PeerTubeTemplateDirective } from '@app/shared/angular/peertube-template.directive'
21import { VideoSortField } from '@app/shared/video/sort-field.type'
22
23export type SelectionType = { [ id: number ]: boolean }
24
25@Component({
26 selector: 'my-videos-selection',
27 templateUrl: './videos-selection.component.html',
28 styleUrls: [ './videos-selection.component.scss' ]
29})
30export class VideosSelectionComponent extends AbstractVideoList implements OnInit, OnDestroy, AfterContentInit {
31 @Input() titlePage: string
32 @Input() miniatureDisplayOptions: MiniatureDisplayOptions
33 @Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<{ videos: Video[], totalVideos: number }>
34 @ContentChildren(PeerTubeTemplateDirective) templates: QueryList<PeerTubeTemplateDirective>
35
36 @Output() selectionChange = new EventEmitter<SelectionType>()
37 @Output() videosModelChange = new EventEmitter<Video[]>()
38
39 _selection: SelectionType = {}
40
41 rowButtonsTemplate: TemplateRef<any>
42 globalButtonsTemplate: TemplateRef<any>
43
44 constructor (
45 protected router: Router,
46 protected route: ActivatedRoute,
47 protected notifier: Notifier,
48 protected authService: AuthService,
49 protected screenService: ScreenService,
50 protected serverService: ServerService
51 ) {
52 super()
53 }
54
55 ngAfterContentInit () {
56 {
57 const t = this.templates.find(t => t.name === 'rowButtons')
58 if (t) this.rowButtonsTemplate = t.template
59 }
60
61 {
62 const t = this.templates.find(t => t.name === 'globalButtons')
63 if (t) this.globalButtonsTemplate = t.template
64 }
65 }
66
67 @Input() get selection () {
68 return this._selection
69 }
70
71 set selection (selection: SelectionType) {
72 this._selection = selection
73 this.selectionChange.emit(this._selection)
74 }
75
76 @Input() get videosModel () {
77 return this.videos
78 }
79
80 set videosModel (videos: Video[]) {
81 this.videos = videos
82 this.videosModelChange.emit(this.videos)
83 }
84
85 ngOnInit () {
86 super.ngOnInit()
87 }
88
89 ngOnDestroy () {
90 super.ngOnDestroy()
91 }
92
93 getVideosObservable (page: number) {
94 return this.getVideosObservableFunction(page, this.sort)
95 }
96
97 abortSelectionMode () {
98 this._selection = {}
99 }
100
101 isInSelectionMode () {
102 return Object.keys(this._selection).some(k => this._selection[ k ] === true)
103 }
104
105 generateSyndicationList () {
106 throw new Error('Method not implemented.')
107 }
108
109 protected onMoreVideos () {
110 this.videosModel = this.videos
111 }
112}