diff options
47 files changed, 280 insertions, 494 deletions
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.html b/client/src/app/+admin/follows/followers-list/followers-list.component.html index 633de9677..c2e9a4df6 100644 --- a/client/src/app/+admin/follows/followers-list/followers-list.component.html +++ b/client/src/app/+admin/follows/followers-list/followers-list.component.html | |||
@@ -4,20 +4,16 @@ | |||
4 | </h1> | 4 | </h1> |
5 | 5 | ||
6 | <p-table | 6 | <p-table |
7 | [value]="followers" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" | 7 | [value]="followers" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" |
8 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)" | 8 | [sortField]="sort.field" [sortOrder]="sort.order" (onPage)="onPage($event)" |
9 | [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" | ||
9 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate | 10 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate |
10 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} followers" | 11 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} followers" |
11 | > | 12 | > |
12 | <ng-template pTemplate="caption"> | 13 | <ng-template pTemplate="caption"> |
13 | <div class="caption"> | 14 | <div class="caption"> |
14 | <div class="ml-auto has-feedback has-clear"> | 15 | <div class="ml-auto"> |
15 | <input | 16 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
16 | type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..." | ||
17 | (keyup)="onSearch($event)" | ||
18 | > | ||
19 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
20 | <span class="sr-only" i18n>Clear filters</span> | ||
21 | </div> | 17 | </div> |
22 | </div> | 18 | </div> |
23 | </ng-template> | 19 | </ng-template> |
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.scss b/client/src/app/+admin/follows/followers-list/followers-list.component.scss index 12c0cd033..35f38aae0 100644 --- a/client/src/app/+admin/follows/followers-list/followers-list.component.scss +++ b/client/src/app/+admin/follows/followers-list/followers-list.component.scss | |||
@@ -1,14 +1,6 @@ | |||
1 | @import '_variables'; | 1 | @import '_variables'; |
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | .caption { | ||
5 | justify-content: flex-end; | ||
6 | |||
7 | input { | ||
8 | @include peertube-input-text(250px); | ||
9 | } | ||
10 | } | ||
11 | |||
12 | a { | 4 | a { |
13 | @include disable-default-a-behaviour; | 5 | @include disable-default-a-behaviour; |
14 | display: inline-block; | 6 | display: inline-block; |
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.ts b/client/src/app/+admin/follows/followers-list/followers-list.component.ts index 904e3c338..4a312f6aa 100644 --- a/client/src/app/+admin/follows/followers-list/followers-list.component.ts +++ b/client/src/app/+admin/follows/followers-list/followers-list.component.ts | |||
@@ -59,7 +59,7 @@ export class FollowersListComponent extends RestTable implements OnInit { | |||
59 | const handle = follow.follower.name + '@' + follow.follower.host | 59 | const handle = follow.follower.name + '@' + follow.follower.host |
60 | this.notifier.success($localize`${handle} rejected from instance followers`) | 60 | this.notifier.success($localize`${handle} rejected from instance followers`) |
61 | 61 | ||
62 | this.loadData() | 62 | this.reloadData() |
63 | }, | 63 | }, |
64 | 64 | ||
65 | err => { | 65 | err => { |
@@ -80,14 +80,14 @@ export class FollowersListComponent extends RestTable implements OnInit { | |||
80 | const handle = follow.follower.name + '@' + follow.follower.host | 80 | const handle = follow.follower.name + '@' + follow.follower.host |
81 | this.notifier.success($localize`${handle} removed from instance followers`) | 81 | this.notifier.success($localize`${handle} removed from instance followers`) |
82 | 82 | ||
83 | this.loadData() | 83 | this.reloadData() |
84 | }, | 84 | }, |
85 | 85 | ||
86 | err => this.notifier.error(err.message) | 86 | err => this.notifier.error(err.message) |
87 | ) | 87 | ) |
88 | } | 88 | } |
89 | 89 | ||
90 | protected loadData () { | 90 | protected reloadData () { |
91 | this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search }) | 91 | this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search }) |
92 | .subscribe( | 92 | .subscribe( |
93 | resultList => { | 93 | resultList => { |
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.html b/client/src/app/+admin/follows/following-list/following-list.component.html index f4e6a60fe..e7c0c9088 100644 --- a/client/src/app/+admin/follows/following-list/following-list.component.html +++ b/client/src/app/+admin/follows/following-list/following-list.component.html | |||
@@ -4,8 +4,9 @@ | |||
4 | </h1> | 4 | </h1> |
5 | 5 | ||
6 | <p-table | 6 | <p-table |
7 | [value]="following" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" | 7 | [value]="following" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" |
8 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)" | 8 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)" |
9 | [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" | ||
9 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate | 10 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate |
10 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} hosts" | 11 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} hosts" |
11 | > | 12 | > |
@@ -18,13 +19,8 @@ | |||
18 | </a> | 19 | </a> |
19 | </div> | 20 | </div> |
20 | 21 | ||
21 | <div class="ml-auto has-feedback has-clear"> | 22 | <div class="ml-auto"> |
22 | <input | 23 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
23 | type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..." | ||
24 | (keyup)="onSearch($event)" | ||
25 | > | ||
26 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
27 | <span class="sr-only" i18n>Clear filters</span> | ||
28 | </div> | 24 | </div> |
29 | </div> | 25 | </div> |
30 | </ng-template> | 26 | </ng-template> |
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.scss b/client/src/app/+admin/follows/following-list/following-list.component.scss index 797882d9a..9474b0a23 100644 --- a/client/src/app/+admin/follows/following-list/following-list.component.scss +++ b/client/src/app/+admin/follows/following-list/following-list.component.scss | |||
@@ -16,14 +16,6 @@ a { | |||
16 | } | 16 | } |
17 | } | 17 | } |
18 | 18 | ||
19 | .caption { | ||
20 | justify-content: flex-end; | ||
21 | |||
22 | input { | ||
23 | @include peertube-input-text(250px); | ||
24 | } | ||
25 | } | ||
26 | |||
27 | .follow-button { | 19 | .follow-button { |
28 | @include create-button; | 20 | @include create-button; |
29 | } | 21 | } |
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts index f34490cc8..b63fe08c0 100644 --- a/client/src/app/+admin/follows/following-list/following-list.component.ts +++ b/client/src/app/+admin/follows/following-list/following-list.component.ts | |||
@@ -45,7 +45,7 @@ export class FollowingListComponent extends RestTable implements OnInit { | |||
45 | this.followService.follow(hosts).subscribe( | 45 | this.followService.follow(hosts).subscribe( |
46 | () => { | 46 | () => { |
47 | this.notifier.success($localize`Follow request(s) sent!`) | 47 | this.notifier.success($localize`Follow request(s) sent!`) |
48 | this.loadData() | 48 | this.reloadData() |
49 | }, | 49 | }, |
50 | 50 | ||
51 | err => this.notifier.error(err.message) | 51 | err => this.notifier.error(err.message) |
@@ -62,14 +62,14 @@ export class FollowingListComponent extends RestTable implements OnInit { | |||
62 | this.followService.unfollow(follow).subscribe( | 62 | this.followService.unfollow(follow).subscribe( |
63 | () => { | 63 | () => { |
64 | this.notifier.success($localize`You are not following ${follow.following.host} anymore.`) | 64 | this.notifier.success($localize`You are not following ${follow.following.host} anymore.`) |
65 | this.loadData() | 65 | this.reloadData() |
66 | }, | 66 | }, |
67 | 67 | ||
68 | err => this.notifier.error(err.message) | 68 | err => this.notifier.error(err.message) |
69 | ) | 69 | ) |
70 | } | 70 | } |
71 | 71 | ||
72 | protected loadData () { | 72 | protected reloadData () { |
73 | this.followService.getFollowing({ pagination: this.pagination, sort: this.sort, search: this.search }) | 73 | this.followService.getFollowing({ pagination: this.pagination, sort: this.sort, search: this.search }) |
74 | .subscribe( | 74 | .subscribe( |
75 | resultList => { | 75 | resultList => { |
diff --git a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts index d6fd1a1ab..3cd65dd6e 100644 --- a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts +++ b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts | |||
@@ -78,7 +78,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit | |||
78 | this.pagination.start = 0 | 78 | this.pagination.start = 0 |
79 | this.saveSelectLocalStorage() | 79 | this.saveSelectLocalStorage() |
80 | 80 | ||
81 | this.loadData() | 81 | this.reloadData() |
82 | } | 82 | } |
83 | 83 | ||
84 | getRedundancyStrategy (redundancy: VideoRedundancy) { | 84 | getRedundancyStrategy (redundancy: VideoRedundancy) { |
@@ -145,7 +145,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit | |||
145 | .subscribe( | 145 | .subscribe( |
146 | () => { | 146 | () => { |
147 | this.notifier.success($localize`Video redundancies removed!`) | 147 | this.notifier.success($localize`Video redundancies removed!`) |
148 | this.loadData() | 148 | this.reloadData() |
149 | }, | 149 | }, |
150 | 150 | ||
151 | err => this.notifier.error(err.message) | 151 | err => this.notifier.error(err.message) |
@@ -153,7 +153,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit | |||
153 | 153 | ||
154 | } | 154 | } |
155 | 155 | ||
156 | protected loadData () { | 156 | protected reloadData () { |
157 | const options = { | 157 | const options = { |
158 | pagination: this.pagination, | 158 | pagination: this.pagination, |
159 | sort: this.sort, | 159 | sort: this.sort, |
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html index 84ce381cc..e3a3a8320 100644 --- a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html +++ b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html | |||
@@ -1,18 +1,14 @@ | |||
1 | <p-table | 1 | <p-table |
2 | [value]="blockedAccounts" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" | 2 | [value]="blockedAccounts" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" |
3 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)" | 3 | [sortField]="sort.field" [sortOrder]="sort.order" (onPage)="onPage($event)" |
4 | [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" | ||
4 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate | 5 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate |
5 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted accounts" | 6 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted accounts" |
6 | > | 7 | > |
7 | <ng-template pTemplate="caption"> | 8 | <ng-template pTemplate="caption"> |
8 | <div class="caption"> | 9 | <div class="caption"> |
9 | <div class="ml-auto has-feedback has-clear"> | 10 | <div class="ml-auto"> |
10 | <input | 11 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
11 | type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..." | ||
12 | (keyup)="onSearch($event)" | ||
13 | > | ||
14 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
15 | <span class="sr-only" i18n>Clear filters</span> | ||
16 | </div> | 12 | </div> |
17 | </div> | 13 | </div> |
18 | </ng-template> | 14 | </ng-template> |
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html index cf2466bdb..d89c8f244 100644 --- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html +++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html | |||
@@ -4,8 +4,9 @@ | |||
4 | </h1> | 4 | </h1> |
5 | 5 | ||
6 | <p-table | 6 | <p-table |
7 | [value]="blocklist" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" | 7 | [value]="blocklist" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" |
8 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" | 8 | [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" |
9 | [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" | ||
9 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate | 10 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate |
10 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} blocked videos" | 11 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} blocked videos" |
11 | (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" | 12 | (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" |
@@ -13,7 +14,7 @@ | |||
13 | <ng-template pTemplate="caption"> | 14 | <ng-template pTemplate="caption"> |
14 | <div class="caption"> | 15 | <div class="caption"> |
15 | <div class="ml-auto"> | 16 | <div class="ml-auto"> |
16 | <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)" (resetTableFilter)="resetTableFilter()"></my-advanced-input-filter> | 17 | <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter> |
17 | </div> | 18 | </div> |
18 | </div> | 19 | </div> |
19 | </ng-template> | 20 | </ng-template> |
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss index b67e33cc1..068aa2aee 100644 --- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss +++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss | |||
@@ -5,23 +5,6 @@ my-global-icon { | |||
5 | height: 24px; | 5 | height: 24px; |
6 | } | 6 | } |
7 | 7 | ||
8 | .input-group { | ||
9 | @include peertube-input-group(300px); | ||
10 | |||
11 | .dropdown-toggle::after { | ||
12 | margin-left: 0; | ||
13 | } | ||
14 | } | ||
15 | |||
16 | .caption { | ||
17 | justify-content: flex-end; | ||
18 | |||
19 | input { | ||
20 | @include peertube-input-text(250px); | ||
21 | flex-grow: 1; | ||
22 | } | ||
23 | } | ||
24 | |||
25 | .badge { | 8 | .badge { |
26 | @include table-badge; | 9 | @include table-badge; |
27 | } | 10 | } |
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts index dfd8dc745..498d8321a 100644 --- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts +++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts | |||
@@ -2,9 +2,9 @@ import { SortMeta } from 'primeng/api' | |||
2 | import { switchMap } from 'rxjs/operators' | 2 | import { switchMap } from 'rxjs/operators' |
3 | import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' | 3 | import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' |
4 | import { environment } from 'src/environments/environment' | 4 | import { environment } from 'src/environments/environment' |
5 | import { AfterViewInit, Component, OnInit } from '@angular/core' | 5 | import { Component, OnInit } from '@angular/core' |
6 | import { DomSanitizer } from '@angular/platform-browser' | 6 | import { DomSanitizer } from '@angular/platform-browser' |
7 | import { ActivatedRoute, Params, Router } from '@angular/router' | 7 | import { ActivatedRoute, Router } from '@angular/router' |
8 | import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' | 8 | import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' |
9 | import { AdvancedInputFilter } from '@app/shared/shared-forms' | 9 | import { AdvancedInputFilter } from '@app/shared/shared-forms' |
10 | import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' | 10 | import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' |
@@ -16,7 +16,7 @@ import { VideoBlacklist, VideoBlacklistType } from '@shared/models' | |||
16 | templateUrl: './video-block-list.component.html', | 16 | templateUrl: './video-block-list.component.html', |
17 | styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-block-list.component.scss' ] | 17 | styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-block-list.component.scss' ] |
18 | }) | 18 | }) |
19 | export class VideoBlockListComponent extends RestTable implements OnInit, AfterViewInit { | 19 | export class VideoBlockListComponent extends RestTable implements OnInit { |
20 | blocklist: (VideoBlacklist & { reasonHtml?: string, embedHtml?: string })[] = [] | 20 | blocklist: (VideoBlacklist & { reasonHtml?: string, embedHtml?: string })[] = [] |
21 | totalRecords = 0 | 21 | totalRecords = 0 |
22 | sort: SortMeta = { field: 'createdAt', order: -1 } | 22 | sort: SortMeta = { field: 'createdAt', order: -1 } |
@@ -64,7 +64,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV | |||
64 | ).subscribe( | 64 | ).subscribe( |
65 | () => { | 65 | () => { |
66 | this.notifier.success($localize`Video ${videoBlock.video.name} switched to manual block.`) | 66 | this.notifier.success($localize`Video ${videoBlock.video.name} switched to manual block.`) |
67 | this.loadData() | 67 | this.reloadData() |
68 | }, | 68 | }, |
69 | 69 | ||
70 | err => this.notifier.error(err.message) | 70 | err => this.notifier.error(err.message) |
@@ -116,11 +116,6 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV | |||
116 | }) | 116 | }) |
117 | 117 | ||
118 | this.initialize() | 118 | this.initialize() |
119 | this.listenToSearchChange() | ||
120 | } | ||
121 | |||
122 | ngAfterViewInit () { | ||
123 | if (this.search) this.setTableFilter(this.search, false) | ||
124 | } | 119 | } |
125 | 120 | ||
126 | getIdentifier () { | 121 | getIdentifier () { |
@@ -144,7 +139,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV | |||
144 | this.videoBlocklistService.unblockVideo(entry.video.id).subscribe( | 139 | this.videoBlocklistService.unblockVideo(entry.video.id).subscribe( |
145 | () => { | 140 | () => { |
146 | this.notifier.success($localize`Video ${entry.video.name} unblocked.`) | 141 | this.notifier.success($localize`Video ${entry.video.name} unblocked.`) |
147 | this.loadData() | 142 | this.reloadData() |
148 | }, | 143 | }, |
149 | 144 | ||
150 | err => this.notifier.error(err.message) | 145 | err => this.notifier.error(err.message) |
@@ -162,7 +157,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV | |||
162 | ) | 157 | ) |
163 | } | 158 | } |
164 | 159 | ||
165 | protected loadData () { | 160 | protected reloadData () { |
166 | this.videoBlocklistService.listBlocks({ | 161 | this.videoBlocklistService.listBlocks({ |
167 | pagination: this.pagination, | 162 | pagination: this.pagination, |
168 | sort: this.sort, | 163 | sort: this.sort, |
diff --git a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html index 5cc0ff137..9d9283536 100644 --- a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html +++ b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html | |||
@@ -8,8 +8,9 @@ | |||
8 | <em i18n>This view also shows comments from muted accounts.</em> | 8 | <em i18n>This view also shows comments from muted accounts.</em> |
9 | 9 | ||
10 | <p-table | 10 | <p-table |
11 | [value]="comments" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" | 11 | [value]="comments" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" |
12 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" | 12 | [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" |
13 | [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" | ||
13 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate | 14 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate |
14 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} comments" | 15 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} comments" |
15 | (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" | 16 | (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" |
@@ -26,7 +27,7 @@ | |||
26 | </div> | 27 | </div> |
27 | 28 | ||
28 | <div class="ml-auto"> | 29 | <div class="ml-auto"> |
29 | <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)" (resetTableFilter)="resetTableFilter()"></my-advanced-input-filter> | 30 | <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter> |
30 | </div> | 31 | </div> |
31 | </div> | 32 | </div> |
32 | </ng-template> | 33 | </ng-template> |
diff --git a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss index 5d97d9bdb..a6f931e3b 100644 --- a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss +++ b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss | |||
@@ -11,23 +11,6 @@ my-global-icon { | |||
11 | height: 24px; | 11 | height: 24px; |
12 | } | 12 | } |
13 | 13 | ||
14 | .input-group { | ||
15 | @include peertube-input-group(300px); | ||
16 | |||
17 | .dropdown-toggle::after { | ||
18 | margin-left: 0; | ||
19 | } | ||
20 | } | ||
21 | |||
22 | .caption { | ||
23 | justify-content: flex-end; | ||
24 | |||
25 | input { | ||
26 | @include peertube-input-text(250px); | ||
27 | flex-grow: 1; | ||
28 | } | ||
29 | } | ||
30 | |||
31 | .video { | 14 | .video { |
32 | display: flex; | 15 | display: flex; |
33 | flex-direction: column; | 16 | flex-direction: column; |
diff --git a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts index ebbbddb43..e2ae993b0 100644 --- a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts +++ b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts | |||
@@ -13,9 +13,7 @@ import { FeedFormat, UserRight } from '@shared/models' | |||
13 | templateUrl: './video-comment-list.component.html', | 13 | templateUrl: './video-comment-list.component.html', |
14 | styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-comment-list.component.scss' ] | 14 | styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-comment-list.component.scss' ] |
15 | }) | 15 | }) |
16 | export class VideoCommentListComponent extends RestTable implements OnInit, AfterViewInit { | 16 | export class VideoCommentListComponent extends RestTable implements OnInit { |
17 | baseRoute = '/admin/moderation/video-comments/list' | ||
18 | |||
19 | comments: VideoCommentAdmin[] | 17 | comments: VideoCommentAdmin[] |
20 | totalRecords = 0 | 18 | totalRecords = 0 |
21 | sort: SortMeta = { field: 'createdAt', order: -1 } | 19 | sort: SortMeta = { field: 'createdAt', order: -1 } |
@@ -91,7 +89,6 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte | |||
91 | 89 | ||
92 | ngOnInit () { | 90 | ngOnInit () { |
93 | this.initialize() | 91 | this.initialize() |
94 | this.listenToSearchChange() | ||
95 | 92 | ||
96 | this.bulkCommentActions = [ | 93 | this.bulkCommentActions = [ |
97 | { | 94 | { |
@@ -103,10 +100,6 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte | |||
103 | ] | 100 | ] |
104 | } | 101 | } |
105 | 102 | ||
106 | ngAfterViewInit () { | ||
107 | if (this.search) this.setTableFilter(this.search, false) | ||
108 | } | ||
109 | |||
110 | getIdentifier () { | 103 | getIdentifier () { |
111 | return 'VideoCommentListComponent' | 104 | return 'VideoCommentListComponent' |
112 | } | 105 | } |
@@ -119,7 +112,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte | |||
119 | return this.selectedComments.length !== 0 | 112 | return this.selectedComments.length !== 0 |
120 | } | 113 | } |
121 | 114 | ||
122 | protected loadData () { | 115 | protected reloadData () { |
123 | this.videoCommentService.getAdminVideoComments({ | 116 | this.videoCommentService.getAdminVideoComments({ |
124 | pagination: this.pagination, | 117 | pagination: this.pagination, |
125 | sort: this.sort, | 118 | sort: this.sort, |
@@ -147,7 +140,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte | |||
147 | this.videoCommentService.deleteVideoComments(commentArgs).subscribe( | 140 | this.videoCommentService.deleteVideoComments(commentArgs).subscribe( |
148 | () => { | 141 | () => { |
149 | this.notifier.success($localize`${commentArgs.length} comments deleted.`) | 142 | this.notifier.success($localize`${commentArgs.length} comments deleted.`) |
150 | this.loadData() | 143 | this.reloadData() |
151 | }, | 144 | }, |
152 | 145 | ||
153 | err => this.notifier.error(err.message), | 146 | err => this.notifier.error(err.message), |
@@ -159,7 +152,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte | |||
159 | private deleteComment (comment: VideoCommentAdmin) { | 152 | private deleteComment (comment: VideoCommentAdmin) { |
160 | this.videoCommentService.deleteVideoComment(comment.video.id, comment.id) | 153 | this.videoCommentService.deleteVideoComment(comment.video.id, comment.id) |
161 | .subscribe( | 154 | .subscribe( |
162 | () => this.loadData(), | 155 | () => this.reloadData(), |
163 | 156 | ||
164 | err => this.notifier.error(err.message) | 157 | err => this.notifier.error(err.message) |
165 | ) | 158 | ) |
diff --git a/client/src/app/+admin/system/jobs/jobs.component.ts b/client/src/app/+admin/system/jobs/jobs.component.ts index 43578eedd..29ba95c5c 100644 --- a/client/src/app/+admin/system/jobs/jobs.component.ts +++ b/client/src/app/+admin/system/jobs/jobs.component.ts | |||
@@ -86,7 +86,7 @@ export class JobsComponent extends RestTable implements OnInit { | |||
86 | onJobStateOrTypeChanged () { | 86 | onJobStateOrTypeChanged () { |
87 | this.pagination.start = 0 | 87 | this.pagination.start = 0 |
88 | 88 | ||
89 | this.loadData() | 89 | this.reloadData() |
90 | this.saveJobStateAndType() | 90 | this.saveJobStateAndType() |
91 | } | 91 | } |
92 | 92 | ||
@@ -104,10 +104,10 @@ export class JobsComponent extends RestTable implements OnInit { | |||
104 | this.jobs = [] | 104 | this.jobs = [] |
105 | this.totalRecords = 0 | 105 | this.totalRecords = 0 |
106 | 106 | ||
107 | this.loadData() | 107 | this.reloadData() |
108 | } | 108 | } |
109 | 109 | ||
110 | protected loadData () { | 110 | protected reloadData () { |
111 | let jobState = this.jobState as JobState | 111 | let jobState = this.jobState as JobState |
112 | if (this.jobState === 'all') jobState = null | 112 | if (this.jobState === 'all') jobState = null |
113 | 113 | ||
diff --git a/client/src/app/+admin/users/user-list/user-list.component.html b/client/src/app/+admin/users/user-list/user-list.component.html index 7170d7019..44d8a7e87 100644 --- a/client/src/app/+admin/users/user-list/user-list.component.html +++ b/client/src/app/+admin/users/user-list/user-list.component.html | |||
@@ -1,7 +1,7 @@ | |||
1 | <p-table | 1 | <p-table |
2 | [value]="users" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" | 2 | [value]="users" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" |
3 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" [resizableColumns]="true" | 3 | [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true" [(selection)]="selectedUsers" |
4 | [(selection)]="selectedUsers" | 4 | [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" |
5 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate | 5 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate |
6 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} users" | 6 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} users" |
7 | (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" | 7 | (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" |
@@ -22,7 +22,7 @@ | |||
22 | </div> | 22 | </div> |
23 | 23 | ||
24 | <div class="ml-auto"> | 24 | <div class="ml-auto"> |
25 | <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)" (resetTableFilter)="resetTableFilter()"></my-advanced-input-filter> | 25 | <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter> |
26 | </div> | 26 | </div> |
27 | 27 | ||
28 | </div> | 28 | </div> |
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts index 435bc17d7..1c60adf89 100644 --- a/client/src/app/+admin/users/user-list/user-list.component.ts +++ b/client/src/app/+admin/users/user-list/user-list.component.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { SortMeta } from 'primeng/api' | 1 | import { SortMeta } from 'primeng/api' |
2 | import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core' | 2 | import { Component, OnInit, ViewChild } from '@angular/core' |
3 | import { ActivatedRoute, Router } from '@angular/router' | 3 | import { ActivatedRoute, Router } from '@angular/router' |
4 | import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService, UserService } from '@app/core' | 4 | import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService, UserService } from '@app/core' |
5 | import { AdvancedInputFilter } from '@app/shared/shared-forms' | 5 | import { AdvancedInputFilter } from '@app/shared/shared-forms' |
@@ -19,7 +19,7 @@ type UserForList = User & { | |||
19 | templateUrl: './user-list.component.html', | 19 | templateUrl: './user-list.component.html', |
20 | styleUrls: [ './user-list.component.scss' ] | 20 | styleUrls: [ './user-list.component.scss' ] |
21 | }) | 21 | }) |
22 | export class UserListComponent extends RestTable implements OnInit, AfterViewInit { | 22 | export class UserListComponent extends RestTable implements OnInit { |
23 | @ViewChild('userBanModal', { static: true }) userBanModal: UserBanModalComponent | 23 | @ViewChild('userBanModal', { static: true }) userBanModal: UserBanModalComponent |
24 | 24 | ||
25 | users: User[] = [] | 25 | users: User[] = [] |
@@ -78,7 +78,6 @@ export class UserListComponent extends RestTable implements OnInit, AfterViewIni | |||
78 | .subscribe(config => this.serverConfig = config) | 78 | .subscribe(config => this.serverConfig = config) |
79 | 79 | ||
80 | this.initialize() | 80 | this.initialize() |
81 | this.listenToSearchChange() | ||
82 | 81 | ||
83 | this.bulkUserActions = [ | 82 | this.bulkUserActions = [ |
84 | [ | 83 | [ |
@@ -127,10 +126,6 @@ export class UserListComponent extends RestTable implements OnInit, AfterViewIni | |||
127 | this.columns.push({ id: 'lastLoginDate', label: 'Last login' }) | 126 | this.columns.push({ id: 'lastLoginDate', label: 'Last login' }) |
128 | } | 127 | } |
129 | 128 | ||
130 | ngAfterViewInit () { | ||
131 | if (this.search) this.setTableFilter(this.search, false) | ||
132 | } | ||
133 | |||
134 | getIdentifier () { | 129 | getIdentifier () { |
135 | return 'UserListComponent' | 130 | return 'UserListComponent' |
136 | } | 131 | } |
@@ -174,7 +169,7 @@ export class UserListComponent extends RestTable implements OnInit, AfterViewIni | |||
174 | } | 169 | } |
175 | 170 | ||
176 | onUserChanged () { | 171 | onUserChanged () { |
177 | this.loadData() | 172 | this.reloadData() |
178 | } | 173 | } |
179 | 174 | ||
180 | async unbanUsers (users: User[]) { | 175 | async unbanUsers (users: User[]) { |
@@ -185,7 +180,7 @@ export class UserListComponent extends RestTable implements OnInit, AfterViewIni | |||
185 | .subscribe( | 180 | .subscribe( |
186 | () => { | 181 | () => { |
187 | this.notifier.success($localize`${users.length} users unbanned.`) | 182 | this.notifier.success($localize`${users.length} users unbanned.`) |
188 | this.loadData() | 183 | this.reloadData() |
189 | }, | 184 | }, |
190 | 185 | ||
191 | err => this.notifier.error(err.message) | 186 | err => this.notifier.error(err.message) |
@@ -207,7 +202,7 @@ export class UserListComponent extends RestTable implements OnInit, AfterViewIni | |||
207 | this.userService.removeUser(users).subscribe( | 202 | this.userService.removeUser(users).subscribe( |
208 | () => { | 203 | () => { |
209 | this.notifier.success($localize`${users.length} users deleted.`) | 204 | this.notifier.success($localize`${users.length} users deleted.`) |
210 | this.loadData() | 205 | this.reloadData() |
211 | }, | 206 | }, |
212 | 207 | ||
213 | err => this.notifier.error(err.message) | 208 | err => this.notifier.error(err.message) |
@@ -218,7 +213,7 @@ export class UserListComponent extends RestTable implements OnInit, AfterViewIni | |||
218 | this.userService.updateUsers(users, { emailVerified: true }).subscribe( | 213 | this.userService.updateUsers(users, { emailVerified: true }).subscribe( |
219 | () => { | 214 | () => { |
220 | this.notifier.success($localize`${users.length} users email set as verified.`) | 215 | this.notifier.success($localize`${users.length} users email set as verified.`) |
221 | this.loadData() | 216 | this.reloadData() |
222 | }, | 217 | }, |
223 | 218 | ||
224 | err => this.notifier.error(err.message) | 219 | err => this.notifier.error(err.message) |
@@ -229,7 +224,7 @@ export class UserListComponent extends RestTable implements OnInit, AfterViewIni | |||
229 | return this.selectedUsers.length !== 0 | 224 | return this.selectedUsers.length !== 0 |
230 | } | 225 | } |
231 | 226 | ||
232 | protected loadData () { | 227 | protected reloadData () { |
233 | this.selectedUsers = [] | 228 | this.selectedUsers = [] |
234 | 229 | ||
235 | this.userService.getUsers({ | 230 | this.userService.getUsers({ |
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html index 2ed0c93d6..8d5eb04e2 100644 --- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html +++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html | |||
@@ -5,12 +5,7 @@ | |||
5 | </h1> | 5 | </h1> |
6 | 6 | ||
7 | <div class="video-channels-header d-flex justify-content-between"> | 7 | <div class="video-channels-header d-flex justify-content-between"> |
8 | <div class="has-feedback has-clear"> | 8 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
9 | <input type="text" placeholder="Search your channels" i18n-placeholder [(ngModel)]="channelsSearch" | ||
10 | (ngModelChange)="onChannelsSearchChanged()" /> | ||
11 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
12 | <span class="sr-only" i18n>Clear filters</span> | ||
13 | </div> | ||
14 | 9 | ||
15 | <a class="create-button" routerLink="create"> | 10 | <a class="create-button" routerLink="create"> |
16 | <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> | 11 | <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> |
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts index f6ba50a48..9e3bf35b4 100644 --- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts +++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts | |||
@@ -1,29 +1,26 @@ | |||
1 | import { ChartData } from 'chart.js' | 1 | import { ChartData } from 'chart.js' |
2 | import { max, maxBy, min, minBy } from 'lodash-es' | 2 | import { max, maxBy, min, minBy } from 'lodash-es' |
3 | import { Subject } from 'rxjs' | 3 | import { mergeMap } from 'rxjs/operators' |
4 | import { debounceTime, mergeMap } from 'rxjs/operators' | 4 | import { Component } from '@angular/core' |
5 | import { Component, OnInit } from '@angular/core' | 5 | import { AuthService, ConfirmService, Notifier, ScreenService } from '@app/core' |
6 | import { AuthService, ConfirmService, Notifier, ScreenService, User } from '@app/core' | ||
7 | import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' | 6 | import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' |
8 | 7 | ||
9 | @Component({ | 8 | @Component({ |
10 | templateUrl: './my-video-channels.component.html', | 9 | templateUrl: './my-video-channels.component.html', |
11 | styleUrls: [ './my-video-channels.component.scss' ] | 10 | styleUrls: [ './my-video-channels.component.scss' ] |
12 | }) | 11 | }) |
13 | export class MyVideoChannelsComponent implements OnInit { | 12 | export class MyVideoChannelsComponent { |
14 | totalItems: number | 13 | totalItems: number |
15 | 14 | ||
16 | videoChannels: VideoChannel[] = [] | 15 | videoChannels: VideoChannel[] = [] |
16 | |||
17 | videoChannelsChartData: ChartData[] | 17 | videoChannelsChartData: ChartData[] |
18 | videoChannelsMinimumDailyViews = 0 | 18 | videoChannelsMinimumDailyViews = 0 |
19 | videoChannelsMaximumDailyViews: number | 19 | videoChannelsMaximumDailyViews: number |
20 | 20 | ||
21 | channelsSearch: string | ||
22 | channelsSearchChanged = new Subject<string>() | ||
23 | |||
24 | chartOptions: any | 21 | chartOptions: any |
25 | 22 | ||
26 | private user: User | 23 | search: string |
27 | 24 | ||
28 | constructor ( | 25 | constructor ( |
29 | private authService: AuthService, | 26 | private authService: AuthService, |
@@ -31,31 +28,15 @@ export class MyVideoChannelsComponent implements OnInit { | |||
31 | private confirmService: ConfirmService, | 28 | private confirmService: ConfirmService, |
32 | private videoChannelService: VideoChannelService, | 29 | private videoChannelService: VideoChannelService, |
33 | private screenService: ScreenService | 30 | private screenService: ScreenService |
34 | ) {} | 31 | ) {} |
35 | |||
36 | ngOnInit () { | ||
37 | this.user = this.authService.getUser() | ||
38 | |||
39 | this.loadVideoChannels() | ||
40 | |||
41 | this.channelsSearchChanged | ||
42 | .pipe(debounceTime(500)) | ||
43 | .subscribe(() => { | ||
44 | this.loadVideoChannels() | ||
45 | }) | ||
46 | } | ||
47 | 32 | ||
48 | get isInSmallView () { | 33 | get isInSmallView () { |
49 | return this.screenService.isInSmallView() | 34 | return this.screenService.isInSmallView() |
50 | } | 35 | } |
51 | 36 | ||
52 | resetSearch () { | 37 | onSearch (search: string) { |
53 | this.channelsSearch = '' | 38 | this.search = search |
54 | this.onChannelsSearchChanged() | 39 | this.loadVideoChannels() |
55 | } | ||
56 | |||
57 | onChannelsSearchChanged () { | ||
58 | this.channelsSearchChanged.next() | ||
59 | } | 40 | } |
60 | 41 | ||
61 | async deleteVideoChannel (videoChannel: VideoChannel) { | 42 | async deleteVideoChannel (videoChannel: VideoChannel) { |
@@ -85,8 +66,11 @@ channel with the same name (${videoChannel.name})!`, | |||
85 | 66 | ||
86 | private loadVideoChannels () { | 67 | private loadVideoChannels () { |
87 | this.authService.userInformationLoaded | 68 | this.authService.userInformationLoaded |
88 | .pipe(mergeMap(() => this.videoChannelService.listAccountVideoChannels(this.user.account, null, true, this.channelsSearch))) | 69 | .pipe(mergeMap(() => { |
89 | .subscribe(res => { | 70 | const user = this.authService.getUser() |
71 | |||
72 | return this.videoChannelService.listAccountVideoChannels(user.account, null, true, this.search) | ||
73 | })).subscribe(res => { | ||
90 | this.videoChannels = res.data | 74 | this.videoChannels = res.data |
91 | this.totalItems = res.total | 75 | this.totalItems = res.total |
92 | 76 | ||
diff --git a/client/src/app/+my-library/my-history/my-history.component.html b/client/src/app/+my-library/my-history/my-history.component.html index 9dec64645..45ca37e0d 100644 --- a/client/src/app/+my-library/my-history/my-history.component.html +++ b/client/src/app/+my-library/my-history/my-history.component.html | |||
@@ -5,14 +5,7 @@ | |||
5 | 5 | ||
6 | <div class="top-buttons"> | 6 | <div class="top-buttons"> |
7 | <div class="search-wrapper"> | 7 | <div class="search-wrapper"> |
8 | <div class="input-group has-feedback has-clear"> | 8 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
9 | <input | ||
10 | type="text" name="history-search" id="history-search" i18n-placeholder placeholder="Search your history" | ||
11 | (keyup)="onSearch($event)" | ||
12 | > | ||
13 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
14 | <span class="sr-only" i18n>Clear filters</span> | ||
15 | </div> | ||
16 | </div> | 9 | </div> |
17 | 10 | ||
18 | <div class="history-switch"> | 11 | <div class="history-switch"> |
@@ -26,14 +19,15 @@ | |||
26 | </button> | 19 | </button> |
27 | </div> | 20 | </div> |
28 | 21 | ||
29 | 22 | <my-videos-selection | |
30 | <div class="no-history" i18n *ngIf="hasDoneFirstQuery && videos.length === 0">You don't have any video in your watch history yet.</div> | 23 | [pagination]="pagination" |
31 | 24 | [(videosModel)]="videos" | |
32 | <div myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()" class="videos"> | 25 | [miniatureDisplayOptions]="miniatureDisplayOptions" |
33 | <div class="video" *ngFor="let video of videos"> | 26 | [titlePage]="titlePage" |
34 | <my-video-miniature | 27 | [getVideosObservableFunction]="getVideosObservableFunction" |
35 | [video]="video" [displayAsRow]="true" | 28 | [user]="user" |
36 | (videoRemoved)="removeVideoFromArray(video)" (videoBlocked)="removeVideoFromArray(video)" | 29 | [loadOnInit]="false" |
37 | ></my-video-miniature> | 30 | i18n-noResultMessage noResultMessage="You don't have any video in your watch history yet." |
38 | </div> | 31 | [enableSelection]="false" |
39 | </div> | 32 | #videosSelection |
33 | ></my-videos-selection> | ||
diff --git a/client/src/app/+my-library/my-history/my-history.component.ts b/client/src/app/+my-library/my-history/my-history.component.ts index 1695bd7ad..ad83db7ab 100644 --- a/client/src/app/+my-library/my-history/my-history.component.ts +++ b/client/src/app/+my-library/my-history/my-history.component.ts | |||
@@ -1,36 +1,55 @@ | |||
1 | import { Component, ComponentFactoryResolver, OnDestroy, OnInit } from '@angular/core' | 1 | import { Subject } from 'rxjs' |
2 | import { tap } from 'rxjs/operators' | ||
3 | import { Component, ComponentFactoryResolver, OnInit, ViewChild } from '@angular/core' | ||
2 | import { ActivatedRoute, Router } from '@angular/router' | 4 | import { ActivatedRoute, Router } from '@angular/router' |
3 | import { | 5 | import { |
4 | AuthService, | 6 | AuthService, |
5 | ComponentPagination, | 7 | ComponentPagination, |
6 | ConfirmService, | 8 | ConfirmService, |
9 | DisableForReuseHook, | ||
7 | LocalStorageService, | 10 | LocalStorageService, |
8 | Notifier, | 11 | Notifier, |
9 | ScreenService, | 12 | ScreenService, |
10 | ServerService, | 13 | ServerService, |
14 | User, | ||
11 | UserService | 15 | UserService |
12 | } from '@app/core' | 16 | } from '@app/core' |
13 | import { immutableAssign } from '@app/helpers' | 17 | import { immutableAssign } from '@app/helpers' |
14 | import { UserHistoryService } from '@app/shared/shared-main' | 18 | import { UserHistoryService, Video } from '@app/shared/shared-main' |
15 | import { AbstractVideoList } from '@app/shared/shared-video-miniature' | 19 | import { MiniatureDisplayOptions, VideosSelectionComponent } from '@app/shared/shared-video-miniature' |
16 | import { Subject } from 'rxjs' | ||
17 | import { debounceTime, tap, distinctUntilChanged } from 'rxjs/operators' | ||
18 | 20 | ||
19 | @Component({ | 21 | @Component({ |
20 | templateUrl: './my-history.component.html', | 22 | templateUrl: './my-history.component.html', |
21 | styleUrls: [ './my-history.component.scss' ] | 23 | styleUrls: [ './my-history.component.scss' ] |
22 | }) | 24 | }) |
23 | export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnDestroy { | 25 | export class MyHistoryComponent implements OnInit, DisableForReuseHook { |
26 | @ViewChild('videosSelection', { static: true }) videosSelection: VideosSelectionComponent | ||
27 | |||
24 | titlePage: string | 28 | titlePage: string |
25 | pagination: ComponentPagination = { | 29 | pagination: ComponentPagination = { |
26 | currentPage: 1, | 30 | currentPage: 1, |
27 | itemsPerPage: 5, | 31 | itemsPerPage: 5, |
28 | totalItems: null | 32 | totalItems: null |
29 | } | 33 | } |
34 | |||
30 | videosHistoryEnabled: boolean | 35 | videosHistoryEnabled: boolean |
31 | search: string | ||
32 | 36 | ||
33 | protected searchStream: Subject<string> | 37 | miniatureDisplayOptions: MiniatureDisplayOptions = { |
38 | date: true, | ||
39 | views: true, | ||
40 | by: true, | ||
41 | privacyLabel: false, | ||
42 | privacyText: true, | ||
43 | state: true, | ||
44 | blacklistInfo: true | ||
45 | } | ||
46 | |||
47 | getVideosObservableFunction = this.getVideosObservable.bind(this) | ||
48 | |||
49 | user: User | ||
50 | |||
51 | videos: Video[] = [] | ||
52 | search: string | ||
34 | 53 | ||
35 | constructor ( | 54 | constructor ( |
36 | protected router: Router, | 55 | protected router: Router, |
@@ -45,45 +64,31 @@ export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnD | |||
45 | private userHistoryService: UserHistoryService, | 64 | private userHistoryService: UserHistoryService, |
46 | protected cfr: ComponentFactoryResolver | 65 | protected cfr: ComponentFactoryResolver |
47 | ) { | 66 | ) { |
48 | super() | ||
49 | |||
50 | this.titlePage = $localize`My watch history` | 67 | this.titlePage = $localize`My watch history` |
51 | } | 68 | } |
52 | 69 | ||
53 | ngOnInit () { | 70 | ngOnInit () { |
54 | super.ngOnInit() | 71 | this.user = this.authService.getUser() |
55 | 72 | ||
56 | this.authService.userInformationLoaded | 73 | this.authService.userInformationLoaded |
57 | .subscribe(() => { | 74 | .subscribe(() => this.videosHistoryEnabled = this.user.videosHistoryEnabled) |
58 | this.videosHistoryEnabled = this.authService.getUser().videosHistoryEnabled | 75 | } |
59 | }) | ||
60 | |||
61 | this.searchStream = new Subject() | ||
62 | 76 | ||
63 | this.searchStream | 77 | disableForReuse () { |
64 | .pipe( | 78 | this.videosSelection.disableForReuse() |
65 | debounceTime(400), | ||
66 | distinctUntilChanged() | ||
67 | ) | ||
68 | .subscribe(search => { | ||
69 | this.search = search | ||
70 | this.reloadVideos() | ||
71 | }) | ||
72 | } | 79 | } |
73 | 80 | ||
74 | onSearch (event: Event) { | 81 | enabledForReuse () { |
75 | const target = event.target as HTMLInputElement | 82 | this.videosSelection.enabledForReuse() |
76 | this.searchStream.next(target.value) | ||
77 | } | 83 | } |
78 | 84 | ||
79 | resetSearch () { | 85 | reloadData () { |
80 | const searchInput = document.getElementById('history-search') as HTMLInputElement | 86 | this.videosSelection.reloadVideos() |
81 | searchInput.value = '' | ||
82 | this.searchStream.next('') | ||
83 | } | 87 | } |
84 | 88 | ||
85 | ngOnDestroy () { | 89 | onSearch (search: string) { |
86 | super.ngOnDestroy() | 90 | this.search = search |
91 | this.reloadData() | ||
87 | } | 92 | } |
88 | 93 | ||
89 | getVideosObservable (page: number) { | 94 | getVideosObservable (page: number) { |
@@ -129,7 +134,7 @@ export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnD | |||
129 | () => { | 134 | () => { |
130 | this.notifier.success($localize`Videos history deleted`) | 135 | this.notifier.success($localize`Videos history deleted`) |
131 | 136 | ||
132 | this.reloadVideos() | 137 | this.reloadData() |
133 | }, | 138 | }, |
134 | 139 | ||
135 | err => this.notifier.error(err.message) | 140 | err => this.notifier.error(err.message) |
diff --git a/client/src/app/+my-library/my-ownership/my-ownership.component.ts b/client/src/app/+my-library/my-ownership/my-ownership.component.ts index a938023b4..aaf028474 100644 --- a/client/src/app/+my-library/my-ownership/my-ownership.component.ts +++ b/client/src/app/+my-library/my-ownership/my-ownership.component.ts | |||
@@ -48,18 +48,18 @@ export class MyOwnershipComponent extends RestTable implements OnInit { | |||
48 | } | 48 | } |
49 | 49 | ||
50 | accepted () { | 50 | accepted () { |
51 | this.loadData() | 51 | this.reloadData() |
52 | } | 52 | } |
53 | 53 | ||
54 | refuse (videoChangeOwnership: VideoChangeOwnership) { | 54 | refuse (videoChangeOwnership: VideoChangeOwnership) { |
55 | this.videoOwnershipService.refuseOwnership(videoChangeOwnership.id) | 55 | this.videoOwnershipService.refuseOwnership(videoChangeOwnership.id) |
56 | .subscribe( | 56 | .subscribe( |
57 | () => this.loadData(), | 57 | () => this.reloadData(), |
58 | err => this.notifier.error(err.message) | 58 | err => this.notifier.error(err.message) |
59 | ) | 59 | ) |
60 | } | 60 | } |
61 | 61 | ||
62 | protected loadData () { | 62 | protected reloadData () { |
63 | return this.videoOwnershipService.getOwnershipChanges(this.pagination, this.sort) | 63 | return this.videoOwnershipService.getOwnershipChanges(this.pagination, this.sort) |
64 | .subscribe( | 64 | .subscribe( |
65 | resultList => { | 65 | resultList => { |
diff --git a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html index 853d47fe6..f91cebacf 100644 --- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html +++ b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html | |||
@@ -7,12 +7,7 @@ | |||
7 | </h1> | 7 | </h1> |
8 | 8 | ||
9 | <div class="video-subscriptions-header"> | 9 | <div class="video-subscriptions-header"> |
10 | <div class="has-feedback has-clear"> | 10 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
11 | <input type="text" placeholder="Search your subscriptions" i18n-placeholder [(ngModel)]="subscriptionsSearch" | ||
12 | (ngModelChange)="onSubscriptionsSearchChanged()" /> | ||
13 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
14 | <span class="sr-only" i18n>Clear filters</span> | ||
15 | </div> | ||
16 | </div> | 11 | </div> |
17 | 12 | ||
18 | <div class="no-results" i18n *ngIf="pagination.totalItems === 0">You don't have any subscription yet.</div> | 13 | <div class="no-results" i18n *ngIf="pagination.totalItems === 0">You don't have any subscription yet.</div> |
diff --git a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss index 53ceaa250..6c1ddf716 100644 --- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss +++ b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss | |||
@@ -58,6 +58,7 @@ input[type=text] { | |||
58 | 58 | ||
59 | .video-subscriptions-header { | 59 | .video-subscriptions-header { |
60 | margin-bottom: 30px; | 60 | margin-bottom: 30px; |
61 | display: flex; | ||
61 | } | 62 | } |
62 | 63 | ||
63 | @media screen and (max-width: $small-view) { | 64 | @media screen and (max-width: $small-view) { |
diff --git a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts index 3b748eccf..1f4a931a0 100644 --- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts +++ b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import { Subject } from 'rxjs' | 1 | import { Subject } from 'rxjs' |
2 | import { debounceTime } from 'rxjs/operators' | 2 | import { Component } from '@angular/core' |
3 | import { Component, OnInit } from '@angular/core' | ||
4 | import { ComponentPagination, Notifier } from '@app/core' | 3 | import { ComponentPagination, Notifier } from '@app/core' |
5 | import { VideoChannel } from '@app/shared/shared-main' | 4 | import { VideoChannel } from '@app/shared/shared-main' |
6 | import { UserSubscriptionService } from '@app/shared/shared-user-subscription' | 5 | import { UserSubscriptionService } from '@app/shared/shared-user-subscription' |
@@ -9,7 +8,7 @@ import { UserSubscriptionService } from '@app/shared/shared-user-subscription' | |||
9 | templateUrl: './my-subscriptions.component.html', | 8 | templateUrl: './my-subscriptions.component.html', |
10 | styleUrls: [ './my-subscriptions.component.scss' ] | 9 | styleUrls: [ './my-subscriptions.component.scss' ] |
11 | }) | 10 | }) |
12 | export class MySubscriptionsComponent implements OnInit { | 11 | export class MySubscriptionsComponent { |
13 | videoChannels: VideoChannel[] = [] | 12 | videoChannels: VideoChannel[] = [] |
14 | 13 | ||
15 | pagination: ComponentPagination = { | 14 | pagination: ComponentPagination = { |
@@ -20,34 +19,13 @@ export class MySubscriptionsComponent implements OnInit { | |||
20 | 19 | ||
21 | onDataSubject = new Subject<any[]>() | 20 | onDataSubject = new Subject<any[]>() |
22 | 21 | ||
23 | subscriptionsSearch: string | 22 | search: string |
24 | subscriptionsSearchChanged = new Subject<string>() | ||
25 | 23 | ||
26 | constructor ( | 24 | constructor ( |
27 | private userSubscriptionService: UserSubscriptionService, | 25 | private userSubscriptionService: UserSubscriptionService, |
28 | private notifier: Notifier | 26 | private notifier: Notifier |
29 | ) {} | 27 | ) {} |
30 | 28 | ||
31 | ngOnInit () { | ||
32 | this.loadSubscriptions() | ||
33 | |||
34 | this.subscriptionsSearchChanged | ||
35 | .pipe(debounceTime(500)) | ||
36 | .subscribe(() => { | ||
37 | this.pagination.currentPage = 1 | ||
38 | this.loadSubscriptions(false) | ||
39 | }) | ||
40 | } | ||
41 | |||
42 | resetSearch () { | ||
43 | this.subscriptionsSearch = '' | ||
44 | this.onSubscriptionsSearchChanged() | ||
45 | } | ||
46 | |||
47 | onSubscriptionsSearchChanged () { | ||
48 | this.subscriptionsSearchChanged.next() | ||
49 | } | ||
50 | |||
51 | onNearOfBottom () { | 29 | onNearOfBottom () { |
52 | // Last page | 30 | // Last page |
53 | if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return | 31 | if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return |
@@ -56,8 +34,13 @@ export class MySubscriptionsComponent implements OnInit { | |||
56 | this.loadSubscriptions() | 34 | this.loadSubscriptions() |
57 | } | 35 | } |
58 | 36 | ||
37 | onSearch (search: string) { | ||
38 | this.search = search | ||
39 | this.loadSubscriptions(false) | ||
40 | } | ||
41 | |||
59 | private loadSubscriptions (more = true) { | 42 | private loadSubscriptions (more = true) { |
60 | this.userSubscriptionService.listSubscriptions({ pagination: this.pagination, search: this.subscriptionsSearch }) | 43 | this.userSubscriptionService.listSubscriptions({ pagination: this.pagination, search: this.search }) |
61 | .subscribe( | 44 | .subscribe( |
62 | res => { | 45 | res => { |
63 | this.videoChannels = more | 46 | this.videoChannels = more |
diff --git a/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts b/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts index d6d7d7a1b..359535526 100644 --- a/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts +++ b/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts | |||
@@ -62,7 +62,7 @@ export class MyVideoImportsComponent extends RestTable implements OnInit { | |||
62 | return '/videos/update/' + video.uuid | 62 | return '/videos/update/' + video.uuid |
63 | } | 63 | } |
64 | 64 | ||
65 | protected loadData () { | 65 | protected reloadData () { |
66 | this.videoImportService.getMyVideoImports(this.pagination, this.sort) | 66 | this.videoImportService.getMyVideoImports(this.pagination, this.sort) |
67 | .subscribe( | 67 | .subscribe( |
68 | resultList => { | 68 | resultList => { |
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html index b88ea3db7..309afcf13 100644 --- a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html +++ b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html | |||
@@ -4,12 +4,7 @@ | |||
4 | </h1> | 4 | </h1> |
5 | 5 | ||
6 | <div class="video-playlists-header d-flex justify-content-between"> | 6 | <div class="video-playlists-header d-flex justify-content-between"> |
7 | <div class="has-feedback has-clear"> | 7 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
8 | <input type="text" placeholder="Search your playlists" i18n-placeholder [(ngModel)]="videoPlaylistsSearch" | ||
9 | (ngModelChange)="onVideoPlaylistSearchChanged()" /> | ||
10 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
11 | <span class="sr-only" i18n>Clear filters</span> | ||
12 | </div> | ||
13 | 8 | ||
14 | <a class="create-button" routerLink="create"> | 9 | <a class="create-button" routerLink="create"> |
15 | <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> | 10 | <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> |
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.ts b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.ts index f6d394923..d90102693 100644 --- a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.ts +++ b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Subject } from 'rxjs' | 1 | import { Subject } from 'rxjs' |
2 | import { debounceTime, mergeMap } from 'rxjs/operators' | 2 | import { mergeMap } from 'rxjs/operators' |
3 | import { Component, OnInit } from '@angular/core' | 3 | import { Component } from '@angular/core' |
4 | import { AuthService, ComponentPagination, ConfirmService, Notifier, User } from '@app/core' | 4 | import { AuthService, ComponentPagination, ConfirmService, Notifier } from '@app/core' |
5 | import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' | 5 | import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' |
6 | import { VideoPlaylistType } from '@shared/models' | 6 | import { VideoPlaylistType } from '@shared/models' |
7 | 7 | ||
@@ -9,10 +9,8 @@ import { VideoPlaylistType } from '@shared/models' | |||
9 | templateUrl: './my-video-playlists.component.html', | 9 | templateUrl: './my-video-playlists.component.html', |
10 | styleUrls: [ './my-video-playlists.component.scss' ] | 10 | styleUrls: [ './my-video-playlists.component.scss' ] |
11 | }) | 11 | }) |
12 | export class MyVideoPlaylistsComponent implements OnInit { | 12 | export class MyVideoPlaylistsComponent { |
13 | videoPlaylistsSearch: string | ||
14 | videoPlaylists: VideoPlaylist[] = [] | 13 | videoPlaylists: VideoPlaylist[] = [] |
15 | videoPlaylistSearchChanged = new Subject<string>() | ||
16 | 14 | ||
17 | pagination: ComponentPagination = { | 15 | pagination: ComponentPagination = { |
18 | currentPage: 1, | 16 | currentPage: 1, |
@@ -22,27 +20,14 @@ export class MyVideoPlaylistsComponent implements OnInit { | |||
22 | 20 | ||
23 | onDataSubject = new Subject<any[]>() | 21 | onDataSubject = new Subject<any[]>() |
24 | 22 | ||
25 | private user: User | 23 | search: string |
26 | 24 | ||
27 | constructor ( | 25 | constructor ( |
28 | private authService: AuthService, | 26 | private authService: AuthService, |
29 | private notifier: Notifier, | 27 | private notifier: Notifier, |
30 | private confirmService: ConfirmService, | 28 | private confirmService: ConfirmService, |
31 | private videoPlaylistService: VideoPlaylistService | 29 | private videoPlaylistService: VideoPlaylistService |
32 | ) {} | 30 | ) {} |
33 | |||
34 | ngOnInit () { | ||
35 | this.user = this.authService.getUser() | ||
36 | |||
37 | this.loadVideoPlaylists() | ||
38 | |||
39 | this.videoPlaylistSearchChanged | ||
40 | .pipe( | ||
41 | debounceTime(500)) | ||
42 | .subscribe(() => { | ||
43 | this.loadVideoPlaylists(true) | ||
44 | }) | ||
45 | } | ||
46 | 31 | ||
47 | async deleteVideoPlaylist (videoPlaylist: VideoPlaylist) { | 32 | async deleteVideoPlaylist (videoPlaylist: VideoPlaylist) { |
48 | const res = await this.confirmService.confirm( | 33 | const res = await this.confirmService.confirm( |
@@ -76,22 +61,20 @@ export class MyVideoPlaylistsComponent implements OnInit { | |||
76 | this.loadVideoPlaylists() | 61 | this.loadVideoPlaylists() |
77 | } | 62 | } |
78 | 63 | ||
79 | resetSearch () { | 64 | onSearch (search: string) { |
80 | this.videoPlaylistsSearch = '' | 65 | this.search = search |
81 | this.onVideoPlaylistSearchChanged() | 66 | this.loadVideoPlaylists(true) |
82 | } | ||
83 | |||
84 | onVideoPlaylistSearchChanged () { | ||
85 | this.videoPlaylistSearchChanged.next() | ||
86 | } | 67 | } |
87 | 68 | ||
88 | private loadVideoPlaylists (reset = false) { | 69 | private loadVideoPlaylists (reset = false) { |
89 | this.authService.userInformationLoaded | 70 | this.authService.userInformationLoaded |
90 | .pipe(mergeMap(() => { | 71 | .pipe(mergeMap(() => { |
91 | return this.videoPlaylistService.listAccountPlaylists(this.user.account, this.pagination, '-updatedAt', this.videoPlaylistsSearch) | 72 | const user = this.authService.getUser() |
92 | })) | 73 | |
93 | .subscribe(res => { | 74 | return this.videoPlaylistService.listAccountPlaylists(user.account, this.pagination, '-updatedAt', this.search) |
75 | })).subscribe(res => { | ||
94 | if (reset) this.videoPlaylists = [] | 76 | if (reset) this.videoPlaylists = [] |
77 | |||
95 | this.videoPlaylists = this.videoPlaylists.concat(res.data) | 78 | this.videoPlaylists = this.videoPlaylists.concat(res.data) |
96 | this.pagination.totalItems = res.total | 79 | this.pagination.totalItems = res.total |
97 | 80 | ||
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.html b/client/src/app/+my-library/my-videos/my-videos.component.html index 7c1cdb511..8d8b482ad 100644 --- a/client/src/app/+my-library/my-videos/my-videos.component.html +++ b/client/src/app/+my-library/my-videos/my-videos.component.html | |||
@@ -19,7 +19,7 @@ | |||
19 | </h1> | 19 | </h1> |
20 | 20 | ||
21 | <div class="videos-header d-flex justify-content-between"> | 21 | <div class="videos-header d-flex justify-content-between"> |
22 | <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)" (resetTableFilter)="resetTableFilter()"></my-advanced-input-filter> | 22 | <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter> |
23 | 23 | ||
24 | <div class="peertube-select-container peertube-select-button"> | 24 | <div class="peertube-select-container peertube-select-button"> |
25 | <select [(ngModel)]="sort" (ngModelChange)="onChangeSortColumn()" class="form-control"> | 25 | <select [(ngModel)]="sort" (ngModelChange)="onChangeSortColumn()" class="form-control"> |
@@ -41,6 +41,7 @@ | |||
41 | [titlePage]="titlePage" | 41 | [titlePage]="titlePage" |
42 | [getVideosObservableFunction]="getVideosObservableFunction" | 42 | [getVideosObservableFunction]="getVideosObservableFunction" |
43 | [user]="user" | 43 | [user]="user" |
44 | [loadOnInit]="false" | ||
44 | #videosSelection | 45 | #videosSelection |
45 | > | 46 | > |
46 | <ng-template ptTemplate="globalButtons"> | 47 | <ng-template ptTemplate="globalButtons"> |
@@ -59,6 +60,5 @@ | |||
59 | </ng-template> | 60 | </ng-template> |
60 | </my-videos-selection> | 61 | </my-videos-selection> |
61 | 62 | ||
62 | |||
63 | <my-video-change-ownership #videoChangeOwnershipModal></my-video-change-ownership> | 63 | <my-video-change-ownership #videoChangeOwnershipModal></my-video-change-ownership> |
64 | <my-live-stream-information #liveStreamInformationModal></my-live-stream-information> | 64 | <my-live-stream-information #liveStreamInformationModal></my-live-stream-information> |
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.ts b/client/src/app/+my-library/my-videos/my-videos.component.ts index f9c1b32b0..1e4a4406d 100644 --- a/client/src/app/+my-library/my-videos/my-videos.component.ts +++ b/client/src/app/+my-library/my-videos/my-videos.component.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import { concat, Observable } from 'rxjs' | 1 | import { concat, Observable } from 'rxjs' |
2 | import { tap, toArray } from 'rxjs/operators' | 2 | import { tap, toArray } from 'rxjs/operators' |
3 | import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core' | 3 | import { Component, OnInit, ViewChild } from '@angular/core' |
4 | import { ActivatedRoute, Router } from '@angular/router' | 4 | import { ActivatedRoute, Router } from '@angular/router' |
5 | import { AuthService, ComponentPagination, ConfirmService, Notifier, RouteFilter, ScreenService, ServerService, User } from '@app/core' | 5 | import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User } from '@app/core' |
6 | import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' | 6 | import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' |
7 | import { immutableAssign } from '@app/helpers' | 7 | import { immutableAssign } from '@app/helpers' |
8 | import { AdvancedInputFilter } from '@app/shared/shared-forms' | 8 | import { AdvancedInputFilter } from '@app/shared/shared-forms' |
@@ -16,7 +16,7 @@ import { VideoChangeOwnershipComponent } from './modals/video-change-ownership.c | |||
16 | templateUrl: './my-videos.component.html', | 16 | templateUrl: './my-videos.component.html', |
17 | styleUrls: [ './my-videos.component.scss' ] | 17 | styleUrls: [ './my-videos.component.scss' ] |
18 | }) | 18 | }) |
19 | export class MyVideosComponent extends RouteFilter implements OnInit, AfterViewInit, DisableForReuseHook { | 19 | export class MyVideosComponent implements OnInit, DisableForReuseHook { |
20 | @ViewChild('videosSelection', { static: true }) videosSelection: VideosSelectionComponent | 20 | @ViewChild('videosSelection', { static: true }) videosSelection: VideosSelectionComponent |
21 | @ViewChild('videoChangeOwnershipModal', { static: true }) videoChangeOwnershipModal: VideoChangeOwnershipComponent | 21 | @ViewChild('videoChangeOwnershipModal', { static: true }) videoChangeOwnershipModal: VideoChangeOwnershipComponent |
22 | @ViewChild('liveStreamInformationModal', { static: true }) liveStreamInformationModal: LiveStreamInformationComponent | 22 | @ViewChild('liveStreamInformationModal', { static: true }) liveStreamInformationModal: LiveStreamInformationComponent |
@@ -42,6 +42,7 @@ export class MyVideosComponent extends RouteFilter implements OnInit, AfterViewI | |||
42 | 42 | ||
43 | videos: Video[] = [] | 43 | videos: Video[] = [] |
44 | getVideosObservableFunction = this.getVideosObservable.bind(this) | 44 | getVideosObservableFunction = this.getVideosObservable.bind(this) |
45 | |||
45 | sort: VideoSortField = '-publishedAt' | 46 | sort: VideoSortField = '-publishedAt' |
46 | 47 | ||
47 | user: User | 48 | user: User |
@@ -53,6 +54,8 @@ export class MyVideosComponent extends RouteFilter implements OnInit, AfterViewI | |||
53 | } | 54 | } |
54 | ] | 55 | ] |
55 | 56 | ||
57 | private search: string | ||
58 | |||
56 | constructor ( | 59 | constructor ( |
57 | protected router: Router, | 60 | protected router: Router, |
58 | protected serverService: ServerService, | 61 | protected serverService: ServerService, |
@@ -63,8 +66,6 @@ export class MyVideosComponent extends RouteFilter implements OnInit, AfterViewI | |||
63 | private confirmService: ConfirmService, | 66 | private confirmService: ConfirmService, |
64 | private videoService: VideoService | 67 | private videoService: VideoService |
65 | ) { | 68 | ) { |
66 | super() | ||
67 | |||
68 | this.titlePage = $localize`My videos` | 69 | this.titlePage = $localize`My videos` |
69 | } | 70 | } |
70 | 71 | ||
@@ -72,16 +73,14 @@ export class MyVideosComponent extends RouteFilter implements OnInit, AfterViewI | |||
72 | this.buildActions() | 73 | this.buildActions() |
73 | 74 | ||
74 | this.user = this.authService.getUser() | 75 | this.user = this.authService.getUser() |
75 | |||
76 | this.initSearch() | ||
77 | this.listenToSearchChange() | ||
78 | } | 76 | } |
79 | 77 | ||
80 | ngAfterViewInit () { | 78 | onSearch (search: string) { |
81 | if (this.search) this.setTableFilter(this.search, false) | 79 | this.search = search |
80 | this.reloadData() | ||
82 | } | 81 | } |
83 | 82 | ||
84 | loadData () { | 83 | reloadData () { |
85 | this.videosSelection.reloadVideos() | 84 | this.videosSelection.reloadVideos() |
86 | } | 85 | } |
87 | 86 | ||
diff --git a/client/src/app/core/rest/rest-table.ts b/client/src/app/core/rest/rest-table.ts index 9baab8a39..a5b48f10c 100644 --- a/client/src/app/core/rest/rest-table.ts +++ b/client/src/app/core/rest/rest-table.ts | |||
@@ -1,25 +1,22 @@ | |||
1 | import * as debug from 'debug' | 1 | import * as debug from 'debug' |
2 | import { LazyLoadEvent, SortMeta } from 'primeng/api' | 2 | import { LazyLoadEvent, SortMeta } from 'primeng/api' |
3 | import { Subject } from 'rxjs' | ||
4 | import { ActivatedRoute, Router } from '@angular/router' | 3 | import { ActivatedRoute, Router } from '@angular/router' |
5 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' | 4 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' |
6 | import { RouteFilter } from '../routing' | ||
7 | import { RestPagination } from './rest-pagination' | 5 | import { RestPagination } from './rest-pagination' |
8 | 6 | ||
9 | const logger = debug('peertube:tables:RestTable') | 7 | const logger = debug('peertube:tables:RestTable') |
10 | 8 | ||
11 | export abstract class RestTable extends RouteFilter { | 9 | export abstract class RestTable { |
12 | 10 | ||
13 | abstract totalRecords: number | 11 | abstract totalRecords: number |
14 | abstract sort: SortMeta | 12 | abstract sort: SortMeta |
15 | abstract pagination: RestPagination | 13 | abstract pagination: RestPagination |
16 | 14 | ||
17 | search: string | ||
18 | rowsPerPageOptions = [ 10, 20, 50, 100 ] | 15 | rowsPerPageOptions = [ 10, 20, 50, 100 ] |
19 | rowsPerPage = this.rowsPerPageOptions[0] | 16 | rowsPerPage = this.rowsPerPageOptions[0] |
20 | expandedRows = {} | 17 | expandedRows = {} |
21 | 18 | ||
22 | protected searchStream: Subject<string> | 19 | search: string |
23 | 20 | ||
24 | protected route: ActivatedRoute | 21 | protected route: ActivatedRoute |
25 | protected router: Router | 22 | protected router: Router |
@@ -28,7 +25,6 @@ export abstract class RestTable extends RouteFilter { | |||
28 | 25 | ||
29 | initialize () { | 26 | initialize () { |
30 | this.loadSort() | 27 | this.loadSort() |
31 | this.initSearch() | ||
32 | } | 28 | } |
33 | 29 | ||
34 | loadSort () { | 30 | loadSort () { |
@@ -56,7 +52,7 @@ export abstract class RestTable extends RouteFilter { | |||
56 | count: this.rowsPerPage | 52 | count: this.rowsPerPage |
57 | } | 53 | } |
58 | 54 | ||
59 | this.loadData() | 55 | this.reloadData() |
60 | this.saveSort() | 56 | this.saveSort() |
61 | } | 57 | } |
62 | 58 | ||
@@ -74,13 +70,18 @@ export abstract class RestTable extends RouteFilter { | |||
74 | count: this.rowsPerPage | 70 | count: this.rowsPerPage |
75 | } | 71 | } |
76 | 72 | ||
77 | this.loadData() | 73 | this.reloadData() |
78 | } | 74 | } |
79 | 75 | ||
80 | this.expandedRows = {} | 76 | this.expandedRows = {} |
81 | } | 77 | } |
82 | 78 | ||
83 | protected abstract loadData (): void | 79 | onSearch (search: string) { |
80 | this.search = search | ||
81 | this.reloadData() | ||
82 | } | ||
83 | |||
84 | protected abstract reloadData (): void | ||
84 | 85 | ||
85 | private getSortLocalStorageKey () { | 86 | private getSortLocalStorageKey () { |
86 | return 'rest-table-sort-' + this.getIdentifier() | 87 | return 'rest-table-sort-' + this.getIdentifier() |
diff --git a/client/src/app/core/routing/index.ts b/client/src/app/core/routing/index.ts index d53a4ae2c..239c27caf 100644 --- a/client/src/app/core/routing/index.ts +++ b/client/src/app/core/routing/index.ts | |||
@@ -5,7 +5,6 @@ export * from './login-guard.service' | |||
5 | export * from './menu-guard.service' | 5 | export * from './menu-guard.service' |
6 | export * from './preload-selected-modules-list' | 6 | export * from './preload-selected-modules-list' |
7 | export * from './redirect.service' | 7 | export * from './redirect.service' |
8 | export * from './route-filter' | ||
9 | export * from './server-config-resolver.service' | 8 | export * from './server-config-resolver.service' |
10 | export * from './unlogged-guard.service' | 9 | export * from './unlogged-guard.service' |
11 | export * from './user-right-guard.service' | 10 | export * from './user-right-guard.service' |
diff --git a/client/src/app/core/routing/route-filter.ts b/client/src/app/core/routing/route-filter.ts deleted file mode 100644 index d2eed7cd4..000000000 --- a/client/src/app/core/routing/route-filter.ts +++ /dev/null | |||
@@ -1,79 +0,0 @@ | |||
1 | import * as debug from 'debug' | ||
2 | import { Subject } from 'rxjs' | ||
3 | import { debounceTime, distinctUntilChanged } from 'rxjs/operators' | ||
4 | import { ActivatedRoute, Params, Router } from '@angular/router' | ||
5 | |||
6 | const logger = debug('peertube:tables:RouteFilter') | ||
7 | |||
8 | export abstract class RouteFilter { | ||
9 | search: string | ||
10 | |||
11 | protected searchStream: Subject<string> | ||
12 | |||
13 | protected route: ActivatedRoute | ||
14 | protected router: Router | ||
15 | |||
16 | initSearch () { | ||
17 | this.searchStream = new Subject() | ||
18 | |||
19 | this.searchStream | ||
20 | .pipe( | ||
21 | debounceTime(200), | ||
22 | distinctUntilChanged() | ||
23 | ) | ||
24 | .subscribe(search => { | ||
25 | this.search = search | ||
26 | |||
27 | logger('On search %s.', this.search) | ||
28 | |||
29 | this.loadData() | ||
30 | }) | ||
31 | } | ||
32 | |||
33 | onSearch (event: Event) { | ||
34 | const target = event.target as HTMLInputElement | ||
35 | this.searchStream.next(target.value) | ||
36 | |||
37 | this.setQueryParams(target.value) | ||
38 | } | ||
39 | |||
40 | resetTableFilter () { | ||
41 | this.setTableFilter('') | ||
42 | this.setQueryParams('') | ||
43 | this.resetSearch() | ||
44 | } | ||
45 | |||
46 | resetSearch () { | ||
47 | this.searchStream.next('') | ||
48 | this.setTableFilter('') | ||
49 | } | ||
50 | |||
51 | listenToSearchChange () { | ||
52 | this.route.queryParams | ||
53 | .subscribe(params => { | ||
54 | this.search = params.search || '' | ||
55 | |||
56 | // Primeng table will run an event to load data | ||
57 | this.setTableFilter(this.search) | ||
58 | }) | ||
59 | } | ||
60 | |||
61 | setTableFilter (filter: string, triggerEvent = true) { | ||
62 | // FIXME: cannot use ViewChild, so create a component for the filter input | ||
63 | const filterInput = document.getElementById('table-filter') as HTMLInputElement | ||
64 | if (!filterInput) return | ||
65 | |||
66 | filterInput.value = filter | ||
67 | |||
68 | if (triggerEvent) filterInput.dispatchEvent(new Event('keyup')) | ||
69 | } | ||
70 | |||
71 | protected abstract loadData (): void | ||
72 | |||
73 | private setQueryParams (search: string) { | ||
74 | const queryParams: Params = {} | ||
75 | |||
76 | if (search) Object.assign(queryParams, { search }) | ||
77 | this.router.navigate([ ], { queryParams }) | ||
78 | } | ||
79 | } | ||
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html index 22f84a96e..b1c065c7a 100644 --- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html +++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html | |||
@@ -1,6 +1,7 @@ | |||
1 | <p-table | 1 | <p-table |
2 | [value]="abuses" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" | 2 | [value]="abuses" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" |
3 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" [resizableColumns]="true" | 3 | [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true" |
4 | [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" | ||
4 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate | 5 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate |
5 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} reports" | 6 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} reports" |
6 | (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" | 7 | (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" |
@@ -8,7 +9,7 @@ | |||
8 | <ng-template pTemplate="caption"> | 9 | <ng-template pTemplate="caption"> |
9 | <div class="caption"> | 10 | <div class="caption"> |
10 | <div class="ml-auto"> | 11 | <div class="ml-auto"> |
11 | <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)" (resetTableFilter)="resetTableFilter()"></my-advanced-input-filter> | 12 | <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter> |
12 | </div> | 13 | </div> |
13 | </div> | 14 | </div> |
14 | </ng-template> | 15 | </ng-template> |
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts index f393c0d1e..4dc2b4f10 100644 --- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts +++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts | |||
@@ -3,7 +3,7 @@ import truncate from 'lodash-es/truncate' | |||
3 | import { SortMeta } from 'primeng/api' | 3 | import { SortMeta } from 'primeng/api' |
4 | import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' | 4 | import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' |
5 | import { environment } from 'src/environments/environment' | 5 | import { environment } from 'src/environments/environment' |
6 | import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core' | 6 | import { Component, Input, OnInit, ViewChild } from '@angular/core' |
7 | import { DomSanitizer } from '@angular/platform-browser' | 7 | import { DomSanitizer } from '@angular/platform-browser' |
8 | import { ActivatedRoute, Router } from '@angular/router' | 8 | import { ActivatedRoute, Router } from '@angular/router' |
9 | import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' | 9 | import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' |
@@ -11,10 +11,10 @@ import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared | |||
11 | import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation' | 11 | import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation' |
12 | import { VideoCommentService } from '@app/shared/shared-video-comment' | 12 | import { VideoCommentService } from '@app/shared/shared-video-comment' |
13 | import { AbuseState, AdminAbuse } from '@shared/models' | 13 | import { AbuseState, AdminAbuse } from '@shared/models' |
14 | import { AdvancedInputFilter } from '../shared-forms' | ||
14 | import { AbuseMessageModalComponent } from './abuse-message-modal.component' | 15 | import { AbuseMessageModalComponent } from './abuse-message-modal.component' |
15 | import { ModerationCommentModalComponent } from './moderation-comment-modal.component' | 16 | import { ModerationCommentModalComponent } from './moderation-comment-modal.component' |
16 | import { ProcessedAbuse } from './processed-abuse.model' | 17 | import { ProcessedAbuse } from './processed-abuse.model' |
17 | import { AdvancedInputFilter } from '../shared-forms' | ||
18 | 18 | ||
19 | const logger = debug('peertube:moderation:AbuseListTableComponent') | 19 | const logger = debug('peertube:moderation:AbuseListTableComponent') |
20 | 20 | ||
@@ -23,7 +23,7 @@ const logger = debug('peertube:moderation:AbuseListTableComponent') | |||
23 | templateUrl: './abuse-list-table.component.html', | 23 | templateUrl: './abuse-list-table.component.html', |
24 | styleUrls: [ '../shared-moderation/moderation.scss', './abuse-list-table.component.scss' ] | 24 | styleUrls: [ '../shared-moderation/moderation.scss', './abuse-list-table.component.scss' ] |
25 | }) | 25 | }) |
26 | export class AbuseListTableComponent extends RestTable implements OnInit, AfterViewInit { | 26 | export class AbuseListTableComponent extends RestTable implements OnInit { |
27 | @Input() viewType: 'admin' | 'user' | 27 | @Input() viewType: 'admin' | 'user' |
28 | 28 | ||
29 | @ViewChild('abuseMessagesModal', { static: true }) abuseMessagesModal: AbuseMessageModalComponent | 29 | @ViewChild('abuseMessagesModal', { static: true }) abuseMessagesModal: AbuseMessageModalComponent |
@@ -89,11 +89,6 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV | |||
89 | ] | 89 | ] |
90 | 90 | ||
91 | this.initialize() | 91 | this.initialize() |
92 | this.listenToSearchChange() | ||
93 | } | ||
94 | |||
95 | ngAfterViewInit () { | ||
96 | if (this.search) this.setTableFilter(this.search, false) | ||
97 | } | 92 | } |
98 | 93 | ||
99 | isAdminView () { | 94 | isAdminView () { |
@@ -109,7 +104,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV | |||
109 | } | 104 | } |
110 | 105 | ||
111 | onModerationCommentUpdated () { | 106 | onModerationCommentUpdated () { |
112 | this.loadData() | 107 | this.reloadData() |
113 | } | 108 | } |
114 | 109 | ||
115 | isAbuseAccepted (abuse: AdminAbuse) { | 110 | isAbuseAccepted (abuse: AdminAbuse) { |
@@ -152,7 +147,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV | |||
152 | this.abuseService.removeAbuse(abuse).subscribe( | 147 | this.abuseService.removeAbuse(abuse).subscribe( |
153 | () => { | 148 | () => { |
154 | this.notifier.success($localize`Abuse deleted.`) | 149 | this.notifier.success($localize`Abuse deleted.`) |
155 | this.loadData() | 150 | this.reloadData() |
156 | }, | 151 | }, |
157 | 152 | ||
158 | err => this.notifier.error(err.message) | 153 | err => this.notifier.error(err.message) |
@@ -162,7 +157,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV | |||
162 | updateAbuseState (abuse: AdminAbuse, state: AbuseState) { | 157 | updateAbuseState (abuse: AdminAbuse, state: AbuseState) { |
163 | this.abuseService.updateAbuse(abuse, { state }) | 158 | this.abuseService.updateAbuse(abuse, { state }) |
164 | .subscribe( | 159 | .subscribe( |
165 | () => this.loadData(), | 160 | () => this.reloadData(), |
166 | 161 | ||
167 | err => this.notifier.error(err.message) | 162 | err => this.notifier.error(err.message) |
168 | ) | 163 | ) |
@@ -189,7 +184,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV | |||
189 | return Actor.IS_LOCAL(abuse.reporterAccount.host) | 184 | return Actor.IS_LOCAL(abuse.reporterAccount.host) |
190 | } | 185 | } |
191 | 186 | ||
192 | protected loadData () { | 187 | protected reloadData () { |
193 | logger('Loading data.') | 188 | logger('Loading data.') |
194 | 189 | ||
195 | const options = { | 190 | const options = { |
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.html b/client/src/app/shared/shared-forms/advanced-input-filter.component.html index 03c4f127b..10d1296cf 100644 --- a/client/src/app/shared/shared-forms/advanced-input-filter.component.html +++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.html | |||
@@ -1,5 +1,5 @@ | |||
1 | <div class="input-group has-feedback has-clear"> | 1 | <div class="input-group has-feedback has-clear"> |
2 | <div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body"> | 2 | <div *ngIf="hasFilters()" class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body"> |
3 | <div class="input-group-text" ngbDropdownToggle> | 3 | <div class="input-group-text" ngbDropdownToggle> |
4 | <span class="caret" aria-haspopup="menu" role="button"></span> | 4 | <span class="caret" aria-haspopup="menu" role="button"></span> |
5 | </div> | 5 | </div> |
@@ -10,13 +10,15 @@ | |||
10 | <a *ngFor="let filter of filters" [routerLink]="[ '.' ]" [queryParams]="filter.queryParams" class="dropdown-item"> | 10 | <a *ngFor="let filter of filters" [routerLink]="[ '.' ]" [queryParams]="filter.queryParams" class="dropdown-item"> |
11 | {{ filter.label }} | 11 | {{ filter.label }} |
12 | </a> | 12 | </a> |
13 | |||
14 | </div> | 13 | </div> |
15 | </div> | 14 | </div> |
15 | |||
16 | <input | 16 | <input |
17 | type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..." | 17 | type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..." |
18 | (keyup)="onSearch($event)" | 18 | [(ngModel)]="searchValue" |
19 | (keyup)="onInputSearch($event)" | ||
19 | > | 20 | > |
21 | |||
20 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="onResetTableFilter()"></a> | 22 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="onResetTableFilter()"></a> |
21 | <span class="sr-only" i18n>Clear filters</span> | 23 | <span class="sr-only" i18n>Clear filters</span> |
22 | </div> | 24 | </div> |
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.ts b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts index 394090751..1b0eed34b 100644 --- a/client/src/app/shared/shared-forms/advanced-input-filter.component.ts +++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts | |||
@@ -1,27 +1,88 @@ | |||
1 | import { Component, EventEmitter, Input, Output } from '@angular/core' | 1 | import * as debug from 'debug' |
2 | import { Params } from '@angular/router' | 2 | import { Subject } from 'rxjs' |
3 | import { debounceTime, distinctUntilChanged } from 'rxjs/operators' | ||
4 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' | ||
5 | import { ActivatedRoute, Params, Router } from '@angular/router' | ||
3 | 6 | ||
4 | export type AdvancedInputFilter = { | 7 | export type AdvancedInputFilter = { |
5 | label: string | 8 | label: string |
6 | queryParams: Params | 9 | queryParams: Params |
7 | } | 10 | } |
8 | 11 | ||
12 | const logger = debug('peertube:AdvancedInputFilterComponent') | ||
13 | |||
9 | @Component({ | 14 | @Component({ |
10 | selector: 'my-advanced-input-filter', | 15 | selector: 'my-advanced-input-filter', |
11 | templateUrl: './advanced-input-filter.component.html', | 16 | templateUrl: './advanced-input-filter.component.html', |
12 | styleUrls: [ './advanced-input-filter.component.scss' ] | 17 | styleUrls: [ './advanced-input-filter.component.scss' ] |
13 | }) | 18 | }) |
14 | export class AdvancedInputFilterComponent { | 19 | export class AdvancedInputFilterComponent implements OnInit { |
15 | @Input() filters: AdvancedInputFilter[] = [] | 20 | @Input() filters: AdvancedInputFilter[] = [] |
16 | 21 | ||
17 | @Output() resetTableFilter = new EventEmitter<void>() | 22 | @Output() search = new EventEmitter<string>() |
18 | @Output() search = new EventEmitter<Event>() | 23 | |
24 | searchValue: string | ||
25 | |||
26 | private searchStream: Subject<string> | ||
27 | |||
28 | constructor ( | ||
29 | private route: ActivatedRoute, | ||
30 | private router: Router | ||
31 | ) { } | ||
32 | |||
33 | ngOnInit () { | ||
34 | this.initSearchStream() | ||
35 | this.listenToRouteSearchChange() | ||
36 | } | ||
19 | 37 | ||
20 | onSearch (event: Event) { | 38 | onInputSearch (event: Event) { |
21 | this.search.emit(event) | 39 | this.updateSearch((event.target as HTMLInputElement).value) |
22 | } | 40 | } |
23 | 41 | ||
24 | onResetTableFilter () { | 42 | onResetTableFilter () { |
25 | this.resetTableFilter.emit() | 43 | this.updateSearch('') |
44 | } | ||
45 | |||
46 | hasFilters () { | ||
47 | return this.filters.length !== 0 | ||
48 | } | ||
49 | |||
50 | private updateSearch (value: string) { | ||
51 | this.searchValue = value | ||
52 | this.searchStream.next(this.searchValue) | ||
53 | } | ||
54 | |||
55 | private listenToRouteSearchChange () { | ||
56 | this.route.queryParams | ||
57 | .subscribe(params => { | ||
58 | const search = params.search || '' | ||
59 | |||
60 | logger('On route search change "%s".', search) | ||
61 | |||
62 | this.updateSearch(search) | ||
63 | }) | ||
64 | } | ||
65 | |||
66 | private initSearchStream () { | ||
67 | this.searchStream = new Subject() | ||
68 | |||
69 | this.searchStream | ||
70 | .pipe( | ||
71 | debounceTime(200), | ||
72 | distinctUntilChanged() | ||
73 | ) | ||
74 | .subscribe(() => { | ||
75 | logger('On search "%s".', this.searchValue) | ||
76 | |||
77 | this.setQueryParams(this.searchValue) | ||
78 | this.search.emit(this.searchValue) | ||
79 | }) | ||
80 | } | ||
81 | |||
82 | private setQueryParams (search: string) { | ||
83 | const queryParams: Params = {} | ||
84 | |||
85 | if (search) Object.assign(queryParams, { search }) | ||
86 | this.router.navigate([ ], { queryParams }) | ||
26 | } | 87 | } |
27 | } | 88 | } |
diff --git a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss index 84dd7dce3..ffabb3646 100644 --- a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss +++ b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss | |||
@@ -11,12 +11,12 @@ | |||
11 | } | 11 | } |
12 | } | 12 | } |
13 | 13 | ||
14 | ::ng-deep .dropdown-toggle::after { | 14 | .sub-menu ::ng-deep .dropdown-toggle::after { |
15 | position: relative; | 15 | position: relative; |
16 | top: 2px; | 16 | top: 2px; |
17 | } | 17 | } |
18 | 18 | ||
19 | ::ng-deep .dropdown-menu { | 19 | .sub-menu ::ng-deep .dropdown-menu { |
20 | margin-top: 0 !important; | 20 | margin-top: 0 !important; |
21 | } | 21 | } |
22 | 22 | ||
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.html b/client/src/app/shared/shared-moderation/account-blocklist.component.html index e914a7c3c..a9fac0810 100644 --- a/client/src/app/shared/shared-moderation/account-blocklist.component.html +++ b/client/src/app/shared/shared-moderation/account-blocklist.component.html | |||
@@ -11,13 +11,8 @@ | |||
11 | > | 11 | > |
12 | <ng-template pTemplate="caption"> | 12 | <ng-template pTemplate="caption"> |
13 | <div class="caption"> | 13 | <div class="caption"> |
14 | <div class="ml-auto has-feedback has-clear"> | 14 | <div class="ml-auto"> |
15 | <input | 15 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
16 | type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..." | ||
17 | (keyup)="onSearch($event)" | ||
18 | > | ||
19 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
20 | <span class="sr-only" i18n>Clear filters</span> | ||
21 | </div> | 16 | </div> |
22 | </div> | 17 | </div> |
23 | </ng-template> | 18 | </ng-template> |
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.scss b/client/src/app/shared/shared-moderation/account-blocklist.component.scss index 63a9df823..bc441811e 100644 --- a/client/src/app/shared/shared-moderation/account-blocklist.component.scss +++ b/client/src/app/shared/shared-moderation/account-blocklist.component.scss | |||
@@ -1,16 +1,6 @@ | |||
1 | @import '_variables'; | 1 | @import '_variables'; |
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | .caption { | ||
5 | justify-content: flex-end; | ||
6 | |||
7 | input { | ||
8 | @include peertube-input-text(250px); | ||
9 | |||
10 | flex-grow: 1; | ||
11 | } | ||
12 | } | ||
13 | |||
14 | .chip { | 4 | .chip { |
15 | @include chip; | 5 | @include chip; |
16 | } | 6 | } |
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.ts b/client/src/app/shared/shared-moderation/account-blocklist.component.ts index 1bce65bf0..1146aeec0 100644 --- a/client/src/app/shared/shared-moderation/account-blocklist.component.ts +++ b/client/src/app/shared/shared-moderation/account-blocklist.component.ts | |||
@@ -44,12 +44,12 @@ export class GenericAccountBlocklistComponent extends RestTable implements OnIni | |||
44 | : $localize`Account ${blockedAccount.nameWithHost} unmuted by your instance.` | 44 | : $localize`Account ${blockedAccount.nameWithHost} unmuted by your instance.` |
45 | ) | 45 | ) |
46 | 46 | ||
47 | this.loadData() | 47 | this.reloadData() |
48 | } | 48 | } |
49 | ) | 49 | ) |
50 | } | 50 | } |
51 | 51 | ||
52 | protected loadData () { | 52 | protected reloadData () { |
53 | const operation = this.mode === BlocklistComponentType.Account | 53 | const operation = this.mode === BlocklistComponentType.Account |
54 | ? this.blocklistService.getUserAccountBlocklist({ | 54 | ? this.blocklistService.getUserAccountBlocklist({ |
55 | pagination: this.pagination, | 55 | pagination: this.pagination, |
diff --git a/client/src/app/shared/shared-moderation/moderation.scss b/client/src/app/shared/shared-moderation/moderation.scss index ab43d8457..b13d06f03 100644 --- a/client/src/app/shared/shared-moderation/moderation.scss +++ b/client/src/app/shared/shared-moderation/moderation.scss | |||
@@ -39,27 +39,10 @@ | |||
39 | } | 39 | } |
40 | } | 40 | } |
41 | 41 | ||
42 | .input-group { | ||
43 | @include peertube-input-group(300px); | ||
44 | |||
45 | .dropdown-toggle::after { | ||
46 | margin-left: 0; | ||
47 | } | ||
48 | } | ||
49 | |||
50 | .chip { | 42 | .chip { |
51 | @include chip; | 43 | @include chip; |
52 | } | 44 | } |
53 | 45 | ||
54 | .caption { | ||
55 | justify-content: flex-end; | ||
56 | |||
57 | input { | ||
58 | @include peertube-input-text(250px); | ||
59 | flex-grow: 1; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | my-action-dropdown.show { | 46 | my-action-dropdown.show { |
64 | ::ng-deep .dropdown-root { | 47 | ::ng-deep .dropdown-root { |
65 | display: block !important; | 48 | display: block !important; |
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.html b/client/src/app/shared/shared-moderation/server-blocklist.component.html index 537186f05..c6d29bb21 100644 --- a/client/src/app/shared/shared-moderation/server-blocklist.component.html +++ b/client/src/app/shared/shared-moderation/server-blocklist.component.html | |||
@@ -4,8 +4,9 @@ | |||
4 | </h1> | 4 | </h1> |
5 | 5 | ||
6 | <p-table | 6 | <p-table |
7 | [value]="blockedServers" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" | 7 | [value]="blockedServers" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" |
8 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)" | 8 | [sortField]="sort.field" [sortOrder]="sort.order" (onPage)="onPage($event)" |
9 | [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" | ||
9 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate | 10 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate |
10 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted instances" | 11 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted instances" |
11 | > | 12 | > |
@@ -18,13 +19,8 @@ | |||
18 | </a> | 19 | </a> |
19 | </div> | 20 | </div> |
20 | 21 | ||
21 | <div class="ml-auto has-feedback has-clear"> | 22 | <div class="ml-auto"> |
22 | <input | 23 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
23 | type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..." | ||
24 | (keyup)="onSearch($event)" | ||
25 | > | ||
26 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
27 | <span class="sr-only" i18n>Clear filters</span> | ||
28 | </div> | 24 | </div> |
29 | </div> | 25 | </div> |
30 | </ng-template> | 26 | </ng-template> |
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.scss b/client/src/app/shared/shared-moderation/server-blocklist.component.scss index af21c0c20..a22972c5f 100644 --- a/client/src/app/shared/shared-moderation/server-blocklist.component.scss +++ b/client/src/app/shared/shared-moderation/server-blocklist.component.scss | |||
@@ -16,15 +16,6 @@ a { | |||
16 | } | 16 | } |
17 | } | 17 | } |
18 | 18 | ||
19 | .caption { | ||
20 | justify-content: flex-end; | ||
21 | |||
22 | input { | ||
23 | @include peertube-input-text(250px); | ||
24 | flex-grow: 1; | ||
25 | } | ||
26 | } | ||
27 | |||
28 | .unblock-button { | 19 | .unblock-button { |
29 | @include peertube-button; | 20 | @include peertube-button; |
30 | @include grey-button; | 21 | @include grey-button; |
@@ -34,15 +25,6 @@ a { | |||
34 | @include create-button; | 25 | @include create-button; |
35 | } | 26 | } |
36 | 27 | ||
37 | .caption { | ||
38 | justify-content: flex-end; | ||
39 | |||
40 | input { | ||
41 | @include peertube-input-text(250px); | ||
42 | flex-grow: 1; | ||
43 | } | ||
44 | } | ||
45 | |||
46 | .chip { | 28 | .chip { |
47 | @include chip; | 29 | @include chip; |
48 | } | 30 | } |
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.ts b/client/src/app/shared/shared-moderation/server-blocklist.component.ts index 546fd53c3..274d8f6e9 100644 --- a/client/src/app/shared/shared-moderation/server-blocklist.component.ts +++ b/client/src/app/shared/shared-moderation/server-blocklist.component.ts | |||
@@ -46,7 +46,7 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit | |||
46 | : $localize`Instance ${host} unmuted by your instance.` | 46 | : $localize`Instance ${host} unmuted by your instance.` |
47 | ) | 47 | ) |
48 | 48 | ||
49 | this.loadData() | 49 | this.reloadData() |
50 | } | 50 | } |
51 | ) | 51 | ) |
52 | } | 52 | } |
@@ -69,13 +69,13 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit | |||
69 | : $localize`Instance ${domain} muted by your instance.` | 69 | : $localize`Instance ${domain} muted by your instance.` |
70 | ) | 70 | ) |
71 | 71 | ||
72 | this.loadData() | 72 | this.reloadData() |
73 | } | 73 | } |
74 | ) | 74 | ) |
75 | }) | 75 | }) |
76 | } | 76 | } |
77 | 77 | ||
78 | protected loadData () { | 78 | protected reloadData () { |
79 | const operation = this.mode === BlocklistComponentType.Account | 79 | const operation = this.mode === BlocklistComponentType.Account |
80 | ? this.blocklistService.getUserServerBlocklist({ | 80 | ? this.blocklistService.getUserServerBlocklist({ |
81 | pagination: this.pagination, | 81 | pagination: this.pagination, |
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.html b/client/src/app/shared/shared-video-miniature/videos-selection.component.html index dec9e99f3..4ee90ce7f 100644 --- a/client/src/app/shared/shared-video-miniature/videos-selection.component.html +++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.html | |||
@@ -1,9 +1,9 @@ | |||
1 | <div class="no-results" i18n *ngIf="hasDoneFirstQuery && videos.length === 0">No results.</div> | 1 | <div class="no-results" i18n *ngIf="hasDoneFirstQuery && videos.length === 0">{{ noResultMessage }}</div> |
2 | 2 | ||
3 | <div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()" class="videos"> | 3 | <div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()" class="videos"> |
4 | <div class="video" *ngFor="let video of videos; let i = index; trackBy: videoById"> | 4 | <div class="video" *ngFor="let video of videos; let i = index; trackBy: videoById"> |
5 | 5 | ||
6 | <div class="checkbox-container"> | 6 | <div class="checkbox-container" *ngIf="enableSelection"> |
7 | <my-peertube-checkbox [inputName]="'video-check-' + video.id" [(ngModel)]="_selection[video.id]"></my-peertube-checkbox> | 7 | <my-peertube-checkbox [inputName]="'video-check-' + video.id" [(ngModel)]="_selection[video.id]"></my-peertube-checkbox> |
8 | </div> | 8 | </div> |
9 | 9 | ||
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts index f8c3800d7..d64ee9b98 100644 --- a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts +++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts | |||
@@ -31,6 +31,9 @@ export class VideosSelectionComponent extends AbstractVideoList implements OnIni | |||
31 | @Input() pagination: ComponentPagination | 31 | @Input() pagination: ComponentPagination |
32 | @Input() titlePage: string | 32 | @Input() titlePage: string |
33 | @Input() miniatureDisplayOptions: MiniatureDisplayOptions | 33 | @Input() miniatureDisplayOptions: MiniatureDisplayOptions |
34 | @Input() noResultMessage = $localize`No results.` | ||
35 | @Input() enableSelection = true | ||
36 | @Input() loadOnInit = true | ||
34 | 37 | ||
35 | @Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<ResultList<Video>> | 38 | @Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<ResultList<Video>> |
36 | 39 | ||