]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Improve advanced input filter
authorChocobozzz <me@florianbigard.com>
Wed, 3 Nov 2021 13:23:55 +0000 (14:23 +0100)
committerChocobozzz <me@florianbigard.com>
Wed, 3 Nov 2021 13:23:55 +0000 (14:23 +0100)
14 files changed:
client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts
client/src/app/+admin/overview/users/user-list/user-list.component.ts
client/src/app/+admin/overview/videos/video-admin.service.ts
client/src/app/+admin/overview/videos/video-list.component.ts
client/src/app/+my-library/my-follows/my-followers.component.ts
client/src/app/+my-library/my-videos/my-videos.component.ts
client/src/app/core/rest/rest.service.ts
client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
client/src/app/shared/shared-forms/advanced-input-filter.component.html
client/src/app/shared/shared-forms/advanced-input-filter.component.scss
client/src/app/shared/shared-forms/advanced-input-filter.component.ts
shared/core-utils/utils/array.ts [new file with mode: 0644]
shared/core-utils/utils/index.ts

index 1fe8d0f9d38c5dac3fa19b5b1b3d11136923229f..dca746f4edd1cbaf6ff5444d9527bcd668def172 100644 (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`
         }
       ]
index a60b228af8e1df7107b44bc316366d00730242eb..25fe6513301329591ab2591bf9f4272012182847 100644 (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`
         }
       ]
index 548e6e80fcdfdac851080a114c683da5018073ce..9fba11cbdb4e3d1b246adfd6d126c323b0157f47 100644 (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`
         }
       ]
index b90fe22d85e06f6dd2fc2c7ab3cfdc493ec41236..f80de7acdfb5c228fdd89200e276d474f9899a37 100644 (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`
           }
         ]
index 635552cf52fd5f76bc9d4417d24cda6639774f46..0f98a5d33b5d698ff1541caa963f1de828d3dee7 100644 (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'
   }
index 4a72b983f95b1eeaa0a9d7dafffc7469dcf0cf1a..0dd9bf6f5fa212ee52128abb9a6cbcb0105e4472 100644 (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
         }
       })
index a117d0915dfe9b65a7d0b0d1fb2fb208c20c4eaf..261e87f996f8881e090f5564057ef1d634baa460 100644 (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`
             }
           ]
index 59152e65855bee70df5ae0b6f300222b9fb64db3..b2a5a3f72a13cd749111cb6bdcad6bbe3373bd45 100644 (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)
+  }
 }
index 10f5861b9f0e264cb48d19bc38d85627141275e2..b902726faa1fb42c4bf6960ece364df6911fa7f6 100644 (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`
         }
       ]
index c662b9bb6858cb4e5560104523fe3af401609083..7031cb53b1240f0b1c456675fbef257bfa0b9dc6 100644 (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>
index 07a43761cfc76084780b9102a653c8b9be2d60b3..ee1b3b5085bfcce7d5d5f82d9e3eedbd1516b21c 100644 (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;
+  }
+}
index a12dddf7ab20077d997ee62c8a8e0f7b2e3c46a6..d8aeaa0fa3fceb133d2176192a87c8913cdbfb6f 100644 (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)
+        }
+      }
+    }
+  }
 }
diff --git a/shared/core-utils/utils/array.ts b/shared/core-utils/utils/array.ts
new file mode 100644 (file)
index 0000000..9e326a5
--- /dev/null
@@ -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
+}
index a71977d8805b0912c1faa619f8e174daf593b973..8d16365a87f9106e9f776f2978105e43639ac775 100644 (file)
@@ -1 +1,2 @@
+export * from './array'
 export * from './object'