Add ability to search on followers/following

This commit is contained in:
Chocobozzz 2018-10-10 09:43:53 +02:00
parent 9ccff23877
commit b014b6b9c7
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
13 changed files with 166 additions and 49 deletions

View File

@ -2,6 +2,15 @@
[value]="followers" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage" [value]="followers" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)"
> >
<ng-template pTemplate="caption">
<div class="caption">
<input
type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
(keyup)="onSearch($event.target.value)"
>
</div>
</ng-template>
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<th i18n style="width: 60px">ID</th> <th i18n style="width: 60px">ID</th>

View File

@ -0,0 +1,10 @@
@import '_variables';
@import '_mixins';
.caption {
justify-content: flex-end;
input {
@include peertube-input-text(250px);
}
}

View File

@ -2,6 +2,17 @@
[value]="following" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage" [value]="following" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)"
> >
<ng-template pTemplate="caption">
<div class="caption">
<div>
<input
type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
(keyup)="onSearch($event.target.value)"
>
</div>
</div>
</ng-template>
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<th i18n style="width: 60px">ID</th> <th i18n style="width: 60px">ID</th>

View File

@ -11,3 +11,11 @@ my-redundancy-checkbox /deep/ my-peertube-checkbox {
margin: 0; margin: 0;
} }
} }
.caption {
justify-content: flex-end;
input {
@include peertube-input-text(250px);
}
}

View File

