Fix rowsPerPage change, add filter clear button, update video-abuse-list search query param dynamically
This commit is contained in:
parent
801d957155
commit
25a42e293b
|
@ -6,10 +6,14 @@
|
|||
>
|
||||
<ng-template pTemplate="caption">
|
||||
<div class="caption">
|
||||
<input
|
||||
type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
|
||||
(keyup)="onSearch($event)"
|
||||
>
|
||||
<div class="ml-auto has-feedback has-clear">
|
||||
<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)="resetSearch()"></a>
|
||||
<span class="sr-only" i18n>Clear filters</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
|
|
|
@ -6,11 +6,13 @@
|
|||
>
|
||||
<ng-template pTemplate="caption">
|
||||
<div class="caption">
|
||||
<div class="ml-auto">
|
||||
<div class="ml-auto has-feedback has-clear">
|
||||
<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)="resetSearch()"></a>
|
||||
<span class="sr-only" i18n>Clear filters</span>
|
||||
</div>
|
||||
<a class="ml-2 follow-button" (click)="addDomainsToFollow()" (key.enter)="addDomainsToFollow()">
|
||||
<my-global-icon iconName="add"></my-global-icon>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<p-table
|
||||
[value]="videoRedundancies" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage"
|
||||
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
|
||||
(onPage)="onPage()" [expandedRowKeys]="expandedRows"
|
||||
(onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
|
||||
>
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
|
|
|
@ -6,11 +6,13 @@
|
|||
>
|
||||
<ng-template pTemplate="caption">
|
||||
<div class="caption">
|
||||
<div class="ml-auto">
|
||||
<div class="ml-auto has-feedback has-clear">
|
||||
<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)="resetSearch()"></a>
|
||||
<span class="sr-only" i18n>Clear filters</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
|
|
@ -6,11 +6,13 @@
|
|||
>
|
||||
<ng-template pTemplate="caption">
|
||||
<div class="caption">
|
||||
<div class="ml-auto">
|
||||
<div class="ml-auto has-feedback has-clear">
|
||||
<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)="resetSearch()"></a>
|
||||
<span class="sr-only" i18n>Clear filters</span>
|
||||
</div>
|
||||
<a class="ml-2 block-button" (click)="addServersToBlock()" (key.enter)="addServersToBlock()">
|
||||
<my-global-icon iconName="add"></my-global-icon>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
alt="Avatar"
|
||||
>
|
||||
<div>
|
||||
<span class="text-muted">{{ createByString(videoAbuse.reporterAccount) }}</span>
|
||||
<span class="text-muted">{{ videoAbuse.reporterAccount.nameWithHost }}</span>
|
||||
</div>
|
||||
</a>
|
||||
<a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'reportee:"' + videoAbuse.reporterAccount.displayName + '"' }" class="ml-auto text-muted video-details-links" i18n>
|
||||
|
@ -34,7 +34,7 @@
|
|||
alt="Avatar"
|
||||
>
|
||||
<div>
|
||||
<span class="text-muted">{{ videoAbuse.video.channel.ownerAccount ? createByString(videoAbuse.video.channel.ownerAccount) : '' }}</span>
|
||||
<span class="text-muted">{{ videoAbuse.video.channel.ownerAccount ? videoAbuse.video.channel.ownerAccount.nameWithHost : '' }}</span>
|
||||
</div>
|
||||
</a>
|
||||
<a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'reportee:"' +videoAbuse.video.channel.ownerAccount.displayName + '"' }" class="ml-auto text-muted video-details-links" i18n>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Component, ViewEncapsulation, Input } from '@angular/core'
|
||||
import { VideoAbuse } from '../../../../../../shared'
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { Account } from '@app/shared/account/account.model'
|
||||
import { Actor } from '@app/shared/actor/actor.model'
|
||||
import { ProcessedVideoAbuse } from './video-abuse-list.component'
|
||||
|
||||
@Component({
|
||||
selector: 'my-video-abuse-details',
|
||||
|
@ -8,9 +9,9 @@ import { Account } from '@app/shared/account/account.model'
|
|||
styleUrls: [ '../moderation.component.scss' ]
|
||||
})
|
||||
export class VideoAbuseDetailsComponent {
|
||||
@Input() videoAbuse: VideoAbuse
|
||||
@Input() videoAbuse: ProcessedVideoAbuse
|
||||
|
||||
createByString (account: Account) {
|
||||
return Account.CREATE_BY_STRING(account.name, account.host)
|
||||
switchToDefaultAvatar ($event: Event) {
|
||||
($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,25 +3,19 @@
|
|||
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" [resizableColumns]="true"
|
||||
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
||||
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} reports"
|
||||
(onPage)="onPage()" [expandedRowKeys]="expandedRows"
|
||||
(onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
|
||||
>
|
||||
<ng-template pTemplate="caption">
|
||||
<div class="caption">
|
||||
<div class="ml-auto">
|
||||
<div class="input-group">
|
||||
<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>Filter reports</h6>
|
||||
|
||||
<!-- TODO:
|
||||
<div class="dropdown-item" i18n>Reports opened by admins</div>
|
||||
<div class="dropdown-item" i18n>Reports on videos with multiple reports</div>
|
||||
<div class="dropdown-item" i18n>Unassigned reports</div>
|
||||
-->
|
||||
<h6 class="dropdown-header" i18n>Advanced report filters</h6>
|
||||
<a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:pending' }" class="dropdown-item" i18n>Unsolved reports</a>
|
||||
<a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:accepted' }" class="dropdown-item" i18n>Accepted reports</a>
|
||||
<a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:rejected' }" class="dropdown-item" i18n>Refused reports</a>
|
||||
|
@ -31,8 +25,10 @@
|
|||
</div>
|
||||
<input
|
||||
type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
|
||||
(keyup)="onSearch($event)"
|
||||
(keyup)="onAbuseSearch($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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -68,7 +64,7 @@
|
|||
>
|
||||
<div>
|
||||
{{ videoAbuse.reporterAccount.displayName }}
|
||||
<span class="text-muted">{{ createByString(videoAbuse.reporterAccount) }}</span>
|
||||
<span class="text-muted">{{ videoAbuse.reporterAccount.nameWithHost }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
|
|
@ -16,9 +16,23 @@ import { getAbsoluteAPIUrl } from '@app/shared/misc/utils'
|
|||
import { DomSanitizer } from '@angular/platform-browser'
|
||||
import { BlocklistService } from '@app/shared/blocklist'
|
||||
import { VideoService } from '@app/shared/video/video.service'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router'
|
||||
import { filter } from 'rxjs/operators'
|
||||
|
||||
export type ProcessedVideoAbuse = VideoAbuse & {
|
||||
moderationCommentHtml?: string,
|
||||
reasonHtml?: string
|
||||
embedHtml?: string
|
||||
updatedAt?: Date
|
||||
// override bare server-side definitions with rich client-side definitions
|
||||
reporterAccount: Account
|
||||
video: VideoAbuse['video'] & {
|
||||
channel: VideoAbuse['video']['channel'] & {
|
||||
ownerAccount: Account
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-video-abuse-list',
|
||||
templateUrl: './video-abuse-list.component.html',
|
||||
|
@ -27,7 +41,7 @@ import { filter } from 'rxjs/operators'
|
|||
export class VideoAbuseListComponent extends RestTable implements OnInit, AfterViewInit {
|
||||
@ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent
|
||||
|
||||
videoAbuses: (VideoAbuse & { moderationCommentHtml?: string, reasonHtml?: string })[] = []
|
||||
videoAbuses: ProcessedVideoAbuse[] = []
|
||||
totalRecords = 0
|
||||
sort: SortMeta = { field: 'createdAt', order: 1 }
|
||||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||
|
@ -44,7 +58,8 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
|
|||
private i18n: I18n,
|
||||
private markdownRenderer: MarkdownService,
|
||||
private sanitizer: DomSanitizer,
|
||||
private route: ActivatedRoute
|
||||
private route: ActivatedRoute,
|
||||
private router: Router
|
||||
) {
|
||||
super()
|
||||
|
||||
|
@ -212,16 +227,25 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
|
|||
this.loadData()
|
||||
}
|
||||
|
||||
createByString (account: Account) {
|
||||
return Account.CREATE_BY_STRING(account.name, account.host)
|
||||
/* Table filter functions */
|
||||
onAbuseSearch (event: Event) {
|
||||
this.onSearch(event)
|
||||
this.setQueryParams((event.target as HTMLInputElement).value)
|
||||
}
|
||||
|
||||
setTableFilter (filter: string) {
|
||||
// FIXME: cannot use ViewChild, so create a component for the filter input
|
||||
const filterInput = document.getElementById('table-filter') as HTMLInputElement
|
||||
if (filterInput) filterInput.value = filter
|
||||
setQueryParams (search: string) {
|
||||
const queryParams: Params = {}
|
||||
if (search) Object.assign(queryParams, { search })
|
||||
this.router.navigate([ '/admin/moderation/video-abuses/list' ], { queryParams })
|
||||
}
|
||||
|
||||
resetTableFilter () {
|
||||
this.setTableFilter('')
|
||||
this.setQueryParams('')
|
||||
this.resetSearch()
|
||||
}
|
||||
/* END Table filter functions */
|
||||
|
||||
isVideoAbuseAccepted (videoAbuse: VideoAbuse) {
|
||||
return videoAbuse.state.id === VideoAbuseState.ACCEPTED
|
||||
}
|
||||
|
@ -279,17 +303,20 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
|
|||
}).subscribe(
|
||||
async resultList => {
|
||||
this.totalRecords = resultList.total
|
||||
this.videoAbuses = []
|
||||
|
||||
this.videoAbuses = resultList.data
|
||||
|
||||
for (const abuse of this.videoAbuses) {
|
||||
for (const abuse of resultList.data) {
|
||||
Object.assign(abuse, {
|
||||
reasonHtml: await this.toHtml(abuse.reason),
|
||||
moderationCommentHtml: await this.toHtml(abuse.moderationComment),
|
||||
embedHtml: this.sanitizer.bypassSecurityTrustHtml(this.getVideoEmbed(abuse)),
|
||||
reporterAccount: new Account(abuse.reporterAccount)
|
||||
})
|
||||
|
||||
if (abuse.video.channel?.ownerAccount) abuse.video.channel.ownerAccount = new Account(abuse.video.channel.ownerAccount)
|
||||
if (abuse.updatedAt === abuse.createdAt) delete abuse.updatedAt
|
||||
|
||||
this.videoAbuses.push(abuse as ProcessedVideoAbuse)
|
||||
}
|
||||
|
||||
},
|
||||
|
|
|
@ -3,15 +3,17 @@
|
|||
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
|
||||
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
||||
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} blacklisted videos"
|
||||
(onPage)="onPage()" [expandedRowKeys]="expandedRows"
|
||||
(onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
|
||||
>
|
||||
<ng-template pTemplate="caption">
|
||||
<div class="caption">
|
||||
<div class="ml-auto">
|
||||
<div class="ml-auto has-feedback has-clear">
|
||||
<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)="resetSearch()"></a>
|
||||
<span class="sr-only" i18n>Clear filters</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<p-table
|
||||
[value]="jobs" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" dataKey="uniqId"
|
||||
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" [first]="pagination.start"
|
||||
[tableStyle]="{'table-layout':'auto'}" (onPage)="onPage()" [expandedRowKeys]="expandedRows"
|
||||
[tableStyle]="{'table-layout':'auto'}" (onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
|
||||
>
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
|
|
|
@ -16,3 +16,7 @@ input[type=submit] {
|
|||
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.input-group-append {
|
||||
height: 30px;
|
||||
}
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
</div>
|
||||
|
||||
<p-table
|
||||
[value]="users" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage"
|
||||
[value]="users" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
|
||||
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
|
||||
[(selection)]="selectedUsers"
|
||||
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
||||
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} users"
|
||||
(onPage)="onPage()" [expandedRowKeys]="expandedRows"
|
||||
(onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
|
||||
>
|
||||
<ng-template pTemplate="caption">
|
||||
<div class="caption">
|
||||
|
@ -25,11 +25,13 @@
|
|||
</my-action-dropdown>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="has-feedback has-clear">
|
||||
<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)="resetSearch()"></a>
|
||||
<span class="sr-only" i18n>Clear filters</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
|
|
@ -19,6 +19,10 @@ my-actor-avatar-info {
|
|||
@include peertube-input-group(fit-content);
|
||||
}
|
||||
|
||||
.input-group-append {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
input {
|
||||
&[type=text] {
|
||||
@include peertube-input-text(340px);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<p-table
|
||||
[value]="videoImports" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage"
|
||||
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
|
||||
(onPage)="onPage()" [expandedRowKeys]="expandedRows"
|
||||
(onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
|
||||
>
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
|
|
|
@ -58,6 +58,10 @@
|
|||
@include peertube-input-group(400px);
|
||||
}
|
||||
|
||||
.input-group-append {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
input:not([type=submit]) {
|
||||
@include peertube-input-text(400px);
|
||||
|
||||
|
|
|
@ -74,10 +74,29 @@ export abstract class RestTable {
|
|||
this.searchStream.next(target.value)
|
||||
}
|
||||
|
||||
onPage () {
|
||||
onPage (event: { first: number, rows: number }) {
|
||||
if (this.rowsPerPage !== event.rows) {
|
||||
this.rowsPerPage = event.rows
|
||||
this.pagination = {
|
||||
start: event.first,
|
||||
count: this.rowsPerPage
|
||||
}
|
||||
this.loadData()
|
||||
}
|
||||
this.expandedRows = {}
|
||||
}
|
||||
|
||||
setTableFilter (filter: string) {
|
||||
// FIXME: cannot use ViewChild, so create a component for the filter input
|
||||
const filterInput = document.getElementById('table-filter') as HTMLInputElement
|
||||
if (filterInput) filterInput.value = filter
|
||||
}
|
||||
|
||||
resetSearch () {
|
||||
this.searchStream.next('')
|
||||
this.setTableFilter('')
|
||||
}
|
||||
|
||||
protected abstract loadData (): void
|
||||
|
||||
private getSortLocalStorageKey () {
|
||||
|
|
|
@ -339,6 +339,11 @@ table {
|
|||
.peertube-select-container {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.caption input[type=text] {
|
||||
width: unset !important;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
|
|||
}
|
||||
|
||||
/* rules for dropdowns excepts when in button group, to avoid impacting the dropdown-toggle */
|
||||
.dropdown:not(.btn-group):not(.dropdown-root):not(.action-dropdown) {
|
||||
.dropdown:not(.btn-group):not(.dropdown-root):not(.action-dropdown):not(.input-group-prepend) {
|
||||
z-index: z(dropdown) !important;
|
||||
|
||||
&.list-overflow-menu,
|
||||
|
@ -270,10 +270,9 @@ ngb-tooltip-window {
|
|||
& > .form-control {
|
||||
flex: initial;
|
||||
}
|
||||
|
||||
.input-group-prepend,
|
||||
.input-group-append {
|
||||
height: 30px;
|
||||
input.form-control {
|
||||
width: unset !important;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.input-group-prepend + input {
|
||||
|
@ -281,3 +280,35 @@ ngb-tooltip-window {
|
|||
border-bottom-left-radius: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.has-feedback.has-clear {
|
||||
position: relative;
|
||||
|
||||
input {
|
||||
padding-right: 1.5rem !important;
|
||||
}
|
||||
|
||||
.form-control-clear {
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
/*
|
||||
* Enable pointer events as they have been disabled since Bootstrap 3.3
|
||||
* See https://github.com/twbs/bootstrap/pull/14104
|
||||
*/
|
||||
pointer-events: all;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
right: .5rem;
|
||||
height: 95%;
|
||||
|
||||
&:hover {
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
input:placeholder-shown + .form-control-clear {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@ p-table {
|
|||
|
||||
.caption {
|
||||
height: 40px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
.input-group-text {
|
||||
|
|
|
@ -223,9 +223,12 @@ interface QueryStringFilterPrefixes {
|
|||
[key: string]: string | { prefix: string, handler: Function, multiple?: boolean }
|
||||
}
|
||||
|
||||
function parseQueryStringFilter (q: string, prefixes: QueryStringFilterPrefixes) {
|
||||
function parseQueryStringFilter (q: string, prefixes: QueryStringFilterPrefixes): {
|
||||
search: string
|
||||
[key: string]: string | number | string[] | number[]
|
||||
} {
|
||||
const tokens = q // tokenize only if we have a querystring
|
||||
? [].concat.apply([], q.split('"').map((v, i) => i % 2 ? v : v.split(' '))).filter(Boolean)
|
||||
? [].concat.apply([], q.split('"').map((v, i) => i % 2 ? v : v.split(' '))).filter(Boolean) // split by space unless using double quotes
|
||||
: []
|
||||
|
||||
// TODO: when Typescript supports Object.fromEntries, replace with the Object method
|
||||
|
@ -252,16 +255,18 @@ function parseQueryStringFilter (q: string, prefixes: QueryStringFilterPrefixes)
|
|||
}
|
||||
})).join(' '),
|
||||
// filters defined in prefixes are added under their own name
|
||||
...objectMap(prefixes, v => {
|
||||
if (typeof v === "string") {
|
||||
return tokens.filter(e => e.startsWith(v)).map(e => e.slice(v.length))
|
||||
...objectMap(prefixes, p => {
|
||||
if (typeof p === "string") {
|
||||
return tokens.filter(e => e.startsWith(p)).map(e => e.slice(p.length)) // we keep the matched item, and remove its prefix
|
||||
} else {
|
||||
const _tokens = tokens.filter(e => e.startsWith(v.prefix)).map(e => e.slice(v.prefix.length)).map(v.handler)
|
||||
return !v.multiple
|
||||
? _tokens.length > 0
|
||||
? _tokens[0]
|
||||
: ''
|
||||
: _tokens
|
||||
const _tokens = tokens.filter(e => e.startsWith(p.prefix)).map(e => e.slice(p.prefix.length)).map(p.handler)
|
||||
// multiple is false by default, meaning we usually just keep the first occurence of a given prefix
|
||||
if (!p.multiple && _tokens.length > 0) {
|
||||
return _tokens[0]
|
||||
} else if (!p.multiple) {
|
||||
return ''
|
||||
}
|
||||
return _tokens
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ export interface VideoAbuse {
|
|||
}
|
||||
|
||||
createdAt: Date
|
||||
updatedAt?: Date
|
||||
updatedAt: Date
|
||||
|
||||
count?: number
|
||||
nth?: number
|
||||
|
|
Loading…
Reference in New Issue