aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+admin/follows
diff options
context:
space:
mode:
authorChocobozzz <florian.bigard@gmail.com>2017-11-15 10:10:41 +0100
committerChocobozzz <florian.bigard@gmail.com>2017-11-27 19:40:51 +0100
commit51548b31815c6f96f314ae96588a9adca150519d (patch)
treeb3298447b7ac128823016fdec92d083e07d9432e /client/src/app/+admin/follows
parent350e31d6b64e4973dfa5e9f7b46841cb09aeb1ad (diff)
downloadPeerTube-51548b31815c6f96f314ae96588a9adca150519d.tar.gz
PeerTube-51548b31815c6f96f314ae96588a9adca150519d.tar.zst
PeerTube-51548b31815c6f96f314ae96588a9adca150519d.zip
Add follow tabs
Following Follow Followers
Diffstat (limited to 'client/src/app/+admin/follows')
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.html16
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.scss3
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.ts41
-rw-r--r--client/src/app/+admin/follows/followers-list/index.ts1
-rw-r--r--client/src/app/+admin/follows/following-add/following-add.component.html35
-rw-r--r--client/src/app/+admin/follows/following-add/following-add.component.scss7
-rw-r--r--client/src/app/+admin/follows/following-add/following-add.component.ts121
-rw-r--r--client/src/app/+admin/follows/following-add/index.ts1
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.html16
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.ts40
-rw-r--r--client/src/app/+admin/follows/following-list/index.ts1
-rw-r--r--client/src/app/+admin/follows/follows.component.html11
-rw-r--r--client/src/app/+admin/follows/follows.component.scss21
-rw-r--r--client/src/app/+admin/follows/follows.component.ts43
-rw-r--r--client/src/app/+admin/follows/follows.routes.ts53
-rw-r--r--client/src/app/+admin/follows/index.ts6
-rw-r--r--client/src/app/+admin/follows/shared/follow.service.ts49
-rw-r--r--client/src/app/+admin/follows/shared/index.ts1
18 files changed, 466 insertions, 0 deletions
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.html b/client/src/app/+admin/follows/followers-list/followers-list.component.html
new file mode 100644
index 000000000..24d75d2b3
--- /dev/null
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.html
@@ -0,0 +1,16 @@
1<div class="row">
2 <div class="content-padding">
3 <h3>Followers list</h3>
4
5 <p-dataTable
6 [value]="followers" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
7 sortField="createdAt" (onLazyLoad)="loadLazy($event)"
8 >
9 <p-column field="id" header="ID"></p-column>
10 <p-column field="host" header="Host"></p-column>
11 <p-column field="email" header="Email"></p-column>
12 <p-column field="score" header="Score"></p-column>
13 <p-column field="createdAt" header="Created date" [sortable]="true"></p-column>
14 </p-dataTable>
15 </div>
16</div>
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.scss b/client/src/app/+admin/follows/followers-list/followers-list.component.scss
new file mode 100644
index 000000000..0a0f621c6
--- /dev/null
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.scss
@@ -0,0 +1,3 @@
1.btn {
2 margin-top: 10px;
3}
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.ts b/client/src/app/+admin/follows/followers-list/followers-list.component.ts
new file mode 100644
index 000000000..208a0c648
--- /dev/null
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.ts
@@ -0,0 +1,41 @@
1import { Component, OnInit } from '@angular/core'
2
3import { NotificationsService } from 'angular2-notifications'
4import { SortMeta } from 'primeng/primeng'
5
6import { ConfirmService } from '../../../core'
7import { RestTable, RestPagination } from '../../../shared'
8import { Pod } from '../../../../../../shared'
9import { FollowService } from '../shared'
10
11@Component({
12 selector: 'my-followers-list',
13 templateUrl: './followers-list.component.html',
14 styleUrls: [ './followers-list.component.scss' ]
15})
16export class FollowersListComponent extends RestTable {
17 followers: Pod[] = []
18 totalRecords = 0
19 rowsPerPage = 10
20 sort: SortMeta = { field: 'createdAt', order: 1 }
21 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
22
23 constructor (
24 private notificationsService: NotificationsService,
25 private followService: FollowService
26 ) {
27 super()
28 }
29
30 protected loadData () {
31 this.followService.getFollowers(this.pagination, this.sort)
32 .subscribe(
33 resultList => {
34 this.followers = resultList.data
35 this.totalRecords = resultList.total
36 },
37
38 err => this.notificationsService.error('Error', err.message)
39 )
40 }
41}
diff --git a/client/src/app/+admin/follows/followers-list/index.ts b/client/src/app/+admin/follows/followers-list/index.ts
new file mode 100644
index 000000000..15390cfbe
--- /dev/null
+++ b/client/src/app/+admin/follows/followers-list/index.ts
@@ -0,0 +1 @@
export * from './followers-list.component'
diff --git a/client/src/app/+admin/follows/following-add/following-add.component.html b/client/src/app/+admin/follows/following-add/following-add.component.html
new file mode 100644
index 000000000..111f6a8de
--- /dev/null
+++ b/client/src/app/+admin/follows/following-add/following-add.component.html
@@ -0,0 +1,35 @@
1<div class="row">
2 <div class="content-padding">
3
4 <h3>Add following</h3>
5
6 <div *ngIf="error" class="alert alert-danger">{{ error }}</div>
7
8 <form (ngSubmit)="addFollowing()" [formGroup]="form">
9 <div class="form-group" *ngFor="let host of hosts; let id = index; trackBy:customTrackBy">
10 <label [for]="'host-' + id">Host (so without "http://")</label>
11
12 <div class="input-group">
13 <input
14 type="text" class="form-control" placeholder="example.com"
15 [id]="'host-' + id" [formControlName]="'host-' + id"
16 />
17 <span class="input-group-btn">
18 <button *ngIf="displayAddField(id)" (click)="addField()" class="btn btn-default" type="button">+</button>
19 <button *ngIf="displayRemoveField(id)" (click)="removeField(id)" class="btn btn-default" type="button">-</button>
20 </span>
21 </div>
22
23 <div [hidden]="form.controls['host-' + id].valid || form.controls['host-' + id].pristine" class="alert alert-warning">
24 It should be a valid host.
25 </div>
26 </div>
27
28 <div *ngIf="canMakeFriends() === false" class="alert alert-warning">
29 It seems that you are not on a HTTPS pod. Your webserver need to have TLS activated in order to follow servers.
30 </div>
31
32 <input type="submit" value="Add following" class="btn btn-default" [disabled]="!isFormValid()">
33 </form>
34 </div>
35</div>
diff --git a/client/src/app/+admin/follows/following-add/following-add.component.scss b/client/src/app/+admin/follows/following-add/following-add.component.scss
new file mode 100644
index 000000000..5fde51636
--- /dev/null
+++ b/client/src/app/+admin/follows/following-add/following-add.component.scss
@@ -0,0 +1,7 @@
1table {
2 margin-bottom: 40px;
3}
4
5.input-group-btn button {
6 width: 35px;
7}
diff --git a/client/src/app/+admin/follows/following-add/following-add.component.ts b/client/src/app/+admin/follows/following-add/following-add.component.ts
new file mode 100644
index 000000000..d95d6afa9
--- /dev/null
+++ b/client/src/app/+admin/follows/following-add/following-add.component.ts
@@ -0,0 +1,121 @@
1import { Component, OnInit } from '@angular/core'
2import { FormControl, FormGroup } from '@angular/forms'
3import { Router } from '@angular/router'
4
5import { NotificationsService } from 'angular2-notifications'
6
7import { ConfirmService } from '../../../core'
8import { validateHost } from '../../../shared'
9import { FollowService } from '../shared'
10
11@Component({
12 selector: 'my-following-add',
13 templateUrl: './following-add.component.html',
14 styleUrls: [ './following-add.component.scss' ]
15})
16export class FollowingAddComponent implements OnInit {
17 form: FormGroup
18 hosts: string[] = [ ]
19 error: string = null
20
21 constructor (
22 private router: Router,
23 private notificationsService: NotificationsService,
24 private confirmService: ConfirmService,
25 private followService: FollowService
26 ) {}
27
28 ngOnInit () {
29 this.form = new FormGroup({})
30 this.addField()
31 }
32
33 addField () {
34 this.form.addControl(`host-${this.hosts.length}`, new FormControl('', [ validateHost ]))
35 this.hosts.push('')
36 }
37
38 canMakeFriends () {
39 return window.location.protocol === 'https:'
40 }
41
42 customTrackBy (index: number, obj: any): any {
43 return index
44 }
45
46 displayAddField (index: number) {
47 return index === (this.hosts.length - 1)
48 }
49
50 displayRemoveField (index: number) {
51 return (index !== 0 || this.hosts.length > 1) && index !== (this.hosts.length - 1)
52 }
53
54 isFormValid () {
55 // Do not check the last input
56 for (let i = 0; i < this.hosts.length - 1; i++) {
57 if (!this.form.controls[`host-${i}`].valid) return false
58 }
59
60 const lastIndex = this.hosts.length - 1
61 // If the last input (which is not the first) is empty, it's ok
62 if (this.hosts[lastIndex] === '' && lastIndex !== 0) {
63 return true
64 } else {
65 return this.form.controls[`host-${lastIndex}`].valid
66 }
67 }
68
69 removeField (index: number) {
70 // Remove the last control
71 this.form.removeControl(`host-${this.hosts.length - 1}`)
72 this.hosts.splice(index, 1)
73 }
74
75 addFollowing () {
76 this.error = ''
77
78 const notEmptyHosts = this.getNotEmptyHosts()
79 if (notEmptyHosts.length === 0) {
80 this.error = 'You need to specify at least 1 host.'
81 return
82 }
83
84 if (!this.isHostsUnique(notEmptyHosts)) {
85 this.error = 'Hosts need to be unique.'
86 return
87 }
88
89 const confirmMessage = 'Are you sure to make friends with:<br /> - ' + notEmptyHosts.join('<br /> - ')
90 this.confirmService.confirm(confirmMessage, 'Follow new server(s)').subscribe(
91 res => {
92 if (res === false) return
93
94 this.followService.follow(notEmptyHosts).subscribe(
95 status => {
96 this.notificationsService.success('Success', 'Follow request(s) sent!')
97 // Wait requests between pods
98 setTimeout(() => this.router.navigate([ '/admin/friends/list' ]), 1000)
99 },
100
101 err => this.notificationsService.error('Error', err.message)
102 )
103 }
104 )
105 }
106
107 private getNotEmptyHosts () {
108 const notEmptyHosts = []
109
110 Object.keys(this.form.value).forEach((hostKey) => {
111 const host = this.form.value[hostKey]
112 if (host !== '') notEmptyHosts.push(host)
113 })
114
115 return notEmptyHosts
116 }
117
118 private isHostsUnique (hosts: string[]) {
119 return hosts.every(host => hosts.indexOf(host) === hosts.lastIndexOf(host))
120 }
121}
diff --git a/client/src/app/+admin/follows/following-add/index.ts b/client/src/app/+admin/follows/following-add/index.ts
new file mode 100644
index 000000000..1b1897ffa
--- /dev/null
+++ b/client/src/app/+admin/follows/following-add/index.ts
@@ -0,0 +1 @@
export * from './following-add.component'
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.html b/client/src/app/+admin/follows/following-list/following-list.component.html
new file mode 100644
index 000000000..fbcebfaa7
--- /dev/null
+++ b/client/src/app/+admin/follows/following-list/following-list.component.html
@@ -0,0 +1,16 @@
1<div class="row">
2 <div class="content-padding">
3 <h3>Following list</h3>
4
5 <p-dataTable
6 [value]="following" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
7 sortField="createdAt" (onLazyLoad)="loadLazy($event)"
8 >
9 <p-column field="id" header="ID"></p-column>
10 <p-column field="host" header="Host"></p-column>
11 <p-column field="email" header="Email"></p-column>
12 <p-column field="score" header="Score"></p-column>
13 <p-column field="createdAt" header="Created date" [sortable]="true"></p-column>
14 </p-dataTable>
15 </div>
16</div>
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts
new file mode 100644
index 000000000..7d2c5084b
--- /dev/null
+++ b/client/src/app/+admin/follows/following-list/following-list.component.ts
@@ -0,0 +1,40 @@
1import { Component, OnInit } from '@angular/core'
2
3import { NotificationsService } from 'angular2-notifications'
4import { SortMeta } from 'primeng/primeng'
5
6import { ConfirmService } from '../../../core'
7import { RestTable, RestPagination } from '../../../shared'
8import { Pod } from '../../../../../../shared'
9import { FollowService } from '../shared'
10
11@Component({
12 selector: 'my-followers-list',
13 templateUrl: './following-list.component.html'
14})
15export class FollowingListComponent extends RestTable {
16 following: Pod[] = []
17 totalRecords = 0
18 rowsPerPage = 10
19 sort: SortMeta = { field: 'createdAt', order: 1 }
20 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
21
22 constructor (
23 private notificationsService: NotificationsService,
24 private followService: FollowService
25 ) {
26 super()
27 }
28
29 protected loadData () {
30 this.followService.getFollowing(this.pagination, this.sort)
31 .subscribe(
32 resultList => {
33 this.following = resultList.data
34 this.totalRecords = resultList.total
35 },
36
37 err => this.notificationsService.error('Error', err.message)
38 )
39 }
40}
diff --git a/client/src/app/+admin/follows/following-list/index.ts b/client/src/app/+admin/follows/following-list/index.ts
new file mode 100644
index 000000000..a70d46a7e
--- /dev/null
+++ b/client/src/app/+admin/follows/following-list/index.ts
@@ -0,0 +1 @@
export * from './following-list.component'
diff --git a/client/src/app/+admin/follows/follows.component.html b/client/src/app/+admin/follows/follows.component.html
new file mode 100644
index 000000000..b67bc9736
--- /dev/null
+++ b/client/src/app/+admin/follows/follows.component.html
@@ -0,0 +1,11 @@
1<div class="follows-menu">
2 <tabset #followsMenuTabs>
3 <tab *ngFor="let link of links">
4 <ng-template tabHeading>
5 <a class="tab-link" [routerLink]="link.path">{{ link.title }}</a>
6 </ng-template>
7 </tab>
8 </tabset>
9</div>
10
11<router-outlet></router-outlet>
diff --git a/client/src/app/+admin/follows/follows.component.scss b/client/src/app/+admin/follows/follows.component.scss
new file mode 100644
index 000000000..d8ab41975
--- /dev/null
+++ b/client/src/app/+admin/follows/follows.component.scss
@@ -0,0 +1,21 @@
1.follows-menu {
2 margin-top: 20px;
3}
4
5tabset /deep/ {
6 .nav-link {
7 padding: 0;
8 }
9
10 .tab-link {
11 display: block;
12 text-align: center;
13 height: 40px;
14 width: 120px;
15 line-height: 40px;
16
17 &:hover, &:active, &:focus {
18 text-decoration: none !important;
19 }
20 }
21}
diff --git a/client/src/app/+admin/follows/follows.component.ts b/client/src/app/+admin/follows/follows.component.ts
new file mode 100644
index 000000000..97422a41b
--- /dev/null
+++ b/client/src/app/+admin/follows/follows.component.ts
@@ -0,0 +1,43 @@
1import { AfterViewInit, Component, ViewChild } from '@angular/core'
2import { TabsetComponent } from 'ngx-bootstrap/tabs'
3
4@Component({
5 templateUrl: './follows.component.html',
6 styleUrls: [ './follows.component.scss' ]
7})
8export class FollowsComponent implements AfterViewInit {
9 @ViewChild('followsMenuTabs') followsMenuTabs: TabsetComponent
10
11 links = [
12 {
13 path: 'following-list',
14 title: 'Following'
15 },
16 {
17 path: 'following-add',
18 title: 'Follow'
19 },
20 {
21 path: 'followers-list',
22 title: 'Followers'
23 }
24 ]
25
26 ngAfterViewInit () {
27 // Avoid issue with change detector
28 setTimeout(() => this.updateActiveTab())
29 }
30
31 private updateActiveTab () {
32 const url = window.location.pathname
33
34 for (let i = 0; i < this.links.length; i++) {
35 const path = this.links[i].path
36
37 if (url.endsWith(path) === true) {
38 this.followsMenuTabs.tabs[i].active = true
39 return
40 }
41 }
42 }
43}
diff --git a/client/src/app/+admin/follows/follows.routes.ts b/client/src/app/+admin/follows/follows.routes.ts
new file mode 100644
index 000000000..b7d44f75b
--- /dev/null
+++ b/client/src/app/+admin/follows/follows.routes.ts
@@ -0,0 +1,53 @@
1import { Routes } from '@angular/router'
2
3import { UserRightGuard } from '../../core'
4import { FollowsComponent } from './follows.component'
5import { FollowingAddComponent } from './following-add'
6import { FollowersListComponent } from './followers-list'
7import { UserRight } from '../../../../../shared'
8import { FollowingListComponent } from './following-list/following-list.component'
9
10export const FollowsRoutes: Routes = [
11 {
12 path: 'follows',
13 component: FollowsComponent,
14 canActivate: [ UserRightGuard ],
15 data: {
16 userRight: UserRight.MANAGE_APPLICATION_FOLLOW
17 },
18 children: [
19 {
20 path: '',
21 redirectTo: 'following-list',
22 pathMatch: 'full'
23 },
24 {
25 path: 'following-list',
26 component: FollowingListComponent,
27 data: {
28 meta: {
29 title: 'Following list'
30 }
31 }
32 },
33 {
34 path: 'followers-list',
35 component: FollowersListComponent,
36 data: {
37 meta: {
38 title: 'Followers list'
39 }
40 }
41 },
42 {
43 path: 'following-add',
44 component: FollowingAddComponent,
45 data: {
46 meta: {
47 title: 'Add follow'
48 }
49 }
50 }
51 ]
52 }
53]
diff --git a/client/src/app/+admin/follows/index.ts b/client/src/app/+admin/follows/index.ts
new file mode 100644
index 000000000..7849a06e7
--- /dev/null
+++ b/client/src/app/+admin/follows/index.ts
@@ -0,0 +1,6 @@
1export * from './following-add'
2export * from './followers-list'
3export * from './following-list'
4export * from './shared'
5export * from './follows.component'
6export * from './follows.routes'
diff --git a/client/src/app/+admin/follows/shared/follow.service.ts b/client/src/app/+admin/follows/shared/follow.service.ts
new file mode 100644
index 000000000..622c33cea
--- /dev/null
+++ b/client/src/app/+admin/follows/shared/follow.service.ts
@@ -0,0 +1,49 @@
1import { Injectable } from '@angular/core'
2import { HttpClient, HttpParams } from '@angular/common/http'
3import { Observable } from 'rxjs/Observable'
4import 'rxjs/add/operator/catch'
5import 'rxjs/add/operator/map'
6
7import { SortMeta } from 'primeng/primeng'
8
9import { RestExtractor, RestPagination, RestService } from '../../../shared'
10import { Pod, ResultList } from '../../../../../../shared'
11
12@Injectable()
13export class FollowService {
14 private static BASE_APPLICATION_URL = API_URL + '/api/v1/application'
15
16 constructor (
17 private authHttp: HttpClient,
18 private restService: RestService,
19 private restExtractor: RestExtractor
20 ) {}
21
22 getFollowing (pagination: RestPagination, sort: SortMeta): Observable<ResultList<Pod>> {
23 let params = new HttpParams()
24 params = this.restService.addRestGetParams(params, pagination, sort)
25
26 return this.authHttp.get<ResultList<Account>>(FollowService.BASE_APPLICATION_URL + '/following', { params })
27 .map(res => this.restExtractor.convertResultListDateToHuman(res))
28 .catch(res => this.restExtractor.handleError(res))
29 }
30
31 getFollowers (pagination: RestPagination, sort: SortMeta): Observable<ResultList<Pod>> {
32 let params = new HttpParams()
33 params = this.restService.addRestGetParams(params, pagination, sort)
34
35 return this.authHttp.get<ResultList<Account>>(FollowService.BASE_APPLICATION_URL + '/followers', { params })
36 .map(res => this.restExtractor.convertResultListDateToHuman(res))
37 .catch(res => this.restExtractor.handleError(res))
38 }
39
40 follow (notEmptyHosts: String[]) {
41 const body = {
42 hosts: notEmptyHosts
43 }
44
45 return this.authHttp.post(FollowService.BASE_APPLICATION_URL + '/follow', body)
46 .map(this.restExtractor.extractDataBool)
47 .catch(res => this.restExtractor.handleError(res))
48 }
49}
diff --git a/client/src/app/+admin/follows/shared/index.ts b/client/src/app/+admin/follows/shared/index.ts
new file mode 100644
index 000000000..78d456def
--- /dev/null
+++ b/client/src/app/+admin/follows/shared/index.ts
@@ -0,0 +1 @@
export * from './follow.service'