]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Add ability to search on followers/following
authorChocobozzz <me@florianbigard.com>
Wed, 10 Oct 2018 07:43:53 +0000 (09:43 +0200)
committerChocobozzz <me@florianbigard.com>
Wed, 10 Oct 2018 07:43:53 +0000 (09:43 +0200)
13 files changed:
client/src/app/+admin/follows/followers-list/followers-list.component.html
client/src/app/+admin/follows/followers-list/followers-list.component.scss
client/src/app/+admin/follows/following-list/following-list.component.html
client/src/app/+admin/follows/following-list/following-list.component.scss
client/src/app/+admin/follows/following-list/following-list.component.ts
client/src/app/+admin/follows/shared/follow.service.ts
client/src/app/+admin/users/user-list/user-list.component.scss
client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.html
client/src/sass/primeng-custom.scss
server/controllers/api/server/follows.ts
server/models/activitypub/actor-follow.ts
server/tests/api/server/follows.ts
server/tests/utils/server/follows.ts

index 5645a60cc27345aab8a2f7f68eebcbf29ffb3ab2..fc022bdb47227c995cb15cbfe5d841665ad3cc58 100644 (file)
@@ -2,6 +2,15 @@
   [value]="followers" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
   [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">
     <tr>
       <th i18n style="width: 60px">ID</th>
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a6f0656b8b79d2f15cb6fe47be74c352f8488238 100644 (file)
@@ -0,0 +1,10 @@
+@import '_variables';
+@import '_mixins';
+
+.caption {
+  justify-content: flex-end;
+
+  input {
+    @include peertube-input-text(250px);
+  }
+}
\ No newline at end of file
index 8af624ac5369f7b09a2497f6820a7ce2d4571163..5bc8fbc2d49c53c05779f086206a70872b9dc11d 100644 (file)
@@ -2,6 +2,17 @@
   [value]="following" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
   [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">
     <tr>
       <th i18n style="width: 60px">ID</th>
index bfcdcaa49ce01524146c34f2cdf8d7446ae8b169..b3bb7f5f87de7418862cbf8e7369e7b6b700514d 100644 (file)
@@ -10,4 +10,12 @@ my-redundancy-checkbox /deep/ my-peertube-checkbox {
   label {
     margin: 0;
   }
+}
+
+.caption {
+  justify-content: flex-end;
+
+  input {
+    @include peertube-input-text(250px);
+  }
 }
\ No newline at end of file
index 70235a48d4a3f43d5b754a13a49cbcd5cadba1af..9b7029f755f425dd0695072ae6abd4bdcd0bbe96 100644 (file)
@@ -53,7 +53,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
   }
 
   protected loadData () {
-    this.followService.getFollowing(this.pagination, this.sort)
+    this.followService.getFollowing(this.pagination, this.sort, this.search)
                       .subscribe(
                         resultList => {
                           this.following = resultList.data
index 27169a9cdb7d1f2b73a32f600ce6cb07f4eed620..a2904179ead2ade1dac1818f976cbce914430b11 100644 (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()
     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 })
                .pipe(
                  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()
     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 })
                .pipe(
                  map(res => this.restExtractor.convertResultListDateToHuman(res)),
index 01f43dfe141c3fcf5c9c536ff853e9f22c333ee8..f235769f06bdecd252ab7db4a89d6ace1e32f6a2 100644 (file)
@@ -18,10 +18,7 @@ tr.banned {
 }
 
 .caption {
-  height: 40px;
-  display: flex;
   justify-content: space-between;
-  align-items: center;
 
   input {
     @include peertube-input-text(250px);
index 69b198faa77c3f423a73cf18481357c10388fba3..7c0df850dab84761695dd38f47323738a312c0b2 100644 (file)
@@ -22,9 +22,9 @@
       </span>
 
       <input
-          type="submit" i18n-value value="Submit" class="action-button-submit"
-          [disabled]="!form.valid"
-          (click)="close()"
+        type="submit" i18n-value value="Submit" class="action-button-submit"
+        [disabled]="!form.valid"
+        (click)="close()"
       />
     </div>
   </div>
index 4a19e0275463b2cf4146f8c72e0d8fc4f8c4749c..0568de4e2b04a6cbd68a69e0c577fc98ee867bfd 100644 (file)
@@ -16,6 +16,12 @@ p-table {
 
   .ui-table-caption {
     border: none;
+
+    .caption {
+      height: 40px;
+      display: flex;
+      align-items: center;
+    }
   }
 
   td {
index d62400e423a9a18c80c4e09b37a873f5a92c2d98..9fa6c34ba3315eb66fc144fff6bcab4a2f0571dd 100644 (file)
@@ -61,14 +61,26 @@ export {
 
 async function listFollowing (req: express.Request, res: express.Response, next: express.NextFunction) {
   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))
 }
 
 async function listFollowers (req: express.Request, res: express.Response, next: express.NextFunction) {
   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))
 }
index 27bb43daedf6d729418a70d75d11339a00bc5afa..3373355efff67f0aa8f852d78a8ab00249e82ed3 100644 (file)
@@ -280,7 +280,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
     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 = {
       distinct: true,
       offset: start,
@@ -299,7 +299,17 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
           model: ActorModel,
           as: 'ActorFollowing',
           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) {
     const query = {
       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) {
     return ActorFollowModel.createListAcceptedFollowForApiQuery('followers', actorIds, t, start, count)
   }
index 310c291bf946e0f4e5015347ea470f68ce4c99bc..e80e93e7f5b33e8a5ced35831f1d977334b0bc42 100644 (file)
@@ -93,7 +93,26 @@ describe('Test follows', function () {
     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] ]) {
       const res = await getFollowingListPaginationAndSort(server.url, 0, 5, 'createdAt')
       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 () {
     const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 5, 'createdAt')
     const follows = res.body.data
index 8a65a958b1145e0b688f5e5a6597ec5761deb274..7741757a67247f20a8c662102f3b736094f0c8c7 100644 (file)
@@ -2,7 +2,7 @@ import * as request from 'supertest'
 import { ServerInfo } from './servers'
 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'
 
   return request(url)
@@ -10,12 +10,13 @@ function getFollowersListPaginationAndSort (url: string, start: number, count: n
     .query({ start })
     .query({ count })
     .query({ sort })
+    .query({ search })
     .set('Accept', 'application/json')
     .expect(200)
     .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'
 
   return request(url)
@@ -23,6 +24,7 @@ function getFollowingListPaginationAndSort (url: string, start: number, count: n
     .query({ start })
     .query({ count })
     .query({ sort })
+    .query({ search })
     .set('Accept', 'application/json')
     .expect(200)
     .expect('Content-Type', /json/)