mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2024-12-01 21:09:17 -06:00
variable columns for users list, more columns possible, badge display for statuses
This commit is contained in:
parent
654a188f80
commit
bc99dfe54e
@ -41,8 +41,12 @@
|
||||
</a>
|
||||
</td>
|
||||
|
||||
<td *ngIf="follow.state === 'accepted'" i18n>Accepted</td>
|
||||
<td *ngIf="follow.state === 'pending'" i18n>Pending</td>
|
||||
<td *ngIf="follow.state === 'accepted'">
|
||||
<span class="badge badge-green" i18n>Accepted</span>
|
||||
</td>
|
||||
<td *ngIf="follow.state === 'pending'">
|
||||
<span class="badge badge-yellow" i18n>Pending</span>
|
||||
</td>
|
||||
|
||||
<td>{{ follow.score }}</td>
|
||||
<td>{{ follow.createdAt | date: 'short' }}</td>
|
||||
|
@ -45,8 +45,12 @@
|
||||
</a>
|
||||
</td>
|
||||
|
||||
<td *ngIf="follow.state === 'accepted'" i18n>Accepted</td>
|
||||
<td *ngIf="follow.state === 'pending'" i18n>Pending</td>
|
||||
<td *ngIf="follow.state === 'accepted'">
|
||||
<span class="badge badge-green" i18n>Accepted</span>
|
||||
</td>
|
||||
<td *ngIf="follow.state === 'pending'">
|
||||
<span class="badge badge-yellow" i18n>Pending</span>
|
||||
</td>
|
||||
|
||||
<td>{{ follow.createdAt | date: 'short' }}</td>
|
||||
<td>
|
||||
|
@ -4,3 +4,7 @@
|
||||
flex-grow: 0;
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
@include table-badge;
|
||||
}
|
||||
|
@ -26,10 +26,10 @@
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th style="width: 40px"></th>
|
||||
<th class="job-id" i18n>ID</th>
|
||||
<th class="job-type" i18n>Type</th>
|
||||
<th class="job-date" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
|
||||
<th class="job-state" i18n>State</th>
|
||||
<th style="width: 100%" class="job-id" i18n>ID</th>
|
||||
<th style="width: 200px" class="job-type" i18n>Type</th>
|
||||
<th style="width: 150px" class="job-date" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
|
||||
<th style="width: 150px" class="job-state" i18n>State</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
|
||||
<td class="job-id" [title]="job.id">{{ job.id }}</td>
|
||||
<td class="job-type">{{ job.type }}</td>
|
||||
<td class="job-date">{{ job.createdAt }}</td>
|
||||
<td class="job-date">{{ job.createdAt | date: 'short' }}</td>
|
||||
<td class="job-state" *ngIf="job.state === 'delayed'" class="text-muted"><span class="glyphicon glyphicon-repeat"></span> <span i18n>Delayed</span></td>
|
||||
<td class="job-state" *ngIf="job.state === 'waiting'" class="text-warning"><span class="glyphicon glyphicon-hourglass"></span> <span i18n>Will start soon...</span></td>
|
||||
<td class="job-state" *ngIf="job.state === 'active'" class="text-warning"><span class="glyphicon glyphicon-cog"></span> <span i18n>Running...</span></td>
|
||||
|
@ -50,19 +50,41 @@
|
||||
<p-tableHeaderCheckbox></p-tableHeaderCheckbox>
|
||||
</th>
|
||||
<th style="width: 40px"></th>
|
||||
<th pResizableColumn i18n pSortableColumn="username">Username <p-sortIcon field="username"></p-sortIcon></th>
|
||||
<th i18n>Email</th>
|
||||
<th style="width: 140px;" i18n pSortableColumn="videoQuotaUsed">Video quota <p-sortIcon field="videoQuotaUsed"></p-sortIcon></th>
|
||||
<th style="width: 120px;" i18n>Role</th>
|
||||
<th style="width: 140px;" pResizableColumn i18n>Auth plugin</th>
|
||||
<th style="width: 150px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
|
||||
<th style="width: 60px;"></th>
|
||||
<th *ngIf="getColumn('username')" pResizableColumn i18n pSortableColumn="username">{{ getColumn('username').label }} <p-sortIcon field="username"></p-sortIcon></th>
|
||||
<th *ngIf="getColumn('email')" i18n>{{ getColumn('email').label }}</th>
|
||||
<th *ngIf="getColumn('quota')" style="width: 160px;" i18n pSortableColumn="videoQuotaUsed">{{ getColumn('quota').label }} <p-sortIcon field="videoQuotaUsed"></p-sortIcon></th>
|
||||
<th *ngIf="getColumn('quotaDaily')" style="width: 160px;" i18n>{{ getColumn('quotaDaily').label }}</th>
|
||||
<th *ngIf="getColumn('role')" style="width: 120px;" i18n pSortableColumn="role">{{ getColumn('role').label }} <p-sortIcon field="role"></p-sortIcon></th>
|
||||
<th *ngIf="getColumn('pluginAuth')" style="width: 140px;" pResizableColumn i18n>{{ getColumn('pluginAuth').label }}</th>
|
||||
<th *ngIf="getColumn('createdAt')" style="width: 150px;" i18n pSortableColumn="createdAt">{{ getColumn('createdAt').label }} <p-sortIcon field="createdAt"></p-sortIcon></th>
|
||||
<th *ngIf="getColumn('lastLoginDate')" style="width: 150px;" i18n pSortableColumn="lastLoginDate">{{ getColumn('lastLoginDate').label }} <p-sortIcon field="lastLoginDate"></p-sortIcon></th>
|
||||
<th style="width: 60px;">
|
||||
<div class="c-hand" ngbDropdown placement="bottom-right auto" container="body" autoClose="outside">
|
||||
<my-global-icon iconName="columns" ngbDropdownToggle></my-global-icon>
|
||||
|
||||
<div role="menu" class="dropdown-menu" ngbDropdownMenu>
|
||||
<div class="dropdown-header" i18n>Table parameters</div>
|
||||
<div ngbDropdownItem class="dropdown-item">
|
||||
<p-multiSelect
|
||||
[options]="columns" [showToggleAll]="true" [(ngModel)]="selectedColumns" optionLabel="label"
|
||||
emptyFilterMessage="No matching column found" i18n-emptyFilterMessage [filter]="false"
|
||||
selectedItemsLabel="{0} columns displayed" i18n-emptyFilterMessage [showHeader]="false"
|
||||
[maxSelectedLabels]="4"
|
||||
></p-multiSelect>
|
||||
</div>
|
||||
<div ngbDropdownItem class="dropdown-item">
|
||||
<my-peertube-checkbox inputName="highlightBannedUsers" [(ngModel)]="highlightBannedUsers"
|
||||
i18n-labelText labelText="Highlight banned users"></my-peertube-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="body" let-expanded="expanded" let-user>
|
||||
|
||||
<tr [pSelectableRow]="user" [ngClass]="{ banned: user.blocked }">
|
||||
<tr [pSelectableRow]="user" [ngClass]="{ banned: highlightBannedUsers && user.blocked }">
|
||||
<td>
|
||||
<p-tableCheckbox [value]="user"></p-tableCheckbox>
|
||||
</td>
|
||||
@ -73,7 +95,7 @@
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<td *ngIf="getColumn('username')">
|
||||
<a i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer" [routerLink]="[ '/accounts/' + user.username ]">
|
||||
<div class="chip two-lines">
|
||||
<img
|
||||
@ -83,17 +105,16 @@
|
||||
alt="Avatar"
|
||||
>
|
||||
<div>
|
||||
<span class="user-table-primary-text">
|
||||
<span *ngIf="user.blocked" i18n-title title="The user was banned" class="glyphicon glyphicon-ban-circle"></span>
|
||||
{{ user.account.displayName }}
|
||||
</span>
|
||||
<span class="user-table-primary-text">{{ user.account.displayName }}</span>
|
||||
<span class="text-muted">{{ user.username }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</td>
|
||||
|
||||
<td *ngIf="!requiresEmailVerification || user.blocked; else emailWithVerificationStatus" [title]="user.email">{{ user.email }}</td>
|
||||
<td *ngIf="!requiresEmailVerification || user.blocked; else emailWithVerificationStatus" [title]="user.email">
|
||||
<a class="table-email" [href]="'mailto:' + user.email">{{ user.email }}</a>
|
||||
</td>
|
||||
|
||||
<ng-template #emailWithVerificationStatus>
|
||||
<td *ngIf="user.emailVerified === false; else emailVerifiedNotFalse" i18n-title title="User's email must be verified to login">
|
||||
@ -106,14 +127,38 @@
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
|
||||
<td>{{ user.videoQuotaUsed }} / {{ user.videoQuota }}</td>
|
||||
<td>{{ user.roleLabel }}</td>
|
||||
<td *ngIf="getColumn('quota')">
|
||||
<div class="progress" i18n-title title="Total video quota">
|
||||
<div class="progress-bar" role="progressbar" [style]="{ width: getUserVideoQuotaPercentage(user) + '%' }"
|
||||
[attr.aria-valuenow]="user.rawVideoQuotaUsed" aria-valuemin="0" [attr.aria-valuemax]="user.rawVideoQuota">
|
||||
</div>
|
||||
<span>{{ user.videoQuotaUsed }}</span>
|
||||
<span>{{ user.videoQuota }}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<td *ngIf="getColumn('quotaDaily')">
|
||||
<div class="progress" i18n-title title="Total daily video quota">
|
||||
<div class="progress-bar secondary" role="progressbar" [style]="{ width: getUserVideoQuotaDailyPercentage(user) + '%' }"
|
||||
[attr.aria-valuenow]="user.rawVideoQuotaUsedDaily" aria-valuemin="0" [attr.aria-valuemax]="user.rawVideoQuotaDaily">
|
||||
</div>
|
||||
<span>{{ user.videoQuotaUsedDaily }}</span>
|
||||
<span>{{ user.videoQuotaDaily }}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td *ngIf="getColumn('role')">
|
||||
<span *ngIf="user.blocked" class="badge badge-banned" i18n-title title="The user was banned">{{ user.roleLabel }}</span>
|
||||
<span *ngIf="!user.blocked" class="badge" [ngClass]="getRoleClass(user.role)">{{ user.roleLabel }}</span>
|
||||
</td>
|
||||
|
||||
<td *ngIf="getColumn('pluginAuth')">
|
||||
<ng-container *ngIf="user.pluginAuth">{{ user.pluginAuth }}</ng-container>
|
||||
</td>
|
||||
|
||||
<td [title]="user.createdAt">{{ user.createdAt | date: 'short' }}</td>
|
||||
<td *ngIf="getColumn('createdAt')" [title]="user.createdAt">{{ user.createdAt | date: 'short' }}</td>
|
||||
|
||||
<td *ngIf="getColumn('lastLoginDate')" [title]="user.lastLoginDate">{{ user.lastLoginDate | date: 'short' }}</td>
|
||||
|
||||
<td class="action-cell">
|
||||
<my-user-moderation-dropdown
|
||||
|
@ -9,6 +9,11 @@ tr.banned > td {
|
||||
background-color: lighten($color: $red, $amount: 40) !important;
|
||||
}
|
||||
|
||||
.table-email {
|
||||
@include disable-default-a-behaviour;
|
||||
color: pvar(--mainForegroundColor);
|
||||
}
|
||||
|
||||
.banned-info {
|
||||
font-style: italic;
|
||||
}
|
||||
@ -36,10 +41,24 @@ p-tableCheckbox {
|
||||
top: -2.5px;
|
||||
}
|
||||
|
||||
my-global-icon {
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
.chip {
|
||||
@include chip;
|
||||
}
|
||||
|
||||
.badge {
|
||||
@include table-badge;
|
||||
}
|
||||
|
||||
.progress {
|
||||
@include progressbar;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
@include peertube-input-group(300px);
|
||||
input {
|
||||
|
@ -4,7 +4,7 @@ import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, Serve
|
||||
import { Actor, DropdownAction } from '@app/shared/shared-main'
|
||||
import { UserBanModalComponent } from '@app/shared/shared-moderation'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { ServerConfig, User } from '@shared/models'
|
||||
import { ServerConfig, User, UserRole } from '@shared/models'
|
||||
import { Params, Router, ActivatedRoute } from '@angular/router'
|
||||
|
||||
@Component({
|
||||
@ -19,9 +19,12 @@ export class UserListComponent extends RestTable implements OnInit {
|
||||
totalRecords = 0
|
||||
sort: SortMeta = { field: 'createdAt', order: 1 }
|
||||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||
highlightBannedUsers = false
|
||||
|
||||
selectedUsers: User[] = []
|
||||
bulkUserActions: DropdownAction<User[]>[][] = []
|
||||
columns: { key: string, label: string }[]
|
||||
_selectedColumns: { key: string, label: string }[]
|
||||
|
||||
private serverConfig: ServerConfig
|
||||
|
||||
@ -46,6 +49,14 @@ export class UserListComponent extends RestTable implements OnInit {
|
||||
return this.serverConfig.signup.requiresEmailVerification
|
||||
}
|
||||
|
||||
get selectedColumns () {
|
||||
return this._selectedColumns
|
||||
}
|
||||
|
||||
set selectedColumns (val) {
|
||||
this._selectedColumns = val
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.serverConfig = this.serverService.getTmpConfig()
|
||||
this.serverService.getConfig()
|
||||
@ -92,12 +103,47 @@ export class UserListComponent extends RestTable implements OnInit {
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
this.columns = [
|
||||
{ key: 'username', label: 'Username' },
|
||||
{ key: 'email', label: 'Email' },
|
||||
{ key: 'quota', label: 'Video quota' },
|
||||
{ key: 'role', label: 'Role' },
|
||||
{ key: 'createdAt', label: 'Created' }
|
||||
]
|
||||
this.selectedColumns = [...this.columns]
|
||||
this.columns.push({ key: 'quotaDaily', label: 'Daily quota' })
|
||||
this.columns.push({ key: 'pluginAuth', label: 'Auth plugin' })
|
||||
this.columns.push({ key: 'lastLoginDate', label: 'Last login' })
|
||||
}
|
||||
|
||||
getIdentifier () {
|
||||
return 'UserListComponent'
|
||||
}
|
||||
|
||||
getRoleClass (role: UserRole) {
|
||||
switch (role) {
|
||||
case UserRole.ADMINISTRATOR:
|
||||
return 'badge-purple'
|
||||
case UserRole.MODERATOR:
|
||||
return 'badge-blue'
|
||||
default:
|
||||
return 'badge-yellow'
|
||||
}
|
||||
}
|
||||
|
||||
getColumn (key: string) {
|
||||
return this.selectedColumns.find((col: any) => col.key === key)
|
||||
}
|
||||
|
||||
getUserVideoQuotaPercentage (user: User & { rawVideoQuota: number, rawVideoQuotaUsed: number}) {
|
||||
return user.rawVideoQuotaUsed * 100 / user.rawVideoQuota
|
||||
}
|
||||
|
||||
getUserVideoQuotaDailyPercentage (user: User & { rawVideoQuotaDaily: number, rawVideoQuotaUsedDaily: number}) {
|
||||
return user.rawVideoQuotaUsedDaily * 100 / user.rawVideoQuotaDaily
|
||||
}
|
||||
|
||||
openBanUserModal (users: User[]) {
|
||||
for (const user of users) {
|
||||
if (user.username === 'root') {
|
||||
|
@ -6,7 +6,7 @@
|
||||
</a>
|
||||
|
||||
<div class="peertube-select-container peertube-select-button ml-2">
|
||||
<select [(ngModel)]="notificationSortType" (ngModelChange)="onNotificationSortTypeChanged()" class="form-control">
|
||||
<select [(ngModel)]="notificationSortType" class="form-control">
|
||||
<option value="undefined" disabled>Sort by</option>
|
||||
<option value="created" i18n>Newest first</option>
|
||||
<option value="unread-created" i18n>Unread first</option>
|
||||
|
@ -17,6 +17,4 @@ export class MyAccountNotificationsComponent {
|
||||
hasUnreadNotifications () {
|
||||
return this.userNotification.notifications.filter(n => n.read === false).length !== 0
|
||||
}
|
||||
|
||||
onNotificationSortTypeChanged () {}
|
||||
}
|
||||
|
@ -374,13 +374,23 @@ export class UserService {
|
||||
private formatUser (user: UserServerModel) {
|
||||
let videoQuota
|
||||
if (user.videoQuota === -1) {
|
||||
videoQuota = this.i18n('Unlimited')
|
||||
videoQuota = '∞'
|
||||
} else {
|
||||
videoQuota = this.bytesPipe.transform(user.videoQuota, 0)
|
||||
}
|
||||
|
||||
const videoQuotaUsed = this.bytesPipe.transform(user.videoQuotaUsed, 0)
|
||||
|
||||
let videoQuotaDaily
|
||||
let videoQuotaUsedDaily
|
||||
if (user.videoQuotaDaily === -1) {
|
||||
videoQuotaDaily = '∞'
|
||||
videoQuotaUsedDaily = this.bytesPipe.transform(0, 0)
|
||||
} else {
|
||||
videoQuotaDaily = this.bytesPipe.transform(user.videoQuotaDaily, 0)
|
||||
videoQuotaUsedDaily = this.bytesPipe.transform(user.videoQuotaUsedDaily || 0, 0)
|
||||
}
|
||||
|
||||
const roleLabels: { [ id in UserRole ]: string } = {
|
||||
[UserRole.USER]: this.i18n('User'),
|
||||
[UserRole.ADMINISTRATOR]: this.i18n('Administrator'),
|
||||
@ -390,7 +400,13 @@ export class UserService {
|
||||
return Object.assign(user, {
|
||||
roleLabel: roleLabels[user.role],
|
||||
videoQuota,
|
||||
videoQuotaUsed
|
||||
videoQuotaUsed,
|
||||
rawVideoQuota: user.videoQuota,
|
||||
rawVideoQuotaUsed: user.videoQuotaUsed,
|
||||
videoQuotaDaily,
|
||||
videoQuotaUsedDaily,
|
||||
rawVideoQuotaDaily: user.videoQuotaDaily,
|
||||
rawVideoQuotaUsedDaily: user.videoQuotaUsedDaily
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,8 @@ const icons = {
|
||||
'go': require('!!raw-loader?!../../../assets/images/feather/arrow-up-right.svg').default,
|
||||
'cross': require('!!raw-loader?!../../../assets/images/feather/x.svg').default,
|
||||
'tick': require('!!raw-loader?!../../../assets/images/feather/check.svg').default,
|
||||
'repeat': require('!!raw-loader?!../../../assets/images/feather/repeat.svg').default
|
||||
'repeat': require('!!raw-loader?!../../../assets/images/feather/repeat.svg').default,
|
||||
'columns': require('!!raw-loader?!../../../assets/images/feather/columns.svg').default
|
||||
}
|
||||
|
||||
export type GlobalIconName = keyof typeof icons
|
||||
|
@ -43,7 +43,8 @@ export class VideoChannelService {
|
||||
listAccountVideoChannels (
|
||||
account: Account,
|
||||
componentPagination?: ComponentPaginationLight,
|
||||
withStats = false
|
||||
withStats = false,
|
||||
search?: string
|
||||
): Observable<ResultList<VideoChannel>> {
|
||||
const pagination = componentPagination
|
||||
? this.restService.componentPaginationToRestPagination(componentPagination)
|
||||
@ -53,6 +54,10 @@ export class VideoChannelService {
|
||||
params = this.restService.addRestGetParams(params, pagination)
|
||||
params = params.set('withStats', withStats + '')
|
||||
|
||||
if (search) {
|
||||
params = params.set('search', search)
|
||||
}
|
||||
|
||||
const url = AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/video-channels'
|
||||
return this.authHttp.get<ResultList<VideoChannelServer>>(url, { params })
|
||||
.pipe(
|
||||
|
1
client/src/assets/images/feather/columns.svg
Normal file
1
client/src/assets/images/feather/columns.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-columns"><path d="M12 3h7a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-7m0-18H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h7m0-18v18"></path></svg>
|
After Width: | Height: | Size: 326 B |
@ -332,9 +332,7 @@
|
||||
|
||||
select {
|
||||
padding: 0 35px 0 12px;
|
||||
width: calc(100% + 2px);
|
||||
position: relative;
|
||||
left: 1px;
|
||||
border: 1px solid #C6C6C6;
|
||||
background: transparent none;
|
||||
appearance: none;
|
||||
@ -692,7 +690,21 @@
|
||||
overflow: hidden;
|
||||
font-size: 0.75rem;
|
||||
border-radius: 0.25rem;
|
||||
color: gray;
|
||||
isolation: isolate;
|
||||
position: relative;
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
color: rgb(92, 92, 92);
|
||||
top: -1px;
|
||||
|
||||
&:nth-of-type(1) {
|
||||
left: .2rem;
|
||||
}
|
||||
&:nth-of-type(2) {
|
||||
right: .2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
color: pvar(--mainBackgroundColor);
|
||||
@ -703,25 +715,11 @@
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
transition: width 0.6s ease;
|
||||
isolation: isolate;
|
||||
|
||||
&:after {
|
||||
content: attr(valuenow-formatted);
|
||||
position: absolute;
|
||||
margin-left: .2rem;
|
||||
mix-blend-mode: screen;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
background-color: pvar(--secondaryColor);
|
||||
}
|
||||
}
|
||||
|
||||
.progress-bar + span {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin breadcrumb {
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
videoPlaylistsSortValidator
|
||||
} from '../../middlewares'
|
||||
import { VideoChannelModel } from '../../models/video/video-channel'
|
||||
import { videoChannelsNameWithHostValidator, videosSortValidator } from '../../middlewares/validators'
|
||||
import { videoChannelsNameWithHostValidator, videosSortValidator, videoChannelsOwnSearchValidator } from '../../middlewares/validators'
|
||||
import { sendUpdateActor } from '../../lib/activitypub/send'
|
||||
import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared'
|
||||
import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel'
|
||||
@ -48,6 +48,7 @@ videoChannelRouter.get('/',
|
||||
videoChannelsSortValidator,
|
||||
setDefaultSort,
|
||||
setDefaultPagination,
|
||||
videoChannelsOwnSearchValidator,
|
||||
asyncMiddleware(listVideoChannels)
|
||||
)
|
||||
|
||||
@ -114,7 +115,13 @@ export {
|
||||
|
||||
async function listVideoChannels (req: express.Request, res: express.Response) {
|
||||
const serverActor = await getServerActor()
|
||||
const resultList = await VideoChannelModel.listForApi(serverActor.id, req.query.start, req.query.count, req.query.sort)
|
||||
const resultList = await VideoChannelModel.listForApi({
|
||||
actorId: serverActor.id,
|
||||
start: req.query.start,
|
||||
count: req.query.count,
|
||||
sort: req.query.sort,
|
||||
search: req.query.search
|
||||
})
|
||||
|
||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||
}
|
||||
|
@ -41,9 +41,22 @@ const videoChannelsSearchValidator = [
|
||||
}
|
||||
]
|
||||
|
||||
const videoChannelsOwnSearchValidator = [
|
||||
query('search').optional().not().isEmpty().withMessage('Should have a valid search'),
|
||||
|
||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
logger.debug('Checking video channels search query', { parameters: req.query })
|
||||
|
||||
if (areValidationErrors(req, res)) return
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
videosSearchValidator,
|
||||
videoChannelsSearchValidator,
|
||||
videosSearchValidator
|
||||
videoChannelsOwnSearchValidator
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ export enum ScopeNames {
|
||||
|
||||
type AvailableForListOptions = {
|
||||
actorId: number
|
||||
search?: string
|
||||
}
|
||||
|
||||
type AvailableWithStatsOptions = {
|
||||
@ -309,15 +310,23 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
|
||||
return VideoChannelModel.count(query)
|
||||
}
|
||||
|
||||
static listForApi (actorId: number, start: number, count: number, sort: string) {
|
||||
static listForApi (parameters: {
|
||||
actorId: number
|
||||
start: number
|
||||
count: number
|
||||
sort: string
|
||||
search?: string
|
||||
}) {
|
||||
const { actorId, search } = parameters
|
||||
|
||||
const query = {
|
||||
offset: start,
|
||||
limit: count,
|
||||
order: getSort(sort)
|
||||
offset: parameters.start,
|
||||
limit: parameters.count,
|
||||
order: getSort(parameters.sort)
|
||||
}
|
||||
|
||||
const scopes = {
|
||||
method: [ ScopeNames.FOR_API, { actorId } as AvailableForListOptions ]
|
||||
method: [ ScopeNames.FOR_API, { actorId, search } as AvailableForListOptions ]
|
||||
}
|
||||
return VideoChannelModel
|
||||
.scope(scopes)
|
||||
|
Loading…
Reference in New Issue
Block a user