aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+my-library
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
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')
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.component.html7
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss4
-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.html (renamed from client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html)14
-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.ts (renamed from client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts)0
-rw-r--r--client/src/app/+my-library/my-library-routing.module.ts12
-rw-r--r--client/src/app/+my-library/my-library.component.ts15
-rw-r--r--client/src/app/+my-library/my-library.module.ts6
-rw-r--r--client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss84
12 files changed, 194 insertions, 97 deletions
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html
index 4c5b46d5b..bbe583971 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html
@@ -27,7 +27,12 @@
27 <div class="video-channel-name">{{ videoChannel.nameWithHost }}</div> 27 <div class="video-channel-name">{{ videoChannel.nameWithHost }}</div>
28 </a> 28 </a>
29 29
30 <div i18n class="video-channel-followers">{videoChannel.followersCount, plural, =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}</div> 30 <a
31 i18n class="video-channel-followers"
32 [routerLink]="[ '/my-library', 'followers' ]" [queryParams]="{ search: 'channel:' + videoChannel.name }"
33 >
34 {videoChannel.followersCount, plural, =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}
35 </a>
31 36
32 <div i18n class="video-channel-videos">{videoChannel.videosCount, plural, =0 {No videos} =1 {1 video} other {{{ videoChannel.videosCount }} videos}}</div> 37 <div i18n class="video-channel-videos">{videoChannel.videosCount, plural, =0 {No videos} =1 {1 video} other {{{ videoChannel.videosCount }} videos}}</div>
33 38
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss
index 9ef5513b6..998e46cb2 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss
@@ -54,6 +54,10 @@ my-edit-button {
54 color: $grey-actor-name; 54 color: $grey-actor-name;
55} 55}
56 56
57.video-channel-followers {
58 color: pvar(--mainForegroundColor);
59}
60
57.video-channel-buttons { 61.video-channel-buttons {
58 margin-top: 10px; 62 margin-top: 10px;
59 min-width: 190px; 63 min-width: 190px;
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-subscriptions/my-subscriptions.component.html b/client/src/app/+my-library/my-follows/my-subscriptions.component.html
index ca5ad794a..775f0e783 100644
--- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html
+++ b/client/src/app/+my-library/my-follows/my-subscriptions.component.html
@@ -12,17 +12,17 @@
12 12
13<div class="no-results" i18n *ngIf="pagination.totalItems === 0">You don't have any subscription yet.</div> 13<div class="no-results" i18n *ngIf="pagination.totalItems === 0">You don't have any subscription yet.</div>
14 14
15<div class="video-channels" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()"> 15<div class="actors" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
16 <div *ngFor="let videoChannel of videoChannels" class="video-channel"> 16 <div *ngFor="let videoChannel of videoChannels" class="actor">
17 <my-actor-avatar [channel]="videoChannel" [internalHref]="[ '/c', videoChannel.nameWithHost ]"></my-actor-avatar> 17 <my-actor-avatar [channel]="videoChannel" [internalHref]="[ '/c', videoChannel.nameWithHost ]"></my-actor-avatar>
18 18
19 <div class="video-channel-info"> 19 <div class="actor-info">
20 <a [routerLink]="[ '/c', videoChannel.nameWithHost ]" class="video-channel-names" i18n-title title="Channel page"> 20 <a [routerLink]="[ '/c', videoChannel.nameWithHost ]" class="actor-names" i18n-title title="Channel page">
21 <div class="video-channel-display-name">{{ videoChannel.displayName }}</div> 21 <div class="actor-display-name">{{ videoChannel.displayName }}</div>
22 <div class="video-channel-name">{{ videoChannel.nameWithHost }}</div> 22 <div class="actor-name">{{ videoChannel.nameWithHost }}</div>
23 </a> 23 </a>
24 24
25 <div i18n class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div> 25 <div i18n class="actor-followers">{{ videoChannel.followersCount }} subscribers</div>
26 26
27 <a [routerLink]="[ '/a', videoChannel.ownerBy ]" i18n-title title="Owner account page" class="actor-owner"> 27 <a [routerLink]="[ '/a', videoChannel.ownerBy ]" i18n-title title="Owner account page" class="actor-owner">
28 <span i18n>Created by {{ videoChannel.ownerBy }}</span> 28 <span i18n>Created by {{ videoChannel.ownerBy }}</span>
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-subscriptions/my-subscriptions.component.ts b/client/src/app/+my-library/my-follows/my-subscriptions.component.ts
index f676aa014..f676aa014 100644
--- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts
+++ b/client/src/app/+my-library/my-follows/my-subscriptions.component.ts
diff --git a/client/src/app/+my-library/my-library-routing.module.ts b/client/src/app/+my-library/my-library-routing.module.ts
index 76894bed8..73858fb82 100644
--- a/client/src/app/+my-library/my-library-routing.module.ts
+++ b/client/src/app/+my-library/my-library-routing.module.ts
@@ -1,10 +1,11 @@
1import { NgModule } from '@angular/core' 1import { NgModule } from '@angular/core'
2import { RouterModule, Routes } from '@angular/router' 2import { RouterModule, Routes } from '@angular/router'
3import { LoginGuard } from '../core' 3import { LoginGuard } from '../core'
4import { MyFollowersComponent } from './my-follows/my-followers.component'
5import { MySubscriptionsComponent } from './my-follows/my-subscriptions.component'
4import { MyHistoryComponent } from './my-history/my-history.component' 6import { MyHistoryComponent } from './my-history/my-history.component'
5import { MyLibraryComponent } from './my-library.component' 7import { MyLibraryComponent } from './my-library.component'
6import { MyOwnershipComponent } from './my-ownership/my-ownership.component' 8import { MyOwnershipComponent } from './my-ownership/my-ownership.component'
7import { MySubscriptionsComponent } from './my-subscriptions/my-subscriptions.component'
8import { MyVideoImportsComponent } from './my-video-imports/my-video-imports.component' 9import { MyVideoImportsComponent } from './my-video-imports/my-video-imports.component'
9import { MyVideoPlaylistCreateComponent } from './my-video-playlists/my-video-playlist-create.component' 10import { MyVideoPlaylistCreateComponent } from './my-video-playlists/my-video-playlist-create.component'
10import { MyVideoPlaylistElementsComponent } from './my-video-playlists/my-video-playlist-elements.component' 11import { MyVideoPlaylistElementsComponent } from './my-video-playlists/my-video-playlist-elements.component'
@@ -100,6 +101,15 @@ const myLibraryRoutes: Routes = [
100 } 101 }
101 }, 102 },
102 { 103 {
104 path: 'followers',
105 component: MyFollowersComponent,
106 data: {
107 meta: {
108 title: $localize`My followers`
109 }
110 }
111 },
112 {
103 path: 'ownership', 113 path: 'ownership',
104 component: MyOwnershipComponent, 114 component: MyOwnershipComponent,
105 data: { 115 data: {
diff --git a/client/src/app/+my-library/my-library.component.ts b/client/src/app/+my-library/my-library.component.ts
index 16a7f63e3..ff901952f 100644
--- a/client/src/app/+my-library/my-library.component.ts
+++ b/client/src/app/+my-library/my-library.component.ts
@@ -61,8 +61,19 @@ export class MyLibraryComponent implements OnInit {
61 }, 61 },
62 62
63 { 63 {
64 label: $localize`Subscriptions`, 64 label: $localize`Follows`,
65 routerLink: '/my-library/subscriptions' 65 children: [
66 {
67 label: $localize`Subscriptions`,
68 iconName: 'subscriptions',
69 routerLink: '/my-library/subscriptions'
70 },
71 {
72 label: $localize`Followers`,
73 iconName: 'follower',
74 routerLink: '/my-library/followers'
75 }
76 ]
66 }, 77 },
67 78
68 { 79 {
diff --git a/client/src/app/+my-library/my-library.module.ts b/client/src/app/+my-library/my-library.module.ts
index 264ad03f7..360c53589 100644
--- a/client/src/app/+my-library/my-library.module.ts
+++ b/client/src/app/+my-library/my-library.module.ts
@@ -13,12 +13,13 @@ import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscripti
13import { SharedVideoLiveModule } from '@app/shared/shared-video-live' 13import { SharedVideoLiveModule } from '@app/shared/shared-video-live'
14import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature' 14import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature'
15import { SharedVideoPlaylistModule } from '@app/shared/shared-video-playlist/shared-video-playlist.module' 15import { SharedVideoPlaylistModule } from '@app/shared/shared-video-playlist/shared-video-playlist.module'
16import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module'
17import { MySubscriptionsComponent } from './my-follows/my-subscriptions.component'
16import { MyHistoryComponent } from './my-history/my-history.component' 18import { MyHistoryComponent } from './my-history/my-history.component'
17import { MyLibraryRoutingModule } from './my-library-routing.module' 19import { MyLibraryRoutingModule } from './my-library-routing.module'
18import { MyLibraryComponent } from './my-library.component' 20import { MyLibraryComponent } from './my-library.component'
19import { MyAcceptOwnershipComponent } from './my-ownership/my-accept-ownership/my-accept-ownership.component' 21import { MyAcceptOwnershipComponent } from './my-ownership/my-accept-ownership/my-accept-ownership.component'
20import { MyOwnershipComponent } from './my-ownership/my-ownership.component' 22import { MyOwnershipComponent } from './my-ownership/my-ownership.component'
21import { MySubscriptionsComponent } from './my-subscriptions/my-subscriptions.component'
22import { MyVideoImportsComponent } from './my-video-imports/my-video-imports.component' 23import { MyVideoImportsComponent } from './my-video-imports/my-video-imports.component'
23import { MyVideoPlaylistCreateComponent } from './my-video-playlists/my-video-playlist-create.component' 24import { MyVideoPlaylistCreateComponent } from './my-video-playlists/my-video-playlist-create.component'
24import { MyVideoPlaylistElementsComponent } from './my-video-playlists/my-video-playlist-elements.component' 25import { MyVideoPlaylistElementsComponent } from './my-video-playlists/my-video-playlist-elements.component'
@@ -26,7 +27,7 @@ import { MyVideoPlaylistUpdateComponent } from './my-video-playlists/my-video-pl
26import { MyVideoPlaylistsComponent } from './my-video-playlists/my-video-playlists.component' 27import { MyVideoPlaylistsComponent } from './my-video-playlists/my-video-playlists.component'
27import { VideoChangeOwnershipComponent } from './my-videos/modals/video-change-ownership.component' 28import { VideoChangeOwnershipComponent } from './my-videos/modals/video-change-ownership.component'
28import { MyVideosComponent } from './my-videos/my-videos.component' 29import { MyVideosComponent } from './my-videos/my-videos.component'
29import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module' 30import { MyFollowersComponent } from './my-follows/my-followers.component'
30 31
31@NgModule({ 32@NgModule({
32 imports: [ 33 imports: [
@@ -61,6 +62,7 @@ import { SharedActorImageModule } from '../shared/shared-actor-image/shared-acto
61 MyAcceptOwnershipComponent, 62 MyAcceptOwnershipComponent,
62 MyVideoImportsComponent, 63 MyVideoImportsComponent,
63 MySubscriptionsComponent, 64 MySubscriptionsComponent,
65 MyFollowersComponent,
64 MyHistoryComponent, 66 MyHistoryComponent,
65 67
66 MyVideoPlaylistCreateComponent, 68 MyVideoPlaylistCreateComponent,
diff --git a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss
deleted file mode 100644
index edca06a66..000000000
--- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss
+++ /dev/null
@@ -1,84 +0,0 @@
1@use '_variables' as *;
2@use '_mixins' as *;
3
4input[type=text] {
5 @include peertube-input-text(300px);
6}
7
8.video-channel {
9 @include row-blocks;
10
11 > my-actor-avatar {
12 @include actor-avatar-size(80px);
13
14 @include margin-right(10px);
15 }
16}
17
18.video-channel-info {
19 flex-grow: 1;
20
21 a.video-channel-names {
22 @include disable-default-a-behaviour;
23
24 width: fit-content;
25 display: flex;
26 align-items: baseline;
27 color: pvar(--mainForegroundColor);
28
29 .video-channel-display-name {
30 font-weight: $font-semibold;
31 font-size: 18px;
32 }
33
34 .video-channel-name {
35 @include margin-left(5px);
36
37 font-size: 14px;
38 color: $grey-actor-name;
39 }
40 }
41}
42
43.actor-owner {
44 @include disable-default-a-behaviour;
45
46 font-size: 13px;
47 color: pvar(--mainForegroundColor);
48
49 span:hover {
50 opacity: 0.8;
51 }
52
53 my-actor-avatar {
54 @include margin-left(7px);
55 display: inline-block;
56 vertical-align: top;
57 }
58}
59
60.video-subscriptions-header {
61 margin-bottom: 30px;
62 display: flex;
63}
64
65@media screen and (max-width: $small-view) {
66 .video-subscriptions-header input[type=text] {
67 width: 100% !important;
68 }
69
70 .video-channel-info {
71 padding-bottom: 10px;
72 text-align: center;
73
74 .video-channel-names {
75 flex-direction: column;
76 align-items: center !important;
77 margin: auto;
78 }
79 }
80
81 img {
82 @include margin-right(0);
83 }
84}