--- /dev/null
+<div class="row" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()">
+ <div class="col-xl-6 col-md-12">
+ <div i18n class="subtitle">Followers</div>
+
+ <div i18n class="no-results" *ngIf="followersPagination.totalItems === 0">This instance does not have followers.</div>
+
+ <a *ngFor="let follower of followers" [href]="buildLink(follower)" target="_blank" rel="noopener noreferrer">
+ {{ follower }}
+ </a>
+ </div>
+
+ <div class="col-xl-6 col-md-12">
+ <div i18n class="subtitle">Followings</div>
+
+ <div i18n class="no-results" *ngIf="followingsPagination.totalItems === 0">This instance does not have followings.</div>
+
+ <a *ngFor="let following of followings" [href]="buildLink(following)" target="_blank" rel="noopener noreferrer">
+ {{ following }}
+ </a>
+ </div>
+
+</div>
--- /dev/null
+@import '_variables';
+@import '_mixins';
+
+.subtitle {
+ font-size: 18px;
+ font-weight: $font-semibold;
+ margin-bottom: 20px;
+}
+
+a {
+ display: block;
+ width: fit-content;
+ margin-top: 3px;
+}
--- /dev/null
+import { Component, OnInit } from '@angular/core'
+import { FollowService } from '@app/shared/instance/follow.service'
+import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model'
+import { Notifier } from '@app/core'
+import { RestService } from '@app/shared'
+import { SortMeta } from 'primeng/api'
+
+@Component({
+ selector: 'my-about-follows',
+ templateUrl: './about-follows.component.html',
+ styleUrls: [ './about-follows.component.scss' ]
+})
+
+export class AboutFollowsComponent implements OnInit {
+ followers: string[] = []
+ followings: string[] = []
+
+ followersPagination: ComponentPagination = {
+ currentPage: 1,
+ itemsPerPage: 40,
+ totalItems: null
+ }
+
+ followingsPagination: ComponentPagination = {
+ currentPage: 1,
+ itemsPerPage: 40,
+ totalItems: null
+ }
+
+ sort: SortMeta = {
+ field: 'createdAt',
+ order: -1
+ }
+
+ constructor (
+ private restService: RestService,
+ private notifier: Notifier,
+ private followService: FollowService
+ ) { }
+
+ ngOnInit () {
+ this.loadMoreFollowers()
+
+ this.loadMoreFollowings()
+ }
+
+ onNearOfBottom () {
+ this.onNearOfFollowersBottom()
+
+ this.onNearOfFollowingsBottom()
+ }
+
+ onNearOfFollowersBottom () {
+ if (!hasMoreItems(this.followersPagination)) return
+
+ this.followersPagination.currentPage += 1
+ this.loadMoreFollowers()
+ }
+
+ onNearOfFollowingsBottom () {
+ if (!hasMoreItems(this.followingsPagination)) return
+
+ this.followingsPagination.currentPage += 1
+ this.loadMoreFollowings()
+ }
+
+ buildLink (host: string) {
+ return window.location.protocol + '//' + host
+ }
+
+ private loadMoreFollowers () {
+ const pagination = this.restService.componentPaginationToRestPagination(this.followersPagination)
+
+ this.followService.getFollowers(pagination, this.sort)
+ .subscribe(
+ resultList => {
+ const newFollowers = resultList.data.map(r => r.follower.host)
+ this.followers = this.followers.concat(newFollowers)
+
+ this.followersPagination.totalItems = resultList.total
+ },
+
+ err => this.notifier.error(err.message)
+ )
+ }
+
+ private loadMoreFollowings () {
+ const pagination = this.restService.componentPaginationToRestPagination(this.followingsPagination)
+
+ this.followService.getFollowing(pagination, this.sort)
+ .subscribe(
+ resultList => {
+ const newFollowings = resultList.data.map(r => r.following.host)
+ this.followings = this.followings.concat(newFollowings)
+
+ this.followingsPagination.totalItems = resultList.total
+ },
+
+ err => this.notifier.error(err.message)
+ )
+ }
+
+}
import { AboutComponent } from './about.component'
import { AboutInstanceComponent } from '@app/+about/about-instance/about-instance.component'
import { AboutPeertubeComponent } from '@app/+about/about-peertube/about-peertube.component'
+import { AboutFollowsComponent } from '@app/+about/about-follows/about-follows.component'
const aboutRoutes: Routes = [
{
title: 'About PeerTube'
}
}
+ },
+ {
+ path: 'follows',
+ component: AboutFollowsComponent,
+ data: {
+ meta: {
+ title: 'About follows'
+ }
+ }
}
]
}
<a i18n routerLink="instance" routerLinkActive="active" class="title-page">Instance</a>
<a i18n routerLink="peertube" routerLinkActive="active" class="title-page">PeerTube</a>
+
+ <a i18n routerLink="follows" routerLinkActive="active" class="title-page">Follows</a>
</div>
</div>
<div class="margin-content">
<router-outlet></router-outlet>
</div>
-</div>
\ No newline at end of file
+</div>
import { AboutInstanceComponent } from '@app/+about/about-instance/about-instance.component'
import { AboutPeertubeComponent } from '@app/+about/about-peertube/about-peertube.component'
import { ContactAdminModalComponent } from '@app/+about/about-instance/contact-admin-modal.component'
+import { AboutFollowsComponent } from '@app/+about/about-follows/about-follows.component'
@NgModule({
imports: [
AboutComponent,
AboutInstanceComponent,
AboutPeertubeComponent,
+ AboutFollowsComponent,
ContactAdminModalComponent
],
import { SharedModule } from '../shared'
import { AdminRoutingModule } from './admin-routing.module'
import { AdminComponent } from './admin.component'
-import { FollowersListComponent, FollowingAddComponent, FollowsComponent, FollowService } from './follows'
+import { FollowersListComponent, FollowingAddComponent, FollowsComponent } from './follows'
import { FollowingListComponent } from './follows/following-list/following-list.component'
import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersComponent, UserUpdateComponent } from './users'
import {
],
providers: [
- FollowService,
RedundancyService,
JobService,
LogsService,
import { SortMeta } from 'primeng/primeng'
import { ActorFollow } from '../../../../../../shared/models/actors/follow.model'
import { RestPagination, RestTable } from '../../../shared'
-import { FollowService } from '../shared'
+import { FollowService } from '@app/shared/instance/follow.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({
import { Notifier } from '@app/core'
import { ConfirmService } from '../../../core'
import { validateHost } from '../../../shared'
-import { FollowService } from '../shared'
+import { FollowService } from '@app/shared/instance/follow.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({
import { ActorFollow } from '../../../../../../shared/models/actors/follow.model'
import { ConfirmService } from '../../../core/confirm/confirm.service'
import { RestPagination, RestTable } from '../../../shared'
-import { FollowService } from '../shared'
+import { FollowService } from '@app/shared/instance/follow.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({
export * from './following-add'
export * from './followers-list'
export * from './following-list'
-export * from './shared'
export * from './follows.component'
export * from './follows.routes'
+++ /dev/null
-import { catchError, map } from 'rxjs/operators'
-import { HttpClient, HttpParams } from '@angular/common/http'
-import { Injectable } from '@angular/core'
-import { SortMeta } from 'primeng/primeng'
-import { Observable } from 'rxjs'
-import { ActorFollow, ResultList } from '../../../../../../shared'
-import { environment } from '../../../../environments/environment'
-import { RestExtractor, RestPagination, RestService } from '../../../shared'
-
-@Injectable()
-export class FollowService {
- private static BASE_APPLICATION_URL = environment.apiUrl + '/api/v1/server'
-
- constructor (
- private authHttp: HttpClient,
- private restService: RestService,
- private restExtractor: RestExtractor
- ) {
- }
-
- 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)),
- catchError(res => this.restExtractor.handleError(res))
- )
- }
-
- 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)),
- catchError(res => this.restExtractor.handleError(res))
- )
- }
-
- follow (notEmptyHosts: string[]) {
- const body = {
- hosts: notEmptyHosts
- }
-
- return this.authHttp.post(FollowService.BASE_APPLICATION_URL + '/following', body)
- .pipe(
- map(this.restExtractor.extractDataBool),
- catchError(res => this.restExtractor.handleError(res))
- )
- }
-
- unfollow (follow: ActorFollow) {
- return this.authHttp.delete(FollowService.BASE_APPLICATION_URL + '/following/' + follow.following.host)
- .pipe(
- map(this.restExtractor.extractDataBool),
- catchError(res => this.restExtractor.handleError(res))
- )
- }
-
- acceptFollower (follow: ActorFollow) {
- const handle = follow.follower.name + '@' + follow.follower.host
-
- return this.authHttp.post(`${FollowService.BASE_APPLICATION_URL}/followers/${handle}/accept`, {})
- .pipe(
- map(this.restExtractor.extractDataBool),
- catchError(res => this.restExtractor.handleError(res))
- )
- }
-
- rejectFollower (follow: ActorFollow) {
- const handle = follow.follower.name + '@' + follow.follower.host
-
- return this.authHttp.post(`${FollowService.BASE_APPLICATION_URL}/followers/${handle}/reject`, {})
- .pipe(
- map(this.restExtractor.extractDataBool),
- catchError(res => this.restExtractor.handleError(res))
- )
- }
-
- removeFollower (follow: ActorFollow) {
- const handle = follow.follower.name + '@' + follow.follower.host
-
- return this.authHttp.delete(`${FollowService.BASE_APPLICATION_URL}/followers/${handle}`)
- .pipe(
- map(this.restExtractor.extractDataBool),
- catchError(res => this.restExtractor.handleError(res))
- )
- }
-}
+++ /dev/null
-export * from './follow.service'
--- /dev/null
+import { catchError, map } from 'rxjs/operators'
+import { HttpClient, HttpParams } from '@angular/common/http'
+import { Injectable } from '@angular/core'
+import { SortMeta } from 'primeng/primeng'
+import { Observable } from 'rxjs'
+import { ActorFollow, ResultList } from '@shared/index'
+import { environment } from '../../../environments/environment'
+import { RestExtractor, RestPagination, RestService } from '../rest'
+
+@Injectable()
+export class FollowService {
+ private static BASE_APPLICATION_URL = 'https://peertube2.cpy.re' + '/api/v1/server'
+
+ constructor (
+ private authHttp: HttpClient,
+ private restService: RestService,
+ private restExtractor: RestExtractor
+ ) {
+ }
+
+ 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)),
+ catchError(res => this.restExtractor.handleError(res))
+ )
+ }
+
+ 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)),
+ catchError(res => this.restExtractor.handleError(res))
+ )
+ }
+
+ follow (notEmptyHosts: string[]) {
+ const body = {
+ hosts: notEmptyHosts
+ }
+
+ return this.authHttp.post(FollowService.BASE_APPLICATION_URL + '/following', body)
+ .pipe(
+ map(this.restExtractor.extractDataBool),
+ catchError(res => this.restExtractor.handleError(res))
+ )
+ }
+
+ unfollow (follow: ActorFollow) {
+ return this.authHttp.delete(FollowService.BASE_APPLICATION_URL + '/following/' + follow.following.host)
+ .pipe(
+ map(this.restExtractor.extractDataBool),
+ catchError(res => this.restExtractor.handleError(res))
+ )
+ }
+
+ acceptFollower (follow: ActorFollow) {
+ const handle = follow.follower.name + '@' + follow.follower.host
+
+ return this.authHttp.post(`${FollowService.BASE_APPLICATION_URL}/followers/${handle}/accept`, {})
+ .pipe(
+ map(this.restExtractor.extractDataBool),
+ catchError(res => this.restExtractor.handleError(res))
+ )
+ }
+
+ rejectFollower (follow: ActorFollow) {
+ const handle = follow.follower.name + '@' + follow.follower.host
+
+ return this.authHttp.post(`${FollowService.BASE_APPLICATION_URL}/followers/${handle}/reject`, {})
+ .pipe(
+ map(this.restExtractor.extractDataBool),
+ catchError(res => this.restExtractor.handleError(res))
+ )
+ }
+
+ removeFollower (follow: ActorFollow) {
+ const handle = follow.follower.name + '@' + follow.follower.host
+
+ return this.authHttp.delete(`${FollowService.BASE_APPLICATION_URL}/followers/${handle}`)
+ .pipe(
+ map(this.restExtractor.extractDataBool),
+ catchError(res => this.restExtractor.handleError(res))
+ )
+ }
+}
import { VideoDownloadComponent } from '@app/shared/video/modals/video-download.component'
import { VideoReportComponent } from '@app/shared/video/modals/video-report.component'
import { ClipboardModule } from 'ngx-clipboard'
+import { FollowService } from '@app/shared/instance/follow.service'
@NgModule({
imports: [
UserNotificationService,
+ FollowService,
+
I18n
]
})
{{ getCurrentGroupedDateLabel(video) }}
</div>
-
<my-video-miniature
[video]="video" [user]="user" [ownerDisplayType]="ownerDisplayType"
[displayVideoActions]="displayVideoActions" [displayOptions]="displayOptions"