Verified Commit 1fd61899 authored by Chocobozzz's avatar Chocobozzz
Browse files

Add ability to filter my videos by live

parent dfcb6f50
......@@ -3,4 +3,4 @@
<ng-container i18n>Reports</ng-container>
</h1>
<my-abuse-list-table viewType="admin" baseRoute="/admin/moderation/abuses/list"></my-abuse-list-table>
<my-abuse-list-table viewType="admin"></my-abuse-list-table>
......@@ -13,25 +13,7 @@
<ng-template pTemplate="caption">
<div class="caption">
<div class="ml-auto">
<div class="input-group has-feedback has-clear">
<div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
<div class="input-group-text" ngbDropdownToggle>
<span class="caret" aria-haspopup="menu" role="button"></span>
</div>
<div role="menu" ngbDropdownMenu>
<h6 class="dropdown-header" i18n>Advanced block filters</h6>
<a [routerLink]="[ '/admin/moderation/video-blocks/list' ]" [queryParams]="{ 'search': 'type:auto' }" class="dropdown-item" i18n>Automatic blocks</a>
<a [routerLink]="[ '/admin/moderation/video-blocks/list' ]" [queryParams]="{ 'search': 'type:manual' }" class="dropdown-item" i18n>Manual blocks</a>
</div>
</div>
<input
type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
(keyup)="onSearch($event)"
>
<a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetTableFilter()"></a>
<span class="sr-only" i18n>Clear filters</span>
</div>
<my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)" (resetTableFilter)="resetTableFilter()"></my-advanced-input-filter>
</div>
</div>
</ng-template>
......
......@@ -6,6 +6,7 @@ import { AfterViewInit, Component, OnInit } from '@angular/core'
import { DomSanitizer } from '@angular/platform-browser'
import { ActivatedRoute, Params, Router } from '@angular/router'
import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
import { AdvancedInputFilter } from '@app/shared/shared-forms'
import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
import { VideoBlockService } from '@app/shared/shared-moderation'
import { VideoBlacklist, VideoBlacklistType } from '@shared/models'
......@@ -24,6 +25,17 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
videoBlocklistActions: DropdownAction<VideoBlacklist>[][] = []
inputFilters: AdvancedInputFilter[] = [
{
queryParams: { 'search': 'type:auto' },
label: $localize`Automatic blocks`
},
{
queryParams: { 'search': 'type:manual' },
label: $localize`Manual blocks`
}
]
constructor (
protected route: ActivatedRoute,
protected router: Router,
......@@ -111,25 +123,6 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
if (this.search) this.setTableFilter(this.search, false)
}
/* Table filter functions */
onBlockSearch (event: Event) {
this.onSearch(event)
this.setQueryParams((event.target as HTMLInputElement).value)
}
setQueryParams (search: string) {
const queryParams: Params = {}
if (search) Object.assign(queryParams, { search })
this.router.navigate([ '/admin/moderation/video-blocks/list' ], { queryParams })
}
resetTableFilter () {
this.setTableFilter('')
this.setQueryParams('')
this.resetSearch()
}
/* END Table filter functions */
getIdentifier () {
return 'VideoBlockListComponent'
}
......
......@@ -26,25 +26,7 @@
</div>
<div class="ml-auto">
<div class="input-group has-feedback has-clear">
<div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
<div class="input-group-text" ngbDropdownToggle>
<span class="caret" aria-haspopup="menu" role="button"></span>
</div>
<div role="menu" ngbDropdownMenu>
<h6 class="dropdown-header" i18n>Advanced comments filters</h6>
<a [routerLink]="[ '/admin/moderation/video-comments/list' ]" [queryParams]="{ 'search': 'local:true' }" class="dropdown-item" i18n>Local comments</a>
<a [routerLink]="[ '/admin/moderation/video-comments/list' ]" [queryParams]="{ 'search': 'local:false' }" class="dropdown-item" i18n>Remote comments</a>
</div>
</div>
<input
type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
(keyup)="onSearch($event)"
>
<a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetTableFilter()"></a>
<span class="sr-only" i18n>Clear filters</span>
</div>
<my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)" (resetTableFilter)="resetTableFilter()"></my-advanced-input-filter>
</div>
</div>
</ng-template>
......
......@@ -2,6 +2,7 @@ import { SortMeta } from 'primeng/api'
import { AfterViewInit, Component, OnInit } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
import { AuthService, ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core'
import { AdvancedInputFilter } from '@app/shared/shared-forms'
import { DropdownAction } from '@app/shared/shared-main'
import { BulkService } from '@app/shared/shared-moderation'
import { VideoCommentAdmin, VideoCommentService } from '@app/shared/shared-video-comment'
......@@ -43,6 +44,17 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte
selectedComments: VideoCommentAdmin[] = []
bulkCommentActions: DropdownAction<VideoCommentAdmin[]>[] = []
inputFilters: AdvancedInputFilter[] = [
{
queryParams: { 'search': 'local:true' },
label: $localize`Local comments`
},
{
queryParams: { 'search': 'local:false' },
label: $localize`Remote comments`
}
]
get authUser () {
return this.auth.getUser()
}
......
......@@ -22,24 +22,7 @@
</div>
<div class="ml-auto">
<div class="input-group has-feedback has-clear">
<div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
<div class="input-group-text" ngbDropdownToggle>
<span class="caret" aria-haspopup="menu" role="button"></span>
</div>
<div role="menu" ngbDropdownMenu>
<h6 class="dropdown-header" i18n>Advanced user filters</h6>
<a [routerLink]="[ '/admin/users/list' ]" [queryParams]="{ 'search': 'banned:true' }" class="dropdown-item" i18n>Banned users</a>
</div>
</div>
<input
type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
(keyup)="onSearch($event)"
>
<a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetTableFilter()"></a>
<span class="sr-only" i18n>Clear filters</span>
</div>
<my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)" (resetTableFilter)="resetTableFilter()"></my-advanced-input-filter>
</div>
</div>
......
......@@ -11,6 +11,7 @@ tr.banned > td {
.table-email {
@include disable-default-a-behaviour;
color: pvar(--mainForegroundColor);
}
......@@ -28,14 +29,6 @@ tr.banned > td {
margin-left: 0.1rem;
}
.caption {
justify-content: space-between;
input {
@include peertube-input-text(250px);
}
}
p-tableCheckbox {
position: relative;
top: -2.5px;
......@@ -55,18 +48,7 @@ my-global-icon {
.progress {
@include progressbar($small: true);
width: auto;
max-width: 100%;
}
.input-group {
@include peertube-input-group(300px);
input {
flex: 1;
}
.dropdown-toggle::after {
margin-left: 0;
}
}
import { SortMeta } from 'primeng/api'
import { Component, OnInit, ViewChild } from '@angular/core'
import { ActivatedRoute, Params, Router } from '@angular/router'
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService, UserService } from '@app/core'
import { Account, DropdownAction } from '@app/shared/shared-main'
import { AdvancedInputFilter } from '@app/shared/shared-forms'
import { DropdownAction } from '@app/shared/shared-main'
import { UserBanModalComponent } from '@app/shared/shared-moderation'
import { ServerConfig, User, UserRole } from '@shared/models'
......@@ -18,19 +19,28 @@ type UserForList = User & {
templateUrl: './user-list.component.html',
styleUrls: [ './user-list.component.scss' ]
})
export class UserListComponent extends RestTable implements OnInit {
export class UserListComponent extends RestTable implements OnInit, AfterViewInit {
@ViewChild('userBanModal', { static: true }) userBanModal: UserBanModalComponent
users: User[] = []
totalRecords = 0
sort: SortMeta = { field: 'createdAt', order: 1 }
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
highlightBannedUsers = false
selectedUsers: User[] = []
bulkUserActions: DropdownAction<User[]>[][] = []
columns: { id: string, label: string }[]
inputFilters: AdvancedInputFilter[] = [
{
queryParams: { 'search': 'banned:true' },
label: $localize`Banned users`
}
]
private _selectedColumns: string[]
private serverConfig: ServerConfig
......@@ -117,6 +127,10 @@ export class UserListComponent extends RestTable implements OnInit {
this.columns.push({ id: 'lastLoginDate', label: 'Last login' })
}
ngAfterViewInit () {
if (this.search) this.setTableFilter(this.search, false)
}
getIdentifier () {
return 'UserListComponent'
}
......
......@@ -3,4 +3,4 @@
<ng-container i18n>Reports</ng-container>
</h1>
<my-abuse-list-table viewType="user" baseRoute="/my-account/abuses"></my-abuse-list-table>
<my-abuse-list-table viewType="user"></my-abuse-list-table>
......@@ -19,12 +19,7 @@
</h1>
<div class="videos-header d-flex justify-content-between">
<div class="has-feedback has-clear">
<input type="text" placeholder="Search your videos" i18n-placeholder [(ngModel)]="videosSearch"
(ngModelChange)="onVideosSearchChanged()" />
<a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
<span class="sr-only" i18n>Clear filters</span>
</div>
<my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)" (resetTableFilter)="resetTableFilter()"></my-advanced-input-filter>
<div class="peertube-select-container peertube-select-button">
<select [(ngModel)]="sort" (ngModelChange)="onChangeSortColumn()" class="form-control">
......
import { concat, Observable, Subject } from 'rxjs'
import { debounceTime, tap, toArray } from 'rxjs/operators'
import { Component, OnInit, ViewChild } from '@angular/core'
import { concat, Observable } from 'rxjs'
import { tap, toArray } from 'rxjs/operators'
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User } from '@app/core'
import { AuthService, ComponentPagination, ConfirmService, Notifier, RouteFilter, ScreenService, ServerService, User } from '@app/core'
import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
import { immutableAssign } from '@app/helpers'
import { AdvancedInputFilter } from '@app/shared/shared-forms'
import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
import { LiveStreamInformationComponent } from '@app/shared/shared-video-live'
import { MiniatureDisplayOptions, SelectionType, VideosSelectionComponent } from '@app/shared/shared-video-miniature'
......@@ -15,7 +16,7 @@ import { VideoChangeOwnershipComponent } from './modals/video-change-ownership.c
templateUrl: './my-videos.component.html',
styleUrls: [ './my-videos.component.scss' ]
})
export class MyVideosComponent implements OnInit, DisableForReuseHook {
export class MyVideosComponent extends RouteFilter implements OnInit, AfterViewInit, DisableForReuseHook {
@ViewChild('videosSelection', { static: true }) videosSelection: VideosSelectionComponent
@ViewChild('videoChangeOwnershipModal', { static: true }) videoChangeOwnershipModal: VideoChangeOwnershipComponent
@ViewChild('liveStreamInformationModal', { static: true }) liveStreamInformationModal: LiveStreamInformationComponent
......@@ -40,13 +41,18 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
videoActions: DropdownAction<{ video: Video }>[] = []
videos: Video[] = []
videosSearch: string
videosSearchChanged = new Subject<string>()
getVideosObservableFunction = this.getVideosObservable.bind(this)
sort: VideoSortField = '-publishedAt'
user: User
inputFilters: AdvancedInputFilter[] = [
{
queryParams: { 'search': 'isLive:true' },
label: $localize`Only live videos`
}
]
constructor (
protected router: Router,
protected serverService: ServerService,
......@@ -57,6 +63,8 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
private confirmService: ConfirmService,
private videoService: VideoService
) {
super()
this.titlePage = $localize`My videos`
}
......@@ -65,20 +73,16 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
this.user = this.authService.getUser()
this.videosSearchChanged
.pipe(debounceTime(500))
.subscribe(() => {
this.videosSelection.reloadVideos()
})
this.initSearch()
this.listenToSearchChange()
}
resetSearch () {
this.videosSearch = ''
this.onVideosSearchChanged()
ngAfterViewInit () {
if (this.search) this.setTableFilter(this.search, false)
}
onVideosSearchChanged () {
this.videosSearchChanged.next()
loadData () {
this.videosSelection.reloadVideos()
}
onChangeSortColumn () {
......@@ -96,7 +100,7 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
getVideosObservable (page: number) {
const newPagination = immutableAssign(this.pagination, { currentPage: page })
return this.videoService.getMyVideos(newPagination, this.sort, this.videosSearch)
return this.videoService.getMyVideos(newPagination, this.sort, this.search)
.pipe(
tap(res => this.pagination.totalItems = res.total)
)
......
import * as debug from 'debug'
import { LazyLoadEvent, SortMeta } from 'primeng/api'
import { Subject } from 'rxjs'
import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
import { ActivatedRoute, Params, Router } from '@angular/router'
import { ActivatedRoute, Router } from '@angular/router'
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { RouteFilter } from '../routing'
import { RestPagination } from './rest-pagination'
const logger = debug('peertube:tables:RestTable')
export abstract class RestTable {
export abstract class RestTable extends RouteFilter {
abstract totalRecords: number
abstract sort: SortMeta
......@@ -19,8 +19,6 @@ export abstract class RestTable {
rowsPerPage = this.rowsPerPageOptions[0]
expandedRows = {}
baseRoute: string
protected searchStream: Subject<string>
protected route: ActivatedRoute
......@@ -66,55 +64,6 @@ export abstract class RestTable {
peertubeLocalStorage.setItem(this.getSortLocalStorageKey(), JSON.stringify(this.sort))
}
initSearch () {
this.searchStream = new Subject()
this.searchStream
.pipe(
debounceTime(400),
distinctUntilChanged()
)
.subscribe(search => {
this.search = search
logger('On search %s.', this.search)
this.loadData()
})
}
onSearch (event: Event) {
const target = event.target as HTMLInputElement
this.searchStream.next(target.value)
this.setQueryParams((event.target as HTMLInputElement).value)
}
setQueryParams (search: string) {
if (!this.baseRoute) return
const queryParams: Params = {}
if (search) Object.assign(queryParams, { search })
this.router.navigate([ this.baseRoute ], { queryParams })
}
resetTableFilter () {
this.setTableFilter('')
this.setQueryParams('')
this.resetSearch()
}
listenToSearchChange () {
this.route.queryParams
.subscribe(params => {
this.search = params.search || ''
// Primeng table will run an event to load data
this.setTableFilter(this.search)
})
}
onPage (event: { first: number, rows: number }) {
logger('On page %o.', event)
......@@ -131,21 +80,6 @@ export abstract class RestTable {
this.expandedRows = {}
}
setTableFilter (filter: string, triggerEvent = true) {
// FIXME: cannot use ViewChild, so create a component for the filter input
const filterInput = document.getElementById('table-filter') as HTMLInputElement
if (!filterInput) return
filterInput.value = filter
if (triggerEvent) filterInput.dispatchEvent(new Event('keyup'))
}
resetSearch () {
this.searchStream.next('')
this.setTableFilter('')
}
protected abstract loadData (): void
private getSortLocalStorageKey () {
......
......@@ -5,6 +5,7 @@ export * from './login-guard.service'
export * from './menu-guard.service'
export * from './preload-selected-modules-list'
export * from './redirect.service'
export * from './route-filter'
export * from './server-config-resolver.service'
export * from './unlogged-guard.service'
export * from './user-right-guard.service'
import * as debug from 'debug'
import { Subject } from 'rxjs'
import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
import { ActivatedRoute, Params, Router } from '@angular/router'
const logger = debug('peertube:tables:RouteFilter')
export abstract class RouteFilter {
search: string
protected searchStream: Subject<string>
protected route: ActivatedRoute
protected router: Router
initSearch () {
this.searchStream = new Subject()
this.searchStream
.pipe(
debounceTime(400),
distinctUntilChanged()
)
.subscribe(search => {
this.search = search
logger('On search %s.', this.search)
this.loadData()
})
}
onSearch (event: Event) {
const target = event.target as HTMLInputElement
this.searchStream.next(target.value)
this.setQueryParams(target.value)
}
resetTableFilter () {
this.setTableFilter('')
this.setQueryParams('')
this.resetSearch()
}
resetSearch () {
this.searchStream.next('')
this.setTableFilter('')
}
listenToSearchChange () {
this.route.queryParams
.subscribe(params => {
this.search = params.search || ''
// Primeng table will run an event to load data
this.setTableFilter(this.search)
})
}
setTableFilter (filter: string, triggerEvent = true) {
// FIXME: cannot use ViewChild, so create a component for the filter input
const filterInput = document.getElementById('table-filter') as HTMLInputElement
if (!filterInput) return
filterInput.value = filter
if (triggerEvent) filterInput.dispatchEvent(new Event('keyup'))
}
protected abstract loadData (): void
private setQueryParams (search: string) {
const queryParams: Params = {}
if (search) Object.assign(queryParams, { search })
this.router.navigate([ ], { queryParams })
}
}
......@@ -7,7 +7,7 @@
<span class="col-3 moderation-expanded-label" i18n>Reporter</span>
<span class="col-9 moderation-expanded-text">