aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+my-library/my-follows
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-10-19 09:44:43 +0200
committerChocobozzz <me@florianbigard.com>2021-10-20 09:25:44 +0200
commit4beda9e12adc7b1f3b178cecd6863ebf3cf431f1 (patch)
tree6244a10b286d66c6dcd7799aee630670d0493781 /client/src/app/+my-library/my-follows
parent9593a78ae1368a9ad8bb11044fce6fde2892701a (diff)
downloadPeerTube-4beda9e12adc7b1f3b178cecd6863ebf3cf431f1.tar.gz
PeerTube-4beda9e12adc7b1f3b178cecd6863ebf3cf431f1.tar.zst
PeerTube-4beda9e12adc7b1f3b178cecd6863ebf3cf431f1.zip
Add ability to view my followers
Diffstat (limited to 'client/src/app/+my-library/my-follows')
-rw-r--r--client/src/app/+my-library/my-follows/my-followers.component.html31
-rw-r--r--client/src/app/+my-library/my-follows/my-followers.component.scss26
-rw-r--r--client/src/app/+my-library/my-follows/my-followers.component.ts76
-rw-r--r--client/src/app/+my-library/my-follows/my-subscriptions.component.html36
-rw-r--r--client/src/app/+my-library/my-follows/my-subscriptions.component.scss16
-rw-r--r--client/src/app/+my-library/my-follows/my-subscriptions.component.ts57
6 files changed, 242 insertions, 0 deletions
diff --git a/client/src/app/+my-library/my-follows/my-followers.component.html b/client/src/app/+my-library/my-follows/my-followers.component.html
new file mode 100644
index 000000000..d2b2dccb6
--- /dev/null
+++ b/client/src/app/+my-library/my-follows/my-followers.component.html
@@ -0,0 +1,31 @@
1<h1>
2 <span>
3 <my-global-icon iconName="follower" aria-hidden="true"></my-global-icon>
4 <ng-container i18n>My followers</ng-container>
5 <span class="badge badge-secondary"> {{ pagination.totalItems }}</span>
6 </span>
7</h1>
8
9<div class="followers-header">
10 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
11</div>
12
13<div class="no-results" i18n *ngIf="pagination.totalItems === 0">No follower found.</div>
14
15<div class="actors" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
16 <div *ngFor="let follow of follows" class="actor">
17 <my-actor-avatar [account]="follow.follower" [href]="follow.follower.url"></my-actor-avatar>
18
19 <div class="actor-info">
20 <a [href]="follow.follower.url" class="actor-names" rel="noopener noreferrer" target="_blank" i18n-title title="Follower page">
21 <div class="actor-display-name">{{ follow.follower.name + '@' + follow.follower.host }}</div>
22 <span class="glyphicon glyphicon-new-window"></span>
23 </a>
24
25 <div class="text-muted">
26 <ng-container *ngIf="isFollowingAccount(follow)" i18n>Is following all your channels</ng-container>
27 <ng-container *ngIf="!isFollowingAccount(follow)" i18n>Is following your channel {{ follow.following.name }}</ng-container>
28 </div>
29 </div>
30 </div>
31</div>
diff --git a/client/src/app/+my-library/my-follows/my-followers.component.scss b/client/src/app/+my-library/my-follows/my-followers.component.scss
new file mode 100644
index 000000000..15b51c419
--- /dev/null
+++ b/client/src/app/+my-library/my-follows/my-followers.component.scss
@@ -0,0 +1,26 @@
1@use '_variables' as *;
2@use '_mixins' as *;
3@use '_actor' as *;
4
5.followers-header {
6 margin-bottom: 30px;
7 display: flex;
8}
9
10input[type=text] {
11 @include peertube-input-text(300px);
12}
13
14.actor {
15 @include actor-row($avatar-size: 40px, $min-height: auto, $separator: true);
16
17 .actor-display-name {
18 font-size: 16px;
19
20 + .glyphicon {
21 @include margin-left(5px);
22
23 font-size: 12px;
24 }
25 }
26}
diff --git a/client/src/app/+my-library/my-follows/my-followers.component.ts b/client/src/app/+my-library/my-follows/my-followers.component.ts
new file mode 100644
index 000000000..a7bbe6d99
--- /dev/null
+++ b/client/src/app/+my-library/my-follows/my-followers.component.ts
@@ -0,0 +1,76 @@
1import { Subject } from 'rxjs'
2import { Component, OnInit } from '@angular/core'
3import { ActivatedRoute } from '@angular/router'
4import { AuthService, ComponentPagination, Notifier } from '@app/core'
5import { UserSubscriptionService } from '@app/shared/shared-user-subscription'
6import { ActorFollow } from '@shared/models'
7
8@Component({
9 templateUrl: './my-followers.component.html',
10 styleUrls: [ './my-followers.component.scss' ]
11})
12export class MyFollowersComponent implements OnInit {
13 follows: ActorFollow[] = []
14
15 pagination: ComponentPagination = {
16 currentPage: 1,
17 itemsPerPage: 10,
18 totalItems: null
19 }
20
21 onDataSubject = new Subject<any[]>()
22 search: string
23
24 constructor (
25 private route: ActivatedRoute,
26 private auth: AuthService,
27 private userSubscriptionService: UserSubscriptionService,
28 private notifier: Notifier
29 ) {}
30
31 ngOnInit () {
32 if (this.route.snapshot.queryParams['search']) {
33 this.search = this.route.snapshot.queryParams['search']
34 }
35 }
36
37 onNearOfBottom () {
38 // Last page
39 if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return
40
41 this.pagination.currentPage += 1
42 this.loadFollowers()
43 }
44
45 onSearch (search: string) {
46 this.search = search
47 this.loadFollowers(false)
48 }
49
50 isFollowingAccount (follow: ActorFollow) {
51 return follow.following.name === this.getUsername()
52 }
53
54 private loadFollowers (more = true) {
55 this.userSubscriptionService.listFollowers({
56 pagination: this.pagination,
57 nameWithHost: this.getUsername(),
58 search: this.search
59 }).subscribe({
60 next: res => {
61 this.follows = more
62 ? this.follows.concat(res.data)
63 : res.data
64 this.pagination.totalItems = res.total
65
66 this.onDataSubject.next(res.data)
67 },
68
69 error: err => this.notifier.error(err.message)
70 })
71 }
72
73 private getUsername () {
74 return this.auth.getUser().username
75 }
76}
diff --git a/client/src/app/+my-library/my-follows/my-subscriptions.component.html b/client/src/app/+my-library/my-follows/my-subscriptions.component.html
new file mode 100644
index 000000000..775f0e783
--- /dev/null
+++ b/client/src/app/+my-library/my-follows/my-subscriptions.component.html
@@ -0,0 +1,36 @@
1<h1>
2 <span>
3 <my-global-icon iconName="subscriptions" aria-hidden="true"></my-global-icon>
4 <ng-container i18n>My subscriptions</ng-container>
5 <span class="badge badge-secondary"> {{ pagination.totalItems }}</span>
6 </span>
7</h1>
8
9<div class="video-subscriptions-header">
10 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
11</div>
12
13<div class="no-results" i18n *ngIf="pagination.totalItems === 0">You don't have any subscription yet.</div>
14
15<div class="actors" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
16 <div *ngFor="let videoChannel of videoChannels" class="actor">
17 <my-actor-avatar [channel]="videoChannel" [internalHref]="[ '/c', videoChannel.nameWithHost ]"></my-actor-avatar>
18
19 <div class="actor-info">
20 <a [routerLink]="[ '/c', videoChannel.nameWithHost ]" class="actor-names" i18n-title title="Channel page">
21 <div class="actor-display-name">{{ videoChannel.displayName }}</div>
22 <div class="actor-name">{{ videoChannel.nameWithHost }}</div>
23 </a>
24
25 <div i18n class="actor-followers">{{ videoChannel.followersCount }} subscribers</div>
26
27 <a [routerLink]="[ '/a', videoChannel.ownerBy ]" i18n-title title="Owner account page" class="actor-owner">
28 <span i18n>Created by {{ videoChannel.ownerBy }}</span>
29
30 <my-actor-avatar [account]="videoChannel.ownerAccount" size="18"></my-actor-avatar>
31 </a>
32 </div>
33
34 <my-subscribe-button [videoChannels]="[videoChannel]"></my-subscribe-button>
35 </div>
36</div>
diff --git a/client/src/app/+my-library/my-follows/my-subscriptions.component.scss b/client/src/app/+my-library/my-follows/my-subscriptions.component.scss
new file mode 100644
index 000000000..310e11cb0
--- /dev/null
+++ b/client/src/app/+my-library/my-follows/my-subscriptions.component.scss
@@ -0,0 +1,16 @@
1@use '_variables' as *;
2@use '_mixins' as *;
3@use '_actor' as *;
4
5.video-subscriptions-header {
6 margin-bottom: 30px;
7 display: flex;
8}
9
10input[type=text] {
11 @include peertube-input-text(300px);
12}
13
14.actor {
15 @include actor-row;
16}
diff --git a/client/src/app/+my-library/my-follows/my-subscriptions.component.ts b/client/src/app/+my-library/my-follows/my-subscriptions.component.ts
new file mode 100644
index 000000000..f676aa014
--- /dev/null
+++ b/client/src/app/+my-library/my-follows/my-subscriptions.component.ts
@@ -0,0 +1,57 @@
1import { Subject } from 'rxjs'
2import { Component } from '@angular/core'
3import { ComponentPagination, Notifier } from '@app/core'
4import { VideoChannel } from '@app/shared/shared-main'
5import { UserSubscriptionService } from '@app/shared/shared-user-subscription'
6
7@Component({
8 templateUrl: './my-subscriptions.component.html',
9 styleUrls: [ './my-subscriptions.component.scss' ]
10})
11export class MySubscriptionsComponent {
12 videoChannels: VideoChannel[] = []
13
14 pagination: ComponentPagination = {
15 currentPage: 1,
16 itemsPerPage: 10,
17 totalItems: null
18 }
19
20 onDataSubject = new Subject<any[]>()
21
22 search: string
23
24 constructor (
25 private userSubscriptionService: UserSubscriptionService,
26 private notifier: Notifier
27 ) {}
28
29 onNearOfBottom () {
30 // Last page
31 if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return
32
33 this.pagination.currentPage += 1
34 this.loadSubscriptions()
35 }
36
37 onSearch (search: string) {
38 this.search = search
39 this.loadSubscriptions(false)
40 }
41
42 private loadSubscriptions (more = true) {
43 this.userSubscriptionService.listSubscriptions({ pagination: this.pagination, search: this.search })
44 .subscribe({
45 next: res => {
46 this.videoChannels = more
47 ? this.videoChannels.concat(res.data)
48 : res.data
49 this.pagination.totalItems = res.total
50
51 this.onDataSubject.next(res.data)
52 },
53
54 error: err => this.notifier.error(err.message)
55 })
56 }
57}