1
0
Fork 0

Improve advanced input filter

This commit is contained in:
Chocobozzz 2021-11-03 14:23:55 +01:00
parent d324756edb
commit dd6d2a7ce5
No known key found for this signature in database
GPG key ID: 583A612D890159BE
14 changed files with 131 additions and 33 deletions

View file

@ -30,11 +30,11 @@ export class VideoBlockListComponent extends RestTable implements OnInit {
title: $localize`Advanced filters`,
children: [
{
queryParams: { search: 'type:auto' },
value: 'type:auto',
label: $localize`Automatic blocks`
},
{
queryParams: { search: 'type:manual' },
value: 'type:manual',
label: $localize`Manual blocks`
}
]

View file

@ -47,11 +47,11 @@ export class VideoCommentListComponent extends RestTable implements OnInit {
title: $localize`Advanced filters`,
children: [
{
queryParams: { search: 'local:true' },
value: 'local:true',
label: $localize`Local comments`
},
{
queryParams: { search: 'local:false' },
value: 'local:false',
label: $localize`Remote comments`
}
]

View file

@ -39,7 +39,7 @@ export class UserListComponent extends RestTable implements OnInit {
title: $localize`Advanced filters`,
children: [
{
queryParams: { search: 'banned:true' },
value: 'banned:true',
label: $localize`Banned users`
}
]

View file

@ -44,11 +44,11 @@ export class VideoAdminService {
title: $localize`Video type`,
children: [
{
queryParams: { search: 'isLive:false' },
value: 'isLive:false',
label: $localize`VOD`
},
{
queryParams: { search: 'isLive:true' },
value: 'isLive:true',
label: $localize`Live`
}
]
@ -58,19 +58,19 @@ export class VideoAdminService {
title: $localize`Video files`,
children: [
{
queryParams: { search: 'webtorrent:true' },
value: 'webtorrent:true',
label: $localize`With WebTorrent`
},
{
queryParams: { search: 'webtorrent:false' },
value: 'webtorrent:false',
label: $localize`Without WebTorrent`
},
{
queryParams: { search: 'hls:true' },
value: 'hls:true',
label: $localize`With HLS`
},
{
queryParams: { search: 'hls:false' },
value: 'hls:false',
label: $localize`Without HLS`
}
]
@ -80,11 +80,11 @@ export class VideoAdminService {
title: $localize`Videos scope`,
children: [
{
queryParams: { search: 'isLocal:false' },
value: 'isLocal:false',
label: $localize`Remote videos`
},
{
queryParams: { search: 'isLocal:true' },
value: 'isLocal:true',
label: $localize`Local videos`
}
]
@ -94,7 +94,7 @@ export class VideoAdminService {
title: $localize`Exclude`,
children: [
{
queryParams: { search: 'excludeMuted' },
value: 'excludeMuted',
label: $localize`Exclude muted accounts`
}
]

View file

@ -86,7 +86,7 @@ export class VideoListComponent extends RestTable implements OnInit {
}
getPrivacyBadgeClass (video: Video) {
if (video.privacy.id === VideoPrivacy.PUBLIC) return 'badge-blue'
if (video.privacy.id === VideoPrivacy.PUBLIC) return 'badge-green'
return 'badge-yellow'
}

View file

@ -39,7 +39,7 @@ export class MyFollowersComponent implements OnInit {
this.auth.userInformationLoaded.subscribe(() => {
const channelFilters = this.auth.getUser().videoChannels.map(c => {
return {
queryParams: { search: 'channel:' + c.name },
value: 'channel:' + c.name,
label: c.name
}
})

View file

@ -82,7 +82,7 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
const channelFilters = this.userChannels.map(c => {
return {
queryParams: { search: 'channel:' + c.name },
value: 'channel:' + c.name,
label: c.name
}
})
@ -92,7 +92,7 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
title: $localize`Advanced filters`,
children: [
{
queryParams: { search: 'isLive:true' },
value: 'isLive:true',
label: $localize`Only live videos`
}
]

View file

@ -82,13 +82,11 @@ export class RestService {
parseQueryStringFilter <T extends QueryStringFilterPrefixes> (q: string, prefixes: T): ParseQueryStringFiltersResult<keyof T> {
if (!q) return {}
// Tokenize the strings using spaces that are not in quotes
const tokens = q.match(/(?:[^\s"]+|"[^"]*")+/g)
.filter(token => !!token)
const tokens = this.tokenizeString(q)
// Build prefix array
const prefixeStrings = Object.values(prefixes)
.map(p => p.prefix)
.map(p => p.prefix)
logger(`Built tokens "${tokens.join(', ')}" for prefixes "${prefixeStrings.join(', ')}"`)
@ -137,4 +135,12 @@ export class RestService {
...additionalFilters
}
}
tokenizeString (q: string) {
if (!q) return []
// Tokenize the strings using spaces that are not in quotes
return q.match(/(?:[^\s"]+|"[^"]*")+/g)
.filter(token => !!token)
}
}

View file

@ -39,23 +39,23 @@ export class AbuseListTableComponent extends RestTable implements OnInit {
title: $localize`Advanced filters`,
children: [
{
queryParams: { search: 'state:pending' },
value: 'state:pending',
label: $localize`Unsolved reports`
},
{
queryParams: { search: 'state:accepted' },
value: 'state:accepted',
label: $localize`Accepted reports`
},
{
queryParams: { search: 'state:rejected' },
value: 'state:rejected',
label: $localize`Refused reports`
},
{
queryParams: { search: 'videoIs:blacklisted' },
value: 'videoIs:blacklisted',
label: $localize`Reports with blocked videos`
},
{
queryParams: { search: 'videoIs:deleted' },
value: 'videoIs:deleted',
label: $localize`Reports with deleted videos`
}
]

View file

@ -8,9 +8,11 @@
<ng-container *ngFor="let group of filters">
<h6 class="dropdown-header">{{ group.title }}</h6>
<a *ngFor="let filter of group.children" [routerLink]="[ '.' ]" [queryParams]="filter.queryParams" class="dropdown-item">
<button *ngFor="let filter of group.children" (click)="onFilterClick(filter)" class="dropdown-item">
<my-global-icon [ngClass]="{ 'no-visible': !isFilterEnabled(filter) }" iconName="tick"></my-global-icon>
{{ filter.label }}
</a>
</button>
</ng-container>
</div>
</div>

View file

@ -1,6 +1,10 @@
@use '_variables' as *;
@use '_mixins' as *;
.dropdown-item {
font-size: 14px;
}
input {
@include peertube-input-text(250px);
}
@ -8,3 +12,22 @@ input {
.input-group-text {
background-color: transparent;
}
my-global-icon {
$size: 18px;
$margin: 2px;
@include margin-right($margin);
opacity: 1;
width: 18px;
height: 18px;
&.no-visible {
@include margin-right($size + $margin);
width: 0;
height: 0;
}
}

View file

@ -3,14 +3,17 @@ import { Subject } from 'rxjs'
import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { ActivatedRoute, Params, Router } from '@angular/router'
import { RestService } from '@app/core'
export type AdvancedInputFilter = {
title: string
children: {
label: string
queryParams: Params
}[]
children: AdvancedInputFilterChild[]
}
export type AdvancedInputFilterChild = {
label: string
value: string
}
const logger = debug('peertube:AdvancedInputFilterComponent')
@ -28,6 +31,8 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
searchValue: string
private enabledFilters = new Set<string>()
private searchStream: Subject<string>
private viewInitialized = false
@ -35,6 +40,7 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
constructor (
private route: ActivatedRoute,
private restService: RestService,
private router: Router
) { }
@ -62,6 +68,18 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
return this.filters && this.filters.length !== 0
}
isFilterEnabled (filter: AdvancedInputFilterChild) {
return this.enabledFilters.has(filter.value)
}
onFilterClick (filter: AdvancedInputFilterChild) {
const newSearch = this.isFilterEnabled(filter)
? this.removeFilterToSearch(this.searchValue, filter)
: this.addFilterToSearch(this.searchValue, filter)
this.router.navigate([ '.' ], { relativeTo: this.route, queryParams: { search: newSearch.trim() } })
}
private scheduleSearchUpdate (value: string) {
this.searchValue = value
this.searchStream.next(this.searchValue)
@ -71,6 +89,7 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
this.searchValue = value
this.setQueryParams(this.searchValue)
this.parseFilters(this.searchValue)
this.emitSearch()
}
@ -84,6 +103,9 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
if (this.searchValue === search) return
this.searchValue = search
this.parseFilters(this.searchValue)
this.emitSearch()
})
}
@ -98,6 +120,7 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
)
.subscribe(() => {
this.setQueryParams(this.searchValue)
this.parseFilters(this.searchValue)
this.emitSearch()
})
@ -120,4 +143,34 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
if (search) Object.assign(queryParams, { search })
this.router.navigate([ ], { queryParams })
}
private removeFilterToSearch (search: string, removedFilter: AdvancedInputFilterChild) {
return search.replace(removedFilter.value, '')
}
private addFilterToSearch (search: string, newFilter: AdvancedInputFilterChild) {
const prefix = newFilter.value.split(':').shift()
// Tokenize search and remove a potential existing filter
const tokens = this.restService.tokenizeString(search)
.filter(t => !t.startsWith(prefix))
tokens.push(newFilter.value)
return tokens.join(' ')
}
private parseFilters (search: string) {
const tokens = this.restService.tokenizeString(search)
this.enabledFilters = new Set()
for (const group of this.filters) {
for (const filter of group.children) {
if (tokens.includes(filter.value)) {
this.enabledFilters.add(filter.value)
}
}
}
}
}

View file

@ -0,0 +1,13 @@
function findCommonElement <T> (array1: T[], array2: T[]) {
for (const a of array1) {
for (const b of array2) {
if (a === b) return a
}
}
return null
}
export {
findCommonElement
}

View file

@ -1 +1,2 @@
export * from './array'
export * from './object'