@ -53,7 +53,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
} }
protected loadData () { protected loadData () {
this.followService.getFollowing(this.pagination, this.sort) this.followService.getFollowing(this.pagination, this.sort, this.search)
.subscribe( .subscribe(
resultList => { resultList => {
this.following = resultList.data this.following = resultList.data

View File

@ -18,10 +18,12 @@ export class FollowService {
) { ) {
} }
getFollowing (pagination: RestPagination, sort: SortMeta): Observable<ResultList<ActorFollow>> { getFollowing (pagination: RestPagination, sort: SortMeta, search?: string): Observable<ResultList<ActorFollow>> {
let params = new HttpParams() let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort) params = this.restService.addRestGetParams(params, pagination, sort)
if (search) params = params.append('search', search)
return this.authHttp.get<ResultList<ActorFollow>>(FollowService.BASE_APPLICATION_URL + '/following', { params }) return this.authHttp.get<ResultList<ActorFollow>>(FollowService.BASE_APPLICATION_URL + '/following', { params })
.pipe( .pipe(
map(res => this.restExtractor.convertResultListDateToHuman(res)), map(res => this.restExtractor.convertResultListDateToHuman(res)),
@ -29,10 +31,12 @@ export class FollowService {
) )
} }
getFollowers (pagination: RestPagination, sort: SortMeta): Observable<ResultList<ActorFollow>> { getFollowers (pagination: RestPagination, sort: SortMeta, search?: string): Observable<ResultList<ActorFollow>> {
let params = new HttpParams() let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort) params = this.restService.addRestGetParams(params, pagination, sort)
if (search) params = params.append('search', search)
return this.authHttp.get<ResultList<ActorFollow>>(FollowService.BASE_APPLICATION_URL + '/followers', { params }) return this.authHttp.get<ResultList<ActorFollow>>(FollowService.BASE_APPLICATION_URL + '/followers', { params })
.pipe( .pipe(
map(res => this.restExtractor.convertResultListDateToHuman(res)), map(res => this.restExtractor.convertResultListDateToHuman(res)),

View File

@ -18,10 +18,7 @@ tr.banned {
} }
.caption { .caption {
height: 40px;
display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center;
input { input {
@include peertube-input-text(250px); @include peertube-input-text(250px);

View File

@ -22,9 +22,9 @@
</span> </span>
<input <input
type="submit" i18n-value value="Submit" class="action-button-submit" type="submit" i18n-value value="Submit" class="action-button-submit"
[disabled]="!form.valid" [disabled]="!form.valid"
(click)="close()" (click)="close()"
/> />
</div> </div>
</div> </div>

View File

@ -16,6 +16,12 @@ p-table {
.ui-table-caption { .ui-table-caption {
border: none; border: none;
.caption {
height: 40px;
display: flex;
align-items: center;
}
} }
td { td {

View File

@ -61,14 +61,26 @@ export {
async function listFollowing (req: express.Request, res: express.Response, next: express.NextFunction) { async function listFollowing (req: express.Request, res: express.Response, next: express.NextFunction) {
const serverActor = await getServerActor() const serverActor = await getServerActor()
const resultList = await ActorFollowModel.listFollowingForApi(serverActor.id, req.query.start, req.query.count, req.query.sort) const resultList = await ActorFollowModel.listFollowingForApi(
serverActor.id,
req.query.start,
req.query.count,
req.query.sort,
req.query.search
)
return res.json(getFormattedObjects(resultList.data, resultList.total)) return res.json(getFormattedObjects(resultList.data, resultList.total))
} }
async function listFollowers (req: express.Request, res: express.Response, next: express.NextFunction) { async function listFollowers (req: express.Request, res: express.Response, next: express.NextFunction) {
const serverActor = await getServerActor() const serverActor = await getServerActor()
const resultList = await ActorFollowModel.listFollowersForApi(serverActor.id, req.query.start, req.query.count, req.query.sort) const resultList = await ActorFollowModel.listFollowersForApi(
serverActor.id,
req.query.start,
req.query.count,
req.query.sort,
req.query.search
)
return res.json(getFormattedObjects(resultList.data, resultList.total)) return res.json(getFormattedObjects(resultList.data, resultList.total))
} }

View File

@ -280,7 +280,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
return ActorFollowModel.findAll(query) return ActorFollowModel.findAll(query)
} }
static listFollowingForApi (id: number, start: number, count: number, sort: string) { static listFollowingForApi (id: number, start: number, count: number, sort: string, search?: string) {
const query = { const query = {
distinct: true, distinct: true,
offset: start, offset: start,
@ -299,7 +299,17 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
model: ActorModel, model: ActorModel,
as: 'ActorFollowing', as: 'ActorFollowing',
required: true, required: true,
include: [ ServerModel ] include: [
{
model: ServerModel,
required: true,
where: search ? {
host: {
[Sequelize.Op.iLike]: '%' + search + '%'
}
} : undefined
}
]
} }
] ]
} }
@ -313,6 +323,49 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
}) })
} }
static listFollowersForApi (id: number, start: number, count: number, sort: string, search?: string) {
const query = {
distinct: true,
offset: start,
limit: count,
order: getSort(sort),
include: [
{
model: ActorModel,
required: true,
as: 'ActorFollower',
include: [
{
model: ServerModel,
required: true,
where: search ? {
host: {
[ Sequelize.Op.iLike ]: '%' + search + '%'
}
} : undefined
}
]
},
{
model: ActorModel,
as: 'ActorFollowing',
required: true,
where: {
id
}
}
]
}
return ActorFollowModel.findAndCountAll(query)
.then(({ rows, count }) => {
return {
data: rows,
total: count
}
})
}
static listSubscriptionsForApi (id: number, start: number, count: number, sort: string) { static listSubscriptionsForApi (id: number, start: number, count: number, sort: string) {
const query = { const query = {
attributes: [], attributes: [],
@ -370,39 +423,6 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
}) })
} }
static listFollowersForApi (id: number, start: number, count: number, sort: string) {
const query = {
distinct: true,
offset: start,
limit: count,
order: getSort(sort),
include: [
{
model: ActorModel,
required: true,
as: 'ActorFollower',
include: [ ServerModel ]
},
{
model: ActorModel,
as: 'ActorFollowing',
required: true,
where: {
id
}
}
]
}
return ActorFollowModel.findAndCountAll(query)
.then(({ rows, count }) => {
return {
data: rows,
total: count
}
})
}
static listAcceptedFollowerUrlsForApi (actorIds: number[], t: Sequelize.Transaction, start?: number, count?: number) { static listAcceptedFollowerUrlsForApi (actorIds: number[], t: Sequelize.Transaction, start?: number, count?: number) {
return ActorFollowModel.createListAcceptedFollowForApiQuery('followers', actorIds, t, start, count) return ActorFollowModel.createListAcceptedFollowForApiQuery('followers', actorIds, t, start, count)
} }

View File

@ -93,7 +93,26 @@ describe('Test follows', function () {
expect(server3Follow.state).to.equal('accepted') expect(server3Follow.state).to.equal('accepted')
}) })
it('Should have 0 followings on server 1 and 2', async function () { it('Should search followings on server 1', async function () {
{
const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', ':9002')
const follows = res.body.data
expect(res.body.total).to.equal(1)
expect(follows.length).to.equal(1)
expect(follows[ 0 ].following.host).to.equal('localhost:9002')
}
{
const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', 'bla')
const follows = res.body.data
expect(res.body.total).to.equal(0)
expect(follows.length).to.equal(0)
}
})
it('Should have 0 followings on server 2 and 3', async function () {
for (const server of [ servers[1], servers[2] ]) { for (const server of [ servers[1], servers[2] ]) {
const res = await getFollowingListPaginationAndSort(server.url, 0, 5, 'createdAt') const res = await getFollowingListPaginationAndSort(server.url, 0, 5, 'createdAt')
const follows = res.body.data const follows = res.body.data
@ -116,6 +135,25 @@ describe('Test follows', function () {
} }
}) })
it('Should search followers on server 2', async function () {
{
const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', '9001')
const follows = res.body.data
expect(res.body.total).to.equal(1)
expect(follows.length).to.equal(1)
expect(follows[ 0 ].following.host).to.equal('localhost:9003')
}
{
const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', 'bla')
const follows = res.body.data
expect(res.body.total).to.equal(0)
expect(follows.length).to.equal(0)
}
})
it('Should have 0 followers on server 1', async function () { it('Should have 0 followers on server 1', async function () {
const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 5, 'createdAt') const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 5, 'createdAt')
const follows = res.body.data const follows = res.body.data

View File

@ -2,7 +2,7 @@ import * as request from 'supertest'
import { ServerInfo } from './servers' import { ServerInfo } from './servers'
import { waitJobs } from './jobs' import { waitJobs } from './jobs'
function getFollowersListPaginationAndSort (url: string, start: number, count: number, sort: string) { function getFollowersListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string) {
const path = '/api/v1/server/followers' const path = '/api/v1/server/followers'
return request(url) return request(url)
@ -10,12 +10,13 @@ function getFollowersListPaginationAndSort (url: string, start: number, count: n
.query({ start }) .query({ start })
.query({ count }) .query({ count })
.query({ sort }) .query({ sort })
.query({ search })
.set('Accept', 'application/json') .set('Accept', 'application/json')
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
} }
function getFollowingListPaginationAndSort (url: string, start: number, count: number, sort: string) { function getFollowingListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string) {
const path = '/api/v1/server/following' const path = '/api/v1/server/following'
return request(url) return request(url)
@ -23,6 +24,7 @@ function getFollowingListPaginationAndSort (url: string, start: number, count: n
.query({ start }) .query({ start })
.query({ count }) .query({ count })
.query({ sort }) .query({ sort })
.query({ search })
.set('Accept', 'application/json') .set('Accept', 'application/json')
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)