diff options
author | Chocobozzz <me@florianbigard.com> | 2021-10-19 09:44:43 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2021-10-20 09:25:44 +0200 |
commit | 4beda9e12adc7b1f3b178cecd6863ebf3cf431f1 (patch) | |
tree | 6244a10b286d66c6dcd7799aee630670d0493781 /client | |
parent | 9593a78ae1368a9ad8bb11044fce6fde2892701a (diff) | |
download | PeerTube-4beda9e12adc7b1f3b178cecd6863ebf3cf431f1.tar.gz PeerTube-4beda9e12adc7b1f3b178cecd6863ebf3cf431f1.tar.zst PeerTube-4beda9e12adc7b1f3b178cecd6863ebf3cf431f1.zip |
Add ability to view my followers
Diffstat (limited to 'client')
32 files changed, 400 insertions, 193 deletions
diff --git a/client/src/app/+about/about-follows/about-follows.component.ts b/client/src/app/+about/about-follows/about-follows.component.ts index a35272681..84b47e967 100644 --- a/client/src/app/+about/about-follows/about-follows.component.ts +++ b/client/src/app/+about/about-follows/about-follows.component.ts | |||
@@ -88,7 +88,7 @@ export class AboutFollowsComponent implements OnInit { | |||
88 | } | 88 | } |
89 | 89 | ||
90 | private loadMoreFollowers (reset = false) { | 90 | private loadMoreFollowers (reset = false) { |
91 | const pagination = this.restService.componentPaginationToRestPagination(this.followersPagination) | 91 | const pagination = this.restService.componentToRestPagination(this.followersPagination) |
92 | 92 | ||
93 | this.followService.getFollowers({ pagination, sort: this.sort, state: 'accepted' }) | 93 | this.followService.getFollowers({ pagination, sort: this.sort, state: 'accepted' }) |
94 | .subscribe({ | 94 | .subscribe({ |
@@ -106,7 +106,7 @@ export class AboutFollowsComponent implements OnInit { | |||
106 | } | 106 | } |
107 | 107 | ||
108 | private loadMoreFollowings (reset = false) { | 108 | private loadMoreFollowings (reset = false) { |
109 | const pagination = this.restService.componentPaginationToRestPagination(this.followingsPagination) | 109 | const pagination = this.restService.componentToRestPagination(this.followingsPagination) |
110 | 110 | ||
111 | this.followService.getFollowing({ pagination, sort: this.sort, state: 'accepted' }) | 111 | this.followService.getFollowing({ pagination, sort: this.sort, state: 'accepted' }) |
112 | .subscribe({ | 112 | .subscribe({ |
diff --git a/client/src/app/+accounts/accounts.component.scss b/client/src/app/+accounts/accounts.component.scss index c4e2159d1..cdd00487b 100644 --- a/client/src/app/+accounts/accounts.component.scss +++ b/client/src/app/+accounts/accounts.component.scss | |||
@@ -1,6 +1,6 @@ | |||
1 | @use '_variables' as *; | 1 | @use '_variables' as *; |
2 | @use '_mixins' as *; | 2 | @use '_mixins' as *; |
3 | @use '_actor' as *; | 3 | @use '_account-channel-page' as *; |
4 | @use '_miniature' as *; | 4 | @use '_miniature' as *; |
5 | 5 | ||
6 | .root { | 6 | .root { |
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html index 1f542e458..537e06d4d 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html +++ b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html | |||
@@ -416,7 +416,7 @@ | |||
416 | <p i18n>⚠️ This functionality requires a lot of attention and extra moderation.</p> | 416 | <p i18n>⚠️ This functionality requires a lot of attention and extra moderation.</p> |
417 | 417 | ||
418 | <span i18n> | 418 | <span i18n> |
419 | See <a href="https://docs.joinpeertube.org/admin-following-instances?id=automatically-follow-other-instances" rel="noopener noreferer" target="_blank">the documentation</a> for more information about the expected URL | 419 | See <a href="https://docs.joinpeertube.org/admin-following-instances?id=automatically-follow-other-instances" rel="noopener noreferrer" target="_blank">the documentation</a> for more information about the expected URL |
420 | </span> | 420 | </span> |
421 | </ng-container> | 421 | </ng-container> |
422 | 422 | ||
diff --git a/client/src/app/+admin/plugins/shared/plugin-api.service.ts b/client/src/app/+admin/plugins/shared/plugin-api.service.ts index c4f480cae..b95ee0c9d 100644 --- a/client/src/app/+admin/plugins/shared/plugin-api.service.ts +++ b/client/src/app/+admin/plugins/shared/plugin-api.service.ts | |||
@@ -51,7 +51,7 @@ export class PluginApiService { | |||
51 | componentPagination: ComponentPagination, | 51 | componentPagination: ComponentPagination, |
52 | sort: string | 52 | sort: string |
53 | ) { | 53 | ) { |
54 | const pagination = this.restService.componentPaginationToRestPagination(componentPagination) | 54 | const pagination = this.restService.componentToRestPagination(componentPagination) |
55 | 55 | ||
56 | let params = new HttpParams() | 56 | let params = new HttpParams() |
57 | params = this.restService.addRestGetParams(params, pagination, sort) | 57 | params = this.restService.addRestGetParams(params, pagination, sort) |
@@ -67,7 +67,7 @@ export class PluginApiService { | |||
67 | sort: string, | 67 | sort: string, |
68 | search?: string | 68 | search?: string |
69 | ) { | 69 | ) { |
70 | const pagination = this.restService.componentPaginationToRestPagination(componentPagination) | 70 | const pagination = this.restService.componentToRestPagination(componentPagination) |
71 | 71 | ||
72 | let params = new HttpParams() | 72 | let params = new HttpParams() |
73 | params = this.restService.addRestGetParams(params, pagination, sort) | 73 | params = this.restService.addRestGetParams(params, pagination, sort) |
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 | |||
10 | input[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 @@ | |||
1 | import { Subject } from 'rxjs' | ||
2 | import { Component, OnInit } from '@angular/core' | ||
3 | import { ActivatedRoute } from '@angular/router' | ||
4 | import { AuthService, ComponentPagination, Notifier } from '@app/core' | ||
5 | import { UserSubscriptionService } from '@app/shared/shared-user-subscription' | ||
6 | import { ActorFollow } from '@shared/models' | ||
7 | |||
8 | @Component({ | ||
9 | templateUrl: './my-followers.component.html', | ||
10 | styleUrls: [ './my-followers.component.scss' ] | ||
11 | }) | ||
12 | export 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 | |||
10 | input[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 @@ | |||
1 | import { NgModule } from '@angular/core' | 1 | import { NgModule } from '@angular/core' |
2 | import { RouterModule, Routes } from '@angular/router' | 2 | import { RouterModule, Routes } from '@angular/router' |
3 | import { LoginGuard } from '../core' | 3 | import { LoginGuard } from '../core' |
4 | import { MyFollowersComponent } from './my-follows/my-followers.component' | ||
5 | import { MySubscriptionsComponent } from './my-follows/my-subscriptions.component' | ||
4 | import { MyHistoryComponent } from './my-history/my-history.component' | 6 | import { MyHistoryComponent } from './my-history/my-history.component' |
5 | import { MyLibraryComponent } from './my-library.component' | 7 | import { MyLibraryComponent } from './my-library.component' |
6 | import { MyOwnershipComponent } from './my-ownership/my-ownership.component' | 8 | import { MyOwnershipComponent } from './my-ownership/my-ownership.component' |
7 | import { MySubscriptionsComponent } from './my-subscriptions/my-subscriptions.component' | ||
8 | import { MyVideoImportsComponent } from './my-video-imports/my-video-imports.component' | 9 | import { MyVideoImportsComponent } from './my-video-imports/my-video-imports.component' |
9 | import { MyVideoPlaylistCreateComponent } from './my-video-playlists/my-video-playlist-create.component' | 10 | import { MyVideoPlaylistCreateComponent } from './my-video-playlists/my-video-playlist-create.component' |
10 | import { MyVideoPlaylistElementsComponent } from './my-video-playlists/my-video-playlist-elements.component' | 11 | import { 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 | |||
13 | import { SharedVideoLiveModule } from '@app/shared/shared-video-live' | 13 | import { SharedVideoLiveModule } from '@app/shared/shared-video-live' |
14 | import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature' | 14 | import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature' |
15 | import { SharedVideoPlaylistModule } from '@app/shared/shared-video-playlist/shared-video-playlist.module' | 15 | import { SharedVideoPlaylistModule } from '@app/shared/shared-video-playlist/shared-video-playlist.module' |
16 | import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module' | ||
17 | import { MySubscriptionsComponent } from './my-follows/my-subscriptions.component' | ||
16 | import { MyHistoryComponent } from './my-history/my-history.component' | 18 | import { MyHistoryComponent } from './my-history/my-history.component' |
17 | import { MyLibraryRoutingModule } from './my-library-routing.module' | 19 | import { MyLibraryRoutingModule } from './my-library-routing.module' |
18 | import { MyLibraryComponent } from './my-library.component' | 20 | import { MyLibraryComponent } from './my-library.component' |
19 | import { MyAcceptOwnershipComponent } from './my-ownership/my-accept-ownership/my-accept-ownership.component' | 21 | import { MyAcceptOwnershipComponent } from './my-ownership/my-accept-ownership/my-accept-ownership.component' |
20 | import { MyOwnershipComponent } from './my-ownership/my-ownership.component' | 22 | import { MyOwnershipComponent } from './my-ownership/my-ownership.component' |
21 | import { MySubscriptionsComponent } from './my-subscriptions/my-subscriptions.component' | ||
22 | import { MyVideoImportsComponent } from './my-video-imports/my-video-imports.component' | 23 | import { MyVideoImportsComponent } from './my-video-imports/my-video-imports.component' |
23 | import { MyVideoPlaylistCreateComponent } from './my-video-playlists/my-video-playlist-create.component' | 24 | import { MyVideoPlaylistCreateComponent } from './my-video-playlists/my-video-playlist-create.component' |
24 | import { MyVideoPlaylistElementsComponent } from './my-video-playlists/my-video-playlist-elements.component' | 25 | import { MyVideoPlaylistElementsComponent } from './my-video-playlists/my-video-playlist-elements.component' |
@@ -26,7 +27,7 @@ import { MyVideoPlaylistUpdateComponent } from './my-video-playlists/my-video-pl | |||
26 | import { MyVideoPlaylistsComponent } from './my-video-playlists/my-video-playlists.component' | 27 | import { MyVideoPlaylistsComponent } from './my-video-playlists/my-video-playlists.component' |
27 | import { VideoChangeOwnershipComponent } from './my-videos/modals/video-change-ownership.component' | 28 | import { VideoChangeOwnershipComponent } from './my-videos/modals/video-change-ownership.component' |
28 | import { MyVideosComponent } from './my-videos/my-videos.component' | 29 | import { MyVideosComponent } from './my-videos/my-videos.component' |
29 | import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module' | 30 | import { 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 | |||
4 | input[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 | } | ||
diff --git a/client/src/app/+video-channels/video-channels.component.scss b/client/src/app/+video-channels/video-channels.component.scss index d174dcd62..72ee2d7bb 100644 --- a/client/src/app/+video-channels/video-channels.component.scss +++ b/client/src/app/+video-channels/video-channels.component.scss | |||
@@ -1,6 +1,6 @@ | |||
1 | @use '_variables' as *; | 1 | @use '_variables' as *; |
2 | @use '_mixins' as *; | 2 | @use '_mixins' as *; |
3 | @use '_actor' as *; | 3 | @use '_account-channel-page' as *; |
4 | @use '_miniature' as *; | 4 | @use '_miniature' as *; |
5 | 5 | ||
6 | .root { | 6 | .root { |
diff --git a/client/src/app/core/rest/rest.service.ts b/client/src/app/core/rest/rest.service.ts index 98e45ffc0..93b5f56b2 100644 --- a/client/src/app/core/rest/rest.service.ts +++ b/client/src/app/core/rest/rest.service.ts | |||
@@ -13,9 +13,8 @@ interface QueryStringFilterPrefixes { | |||
13 | } | 13 | } |
14 | } | 14 | } |
15 | 15 | ||
16 | type ParseQueryStringFilterResult = { | 16 | type ParseQueryStringFilters <K extends keyof any> = Partial<Record<K, string | number | boolean | (string | number | boolean)[]>> |
17 | [key: string]: string | number | boolean | (string | number | boolean)[] | 17 | type ParseQueryStringFiltersResult <K extends keyof any> = ParseQueryStringFilters<K> & { search?: string } |
18 | } | ||
19 | 18 | ||
20 | @Injectable() | 19 | @Injectable() |
21 | export class RestService { | 20 | export class RestService { |
@@ -67,14 +66,17 @@ export class RestService { | |||
67 | return params | 66 | return params |
68 | } | 67 | } |
69 | 68 | ||
70 | componentPaginationToRestPagination (componentPagination: ComponentPaginationLight): RestPagination { | 69 | componentToRestPagination (componentPagination: ComponentPaginationLight): RestPagination { |
71 | const start: number = (componentPagination.currentPage - 1) * componentPagination.itemsPerPage | 70 | const start: number = (componentPagination.currentPage - 1) * componentPagination.itemsPerPage |
72 | const count: number = componentPagination.itemsPerPage | 71 | const count: number = componentPagination.itemsPerPage |
73 | 72 | ||
74 | return { start, count } | 73 | return { start, count } |
75 | } | 74 | } |
76 | 75 | ||
77 | parseQueryStringFilter (q: string, prefixes: QueryStringFilterPrefixes): ParseQueryStringFilterResult { | 76 | /* |
77 | * Returns an object containing the filters and the remaining search | ||
78 | */ | ||
79 | parseQueryStringFilter <T extends QueryStringFilterPrefixes> (q: string, prefixes: T): ParseQueryStringFiltersResult<keyof T> { | ||
78 | if (!q) return {} | 80 | if (!q) return {} |
79 | 81 | ||
80 | // Tokenize the strings using spaces that are not in quotes | 82 | // Tokenize the strings using spaces that are not in quotes |
@@ -90,9 +92,9 @@ export class RestService { | |||
90 | return prefixeStrings.every(prefixString => t.startsWith(prefixString) === false) | 92 | return prefixeStrings.every(prefixString => t.startsWith(prefixString) === false) |
91 | }) | 93 | }) |
92 | 94 | ||
93 | const additionalFilters: ParseQueryStringFilterResult = {} | 95 | const additionalFilters: ParseQueryStringFilters<keyof T> = {} |
94 | 96 | ||
95 | for (const prefixKey of Object.keys(prefixes)) { | 97 | for (const prefixKey of Object.keys(prefixes) as (keyof T)[]) { |
96 | const prefixObj = prefixes[prefixKey] | 98 | const prefixObj = prefixes[prefixKey] |
97 | const prefix = prefixObj.prefix | 99 | const prefix = prefixObj.prefix |
98 | 100 | ||
diff --git a/client/src/app/shared/shared-custom-markup/custom-markup-help.component.html b/client/src/app/shared/shared-custom-markup/custom-markup-help.component.html index dd7a56d7d..0ca84ff78 100644 --- a/client/src/app/shared/shared-custom-markup/custom-markup-help.component.html +++ b/client/src/app/shared/shared-custom-markup/custom-markup-help.component.html | |||
@@ -1,3 +1,3 @@ | |||
1 | <ng-container i18n> | 1 | <ng-container i18n> |
2 | <a href="https://en.wikipedia.org/wiki/Markdown#Example" target="_blank" rel="noreferer noopener">Markdown compatible</a> that also supports <a href="https://docs.joinpeertube.org/api-custom-client-markup" target="_blank" rel="noreferer noopener">custom PeerTube HTML tags</a> | 2 | <a href="https://en.wikipedia.org/wiki/Markdown#Example" target="_blank" rel="noreferrer noopener">Markdown compatible</a> that also supports <a href="https://docs.joinpeertube.org/api-custom-client-markup" target="_blank" rel="noreferrer noopener">custom PeerTube HTML tags</a> |
3 | </ng-container> | 3 | </ng-container> |
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.ts b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts index 72cd6d460..113219f48 100644 --- a/client/src/app/shared/shared-forms/advanced-input-filter.component.ts +++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts | |||
@@ -77,6 +77,8 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit { | |||
77 | 77 | ||
78 | logger('On route search change "%s".', search) | 78 | logger('On route search change "%s".', search) |
79 | 79 | ||
80 | if (this.searchValue === search) return | ||
81 | |||
80 | this.searchValue = search | 82 | this.searchValue = search |
81 | this.emitSearch() | 83 | this.emitSearch() |
82 | }) | 84 | }) |
diff --git a/client/src/app/shared/shared-main/users/user-history.service.ts b/client/src/app/shared/shared-main/users/user-history.service.ts index 91268af8c..a4841897d 100644 --- a/client/src/app/shared/shared-main/users/user-history.service.ts +++ b/client/src/app/shared/shared-main/users/user-history.service.ts | |||
@@ -19,7 +19,7 @@ export class UserHistoryService { | |||
19 | ) {} | 19 | ) {} |
20 | 20 | ||
21 | getUserVideosHistory (historyPagination: ComponentPaginationLight, search?: string) { | 21 | getUserVideosHistory (historyPagination: ComponentPaginationLight, search?: string) { |
22 | const pagination = this.restService.componentPaginationToRestPagination(historyPagination) | 22 | const pagination = this.restService.componentToRestPagination(historyPagination) |
23 | 23 | ||
24 | let params = new HttpParams() | 24 | let params = new HttpParams() |
25 | params = this.restService.addRestGetParams(params, pagination) | 25 | params = this.restService.addRestGetParams(params, pagination) |
diff --git a/client/src/app/shared/shared-main/users/user-notification.service.ts b/client/src/app/shared/shared-main/users/user-notification.service.ts index 09fee87a3..e27dab21a 100644 --- a/client/src/app/shared/shared-main/users/user-notification.service.ts +++ b/client/src/app/shared/shared-main/users/user-notification.service.ts | |||
@@ -29,7 +29,7 @@ export class UserNotificationService { | |||
29 | const { pagination, ignoreLoadingBar, unread, sort } = parameters | 29 | const { pagination, ignoreLoadingBar, unread, sort } = parameters |
30 | 30 | ||
31 | let params = new HttpParams() | 31 | let params = new HttpParams() |
32 | params = this.restService.addRestGetParams(params, this.restService.componentPaginationToRestPagination(pagination), sort) | 32 | params = this.restService.addRestGetParams(params, this.restService.componentToRestPagination(pagination), sort) |
33 | 33 | ||
34 | if (unread) params = params.append('unread', `${unread}`) | 34 | if (unread) params = params.append('unread', `${unread}`) |
35 | 35 | ||
diff --git a/client/src/app/shared/shared-main/users/user-notifications.component.html b/client/src/app/shared/shared-main/users/user-notifications.component.html index ee8df864a..9af6da784 100644 --- a/client/src/app/shared/shared-main/users/user-notifications.component.html +++ b/client/src/app/shared/shared-main/users/user-notifications.component.html | |||
@@ -203,7 +203,7 @@ | |||
203 | <my-global-icon iconName="cog" aria-hidden="true"></my-global-icon> | 203 | <my-global-icon iconName="cog" aria-hidden="true"></my-global-icon> |
204 | 204 | ||
205 | <div class="message" i18n> | 205 | <div class="message" i18n> |
206 | <a (click)="markAsRead(notification)" [href]="notification.peertubeVersionLink" target="_blank" rel="noopener noreferer">A new version of PeerTube</a> is available: {{ notification.peertube.latestVersion }} | 206 | <a (click)="markAsRead(notification)" [href]="notification.peertubeVersionLink" target="_blank" rel="noopener noreferrer">A new version of PeerTube</a> is available: {{ notification.peertube.latestVersion }} |
207 | </div> | 207 | </div> |
208 | </ng-container> | 208 | </ng-container> |
209 | 209 | ||
diff --git a/client/src/app/shared/shared-main/video-channel/video-channel.service.ts b/client/src/app/shared/shared-main/video-channel/video-channel.service.ts index 7560a35a8..dc00fabdc 100644 --- a/client/src/app/shared/shared-main/video-channel/video-channel.service.ts +++ b/client/src/app/shared/shared-main/video-channel/video-channel.service.ts | |||
@@ -50,7 +50,7 @@ export class VideoChannelService { | |||
50 | const { account, componentPagination, withStats = false, sort, search } = options | 50 | const { account, componentPagination, withStats = false, sort, search } = options |
51 | 51 | ||
52 | const pagination = componentPagination | 52 | const pagination = componentPagination |
53 | ? this.restService.componentPaginationToRestPagination(componentPagination) | 53 | ? this.restService.componentToRestPagination(componentPagination) |
54 | : { start: 0, count: 20 } | 54 | : { start: 0, count: 20 } |
55 | 55 | ||
56 | let params = new HttpParams() | 56 | let params = new HttpParams() |
diff --git a/client/src/app/shared/shared-main/video/video.service.ts b/client/src/app/shared/shared-main/video/video.service.ts index 3481b116f..2f43f1b9d 100644 --- a/client/src/app/shared/shared-main/video/video.service.ts +++ b/client/src/app/shared/shared-main/video/video.service.ts | |||
@@ -123,7 +123,7 @@ export class VideoService { | |||
123 | } | 123 | } |
124 | 124 | ||
125 | getMyVideos (videoPagination: ComponentPaginationLight, sort: VideoSortField, search?: string): Observable<ResultList<Video>> { | 125 | getMyVideos (videoPagination: ComponentPaginationLight, sort: VideoSortField, search?: string): Observable<ResultList<Video>> { |
126 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) | 126 | const pagination = this.restService.componentToRestPagination(videoPagination) |
127 | 127 | ||
128 | let params = new HttpParams() | 128 | let params = new HttpParams() |
129 | params = this.restService.addRestGetParams(params, pagination, sort) | 129 | params = this.restService.addRestGetParams(params, pagination, sort) |
@@ -377,7 +377,7 @@ export class VideoService { | |||
377 | private buildCommonVideosParams (options: CommonVideoParams & { params: HttpParams }) { | 377 | private buildCommonVideosParams (options: CommonVideoParams & { params: HttpParams }) { |
378 | const { params, videoPagination, sort, filter, categoryOneOf, languageOneOf, skipCount, nsfwPolicy, isLive, nsfw } = options | 378 | const { params, videoPagination, sort, filter, categoryOneOf, languageOneOf, skipCount, nsfwPolicy, isLive, nsfw } = options |
379 | 379 | ||
380 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) | 380 | const pagination = this.restService.componentToRestPagination(videoPagination) |
381 | let newParams = this.restService.addRestGetParams(params, pagination, sort) | 381 | let newParams = this.restService.addRestGetParams(params, pagination, sort) |
382 | 382 | ||
383 | if (filter) newParams = newParams.set('filter', filter) | 383 | if (filter) newParams = newParams.set('filter', filter) |
diff --git a/client/src/app/shared/shared-search/search.service.ts b/client/src/app/shared/shared-search/search.service.ts index fdfab0e0e..71350c733 100644 --- a/client/src/app/shared/shared-search/search.service.ts +++ b/client/src/app/shared/shared-search/search.service.ts | |||
@@ -43,7 +43,7 @@ export class SearchService { | |||
43 | let pagination: RestPagination | 43 | let pagination: RestPagination |
44 | 44 | ||
45 | if (componentPagination) { | 45 | if (componentPagination) { |
46 | pagination = this.restService.componentPaginationToRestPagination(componentPagination) | 46 | pagination = this.restService.componentToRestPagination(componentPagination) |
47 | } | 47 | } |
48 | 48 | ||
49 | let params = new HttpParams() | 49 | let params = new HttpParams() |
@@ -77,7 +77,7 @@ export class SearchService { | |||
77 | 77 | ||
78 | let pagination: RestPagination | 78 | let pagination: RestPagination |
79 | if (componentPagination) { | 79 | if (componentPagination) { |
80 | pagination = this.restService.componentPaginationToRestPagination(componentPagination) | 80 | pagination = this.restService.componentToRestPagination(componentPagination) |
81 | } | 81 | } |
82 | 82 | ||
83 | let params = new HttpParams() | 83 | let params = new HttpParams() |
@@ -111,7 +111,7 @@ export class SearchService { | |||
111 | 111 | ||
112 | let pagination: RestPagination | 112 | let pagination: RestPagination |
113 | if (componentPagination) { | 113 | if (componentPagination) { |
114 | pagination = this.restService.componentPaginationToRestPagination(componentPagination) | 114 | pagination = this.restService.componentToRestPagination(componentPagination) |
115 | } | 115 | } |
116 | 116 | ||
117 | let params = new HttpParams() | 117 | let params = new HttpParams() |
diff --git a/client/src/app/shared/shared-user-subscription/user-subscription.service.ts b/client/src/app/shared/shared-user-subscription/user-subscription.service.ts index f289fb6cf..ede65ff39 100644 --- a/client/src/app/shared/shared-user-subscription/user-subscription.service.ts +++ b/client/src/app/shared/shared-user-subscription/user-subscription.service.ts | |||
@@ -6,7 +6,7 @@ import { Injectable } from '@angular/core' | |||
6 | import { ComponentPaginationLight, RestExtractor, RestService } from '@app/core' | 6 | import { ComponentPaginationLight, RestExtractor, RestService } from '@app/core' |
7 | import { buildBulkObservable } from '@app/helpers' | 7 | import { buildBulkObservable } from '@app/helpers' |
8 | import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' | 8 | import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' |
9 | import { ResultList, VideoChannel as VideoChannelServer, VideoSortField } from '@shared/models' | 9 | import { ActorFollow, ResultList, VideoChannel as VideoChannelServer, VideoSortField } from '@shared/models' |
10 | import { environment } from '../../../environments/environment' | 10 | import { environment } from '../../../environments/environment' |
11 | 11 | ||
12 | const logger = debug('peertube:subscriptions:UserSubscriptionService') | 12 | const logger = debug('peertube:subscriptions:UserSubscriptionService') |
@@ -17,6 +17,8 @@ type SubscriptionExistResultObservable = { [ uri: string ]: Observable<boolean> | |||
17 | @Injectable() | 17 | @Injectable() |
18 | export class UserSubscriptionService { | 18 | export class UserSubscriptionService { |
19 | static BASE_USER_SUBSCRIPTIONS_URL = environment.apiUrl + '/api/v1/users/me/subscriptions' | 19 | static BASE_USER_SUBSCRIPTIONS_URL = environment.apiUrl + '/api/v1/users/me/subscriptions' |
20 | static BASE_VIDEO_CHANNELS_URL = environment.apiUrl + '/api/v1/video-channels' | ||
21 | static BASE_ACCOUNTS_URL = environment.apiUrl + '/api/v1/accounts' | ||
20 | 22 | ||
21 | // Use a replay subject because we "next" a value before subscribing | 23 | // Use a replay subject because we "next" a value before subscribing |
22 | private existsSubject = new ReplaySubject<string>(1) | 24 | private existsSubject = new ReplaySubject<string>(1) |
@@ -43,13 +45,46 @@ export class UserSubscriptionService { | |||
43 | ) | 45 | ) |
44 | } | 46 | } |
45 | 47 | ||
48 | listFollowers (parameters: { | ||
49 | pagination: ComponentPaginationLight | ||
50 | nameWithHost: string | ||
51 | search?: string | ||
52 | }) { | ||
53 | const { pagination, nameWithHost, search } = parameters | ||
54 | |||
55 | let url = `${UserSubscriptionService.BASE_ACCOUNTS_URL}/${nameWithHost}/followers` | ||
56 | |||
57 | let params = new HttpParams() | ||
58 | params = this.restService.addRestGetParams(params, this.restService.componentToRestPagination(pagination), '-createdAt') | ||
59 | |||
60 | if (search) { | ||
61 | const filters = this.restService.parseQueryStringFilter(search, { | ||
62 | channel: { | ||
63 | prefix: 'channel:' | ||
64 | } | ||
65 | }) | ||
66 | |||
67 | if (filters.channel) { | ||
68 | url = `${UserSubscriptionService.BASE_VIDEO_CHANNELS_URL}/${filters.channel}/followers` | ||
69 | } | ||
70 | |||
71 | params = this.restService.addObjectParams(params, { search: filters.search }) | ||
72 | } | ||
73 | |||
74 | return this.authHttp | ||
75 | .get<ResultList<ActorFollow>>(url, { params }) | ||
76 | .pipe( | ||
77 | catchError(err => this.restExtractor.handleError(err)) | ||
78 | ) | ||
79 | } | ||
80 | |||
46 | getUserSubscriptionVideos (parameters: { | 81 | getUserSubscriptionVideos (parameters: { |
47 | videoPagination: ComponentPaginationLight | 82 | videoPagination: ComponentPaginationLight |
48 | sort: VideoSortField | 83 | sort: VideoSortField |
49 | skipCount?: boolean | 84 | skipCount?: boolean |
50 | }): Observable<ResultList<Video>> { | 85 | }): Observable<ResultList<Video>> { |
51 | const { videoPagination, sort, skipCount } = parameters | 86 | const { videoPagination, sort, skipCount } = parameters |
52 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) | 87 | const pagination = this.restService.componentToRestPagination(videoPagination) |
53 | 88 | ||
54 | let params = new HttpParams() | 89 | let params = new HttpParams() |
55 | params = this.restService.addRestGetParams(params, pagination, sort) | 90 | params = this.restService.addRestGetParams(params, pagination, sort) |
@@ -106,7 +141,7 @@ export class UserSubscriptionService { | |||
106 | const { pagination, search } = parameters | 141 | const { pagination, search } = parameters |
107 | const url = UserSubscriptionService.BASE_USER_SUBSCRIPTIONS_URL | 142 | const url = UserSubscriptionService.BASE_USER_SUBSCRIPTIONS_URL |
108 | 143 | ||
109 | const restPagination = this.restService.componentPaginationToRestPagination(pagination) | 144 | const restPagination = this.restService.componentToRestPagination(pagination) |
110 | 145 | ||
111 | let params = new HttpParams() | 146 | let params = new HttpParams() |
112 | params = this.restService.addRestGetParams(params, restPagination) | 147 | params = this.restService.addRestGetParams(params, restPagination) |
diff --git a/client/src/app/shared/shared-video-comment/video-comment.service.ts b/client/src/app/shared/shared-video-comment/video-comment.service.ts index 5550c96e4..fd1cae7f8 100644 --- a/client/src/app/shared/shared-video-comment/video-comment.service.ts +++ b/client/src/app/shared/shared-video-comment/video-comment.service.ts | |||
@@ -81,7 +81,7 @@ export class VideoCommentService { | |||
81 | }): Observable<ThreadsResultList<VideoComment>> { | 81 | }): Observable<ThreadsResultList<VideoComment>> { |
82 | const { videoId, componentPagination, sort } = parameters | 82 | const { videoId, componentPagination, sort } = parameters |
83 | 83 | ||
84 | const pagination = this.restService.componentPaginationToRestPagination(componentPagination) | 84 | const pagination = this.restService.componentToRestPagination(componentPagination) |
85 | 85 | ||
86 | let params = new HttpParams() | 86 | let params = new HttpParams() |
87 | params = this.restService.addRestGetParams(params, pagination, sort) | 87 | params = this.restService.addRestGetParams(params, pagination, sort) |
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist.service.ts b/client/src/app/shared/shared-video-playlist/video-playlist.service.ts index fc291329a..3faf81d11 100644 --- a/client/src/app/shared/shared-video-playlist/video-playlist.service.ts +++ b/client/src/app/shared/shared-video-playlist/video-playlist.service.ts | |||
@@ -62,7 +62,7 @@ export class VideoPlaylistService { | |||
62 | 62 | ||
63 | listChannelPlaylists (videoChannel: VideoChannel, componentPagination: ComponentPaginationLight): Observable<ResultList<VideoPlaylist>> { | 63 | listChannelPlaylists (videoChannel: VideoChannel, componentPagination: ComponentPaginationLight): Observable<ResultList<VideoPlaylist>> { |
64 | const url = VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.nameWithHost + '/video-playlists' | 64 | const url = VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.nameWithHost + '/video-playlists' |
65 | const pagination = this.restService.componentPaginationToRestPagination(componentPagination) | 65 | const pagination = this.restService.componentToRestPagination(componentPagination) |
66 | 66 | ||
67 | let params = new HttpParams() | 67 | let params = new HttpParams() |
68 | params = this.restService.addRestGetParams(params, pagination) | 68 | params = this.restService.addRestGetParams(params, pagination) |
@@ -103,7 +103,7 @@ export class VideoPlaylistService { | |||
103 | ): Observable<ResultList<VideoPlaylist>> { | 103 | ): Observable<ResultList<VideoPlaylist>> { |
104 | const url = AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/video-playlists' | 104 | const url = AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/video-playlists' |
105 | const pagination = componentPagination | 105 | const pagination = componentPagination |
106 | ? this.restService.componentPaginationToRestPagination(componentPagination) | 106 | ? this.restService.componentToRestPagination(componentPagination) |
107 | : undefined | 107 | : undefined |
108 | 108 | ||
109 | let params = new HttpParams() | 109 | let params = new HttpParams() |
@@ -259,7 +259,7 @@ export class VideoPlaylistService { | |||
259 | componentPagination: ComponentPaginationLight | 259 | componentPagination: ComponentPaginationLight |
260 | }): Observable<ResultList<VideoPlaylistElement>> { | 260 | }): Observable<ResultList<VideoPlaylistElement>> { |
261 | const path = VideoPlaylistService.BASE_VIDEO_PLAYLIST_URL + options.videoPlaylistId + '/videos' | 261 | const path = VideoPlaylistService.BASE_VIDEO_PLAYLIST_URL + options.videoPlaylistId + '/videos' |
262 | const pagination = this.restService.componentPaginationToRestPagination(options.componentPagination) | 262 | const pagination = this.restService.componentToRestPagination(options.componentPagination) |
263 | 263 | ||
264 | let params = new HttpParams() | 264 | let params = new HttpParams() |
265 | params = this.restService.addRestGetParams(params, pagination) | 265 | params = this.restService.addRestGetParams(params, pagination) |
diff --git a/client/src/sass/include/_account-channel-page.scss b/client/src/sass/include/_account-channel-page.scss new file mode 100644 index 000000000..b135bbb6d --- /dev/null +++ b/client/src/sass/include/_account-channel-page.scss | |||
@@ -0,0 +1,88 @@ | |||
1 | @use '_variables' as *; | ||
2 | @use '_mixins' as *; | ||
3 | |||
4 | @mixin section-label-responsive { | ||
5 | color: pvar(--mainColor); | ||
6 | font-size: 12px; | ||
7 | margin-bottom: 15px; | ||
8 | font-weight: $font-bold; | ||
9 | letter-spacing: 2.5px; | ||
10 | |||
11 | @media screen and (max-width: $mobile-view) { | ||
12 | font-size: 10px; | ||
13 | letter-spacing: 2.1px; | ||
14 | margin-bottom: 5px; | ||
15 | } | ||
16 | } | ||
17 | |||
18 | @mixin show-more-description { | ||
19 | color: pvar(--mainColor); | ||
20 | cursor: pointer; | ||
21 | margin: 10px auto 45px; | ||
22 | } | ||
23 | |||
24 | @mixin avatar-row-responsive ($img-margin, $grey-font-size) { | ||
25 | display: flex; | ||
26 | grid-column: 1; | ||
27 | margin-bottom: 30px; | ||
28 | |||
29 | .main-avatar { | ||
30 | @include actor-avatar-size(120px); | ||
31 | } | ||
32 | |||
33 | > div { | ||
34 | @include margin-left($img-margin); | ||
35 | |||
36 | min-width: 1px; | ||
37 | } | ||
38 | |||
39 | .actor-info { | ||
40 | display: flex; | ||
41 | |||
42 | > div:first-child { | ||
43 | flex-grow: 1; | ||
44 | min-width: 1px; | ||
45 | } | ||
46 | } | ||
47 | |||
48 | .actor-display-name { | ||
49 | @include peertube-word-wrap; | ||
50 | |||
51 | display: flex; | ||
52 | flex-wrap: wrap; | ||
53 | } | ||
54 | |||
55 | h1 { | ||
56 | font-size: 28px; | ||
57 | font-weight: $font-bold; | ||
58 | margin: 0; | ||
59 | } | ||
60 | |||
61 | .actor-handle { | ||
62 | @include ellipsis; | ||
63 | } | ||
64 | |||
65 | .actor-handle, | ||
66 | .actor-counters { | ||
67 | color: pvar(--greyForegroundColor); | ||
68 | font-size: $grey-font-size; | ||
69 | } | ||
70 | |||
71 | .actor-counters > *:not(:last-child)::after { | ||
72 | content: '•'; | ||
73 | margin: 0 10px; | ||
74 | color: pvar(--mainColor); | ||
75 | } | ||
76 | |||
77 | @media screen and (max-width: $mobile-view) { | ||
78 | margin-bottom: 15px; | ||
79 | |||
80 | h1 { | ||
81 | font-size: 22px; | ||
82 | } | ||
83 | |||
84 | .main-avatar { | ||
85 | @include actor-avatar-size(80px); | ||
86 | } | ||
87 | } | ||
88 | } | ||
diff --git a/client/src/sass/include/_actor.scss b/client/src/sass/include/_actor.scss index b135bbb6d..f9e44b8ad 100644 --- a/client/src/sass/include/_actor.scss +++ b/client/src/sass/include/_actor.scss | |||
@@ -1,88 +1,68 @@ | |||
1 | @use '_variables' as *; | 1 | @use '_variables' as *; |
2 | @use '_mixins' as *; | 2 | @use '_mixins' as *; |
3 | 3 | ||
4 | @mixin section-label-responsive { | 4 | @mixin actor-row ($avatar-size: 80px, $avatar-margin-right: 10px, $min-height: 130px, $separator: true) { |
5 | color: pvar(--mainColor); | 5 | @include row-blocks($min-height: $min-height, $separator: $separator); |
6 | font-size: 12px; | ||
7 | margin-bottom: 15px; | ||
8 | font-weight: $font-bold; | ||
9 | letter-spacing: 2.5px; | ||
10 | |||
11 | @media screen and (max-width: $mobile-view) { | ||
12 | font-size: 10px; | ||
13 | letter-spacing: 2.1px; | ||
14 | margin-bottom: 5px; | ||
15 | } | ||
16 | } | ||
17 | |||
18 | @mixin show-more-description { | ||
19 | color: pvar(--mainColor); | ||
20 | cursor: pointer; | ||
21 | margin: 10px auto 45px; | ||
22 | } | ||
23 | |||
24 | @mixin avatar-row-responsive ($img-margin, $grey-font-size) { | ||
25 | display: flex; | ||
26 | grid-column: 1; | ||
27 | margin-bottom: 30px; | ||
28 | 6 | ||
29 | .main-avatar { | 7 | > my-actor-avatar { |
30 | @include actor-avatar-size(120px); | 8 | @include actor-avatar-size($avatar-size); |
31 | } | ||
32 | |||
33 | > div { | ||
34 | @include margin-left($img-margin); | ||
35 | 9 | ||
36 | min-width: 1px; | 10 | @include margin-right($avatar-margin-right); |
37 | } | 11 | } |
38 | 12 | ||
39 | .actor-info { | 13 | .actor-info { |
40 | display: flex; | 14 | flex-grow: 1; |
41 | |||
42 | > div:first-child { | ||
43 | flex-grow: 1; | ||
44 | min-width: 1px; | ||
45 | } | ||
46 | } | 15 | } |
47 | 16 | ||
48 | .actor-display-name { | 17 | .actor-names { |
49 | @include peertube-word-wrap; | 18 | @include disable-default-a-behaviour; |
50 | 19 | ||
20 | width: fit-content; | ||
51 | display: flex; | 21 | display: flex; |
52 | flex-wrap: wrap; | 22 | align-items: baseline; |
23 | color: pvar(--mainForegroundColor); | ||
53 | } | 24 | } |
54 | 25 | ||
55 | h1 { | 26 | .actor-display-name { |
56 | font-size: 28px; | 27 | font-weight: $font-semibold; |
57 | font-weight: $font-bold; | 28 | font-size: 18px; |
58 | margin: 0; | ||
59 | } | 29 | } |
60 | 30 | ||
61 | .actor-handle { | 31 | .actor-name { |
62 | @include ellipsis; | 32 | @include margin-left(5px); |
63 | } | ||
64 | 33 | ||
65 | .actor-handle, | 34 | font-size: 14px; |
66 | .actor-counters { | 35 | color: $grey-actor-name; |
67 | color: pvar(--greyForegroundColor); | ||
68 | font-size: $grey-font-size; | ||
69 | } | 36 | } |
70 | 37 | ||
71 | .actor-counters > *:not(:last-child)::after { | 38 | .actor-owner { |
72 | content: '•'; | 39 | @include disable-default-a-behaviour; |
73 | margin: 0 10px; | ||
74 | color: pvar(--mainColor); | ||
75 | } | ||
76 | 40 | ||
77 | @media screen and (max-width: $mobile-view) { | 41 | font-size: 13px; |
78 | margin-bottom: 15px; | 42 | color: pvar(--mainForegroundColor); |
79 | 43 | ||
80 | h1 { | 44 | span:hover { |
81 | font-size: 22px; | 45 | opacity: 0.8; |
82 | } | 46 | } |
83 | 47 | ||
84 | .main-avatar { | 48 | my-actor-avatar { |
85 | @include actor-avatar-size(80px); | 49 | @include margin-left(7px); |
50 | |||
51 | display: inline-block; | ||
52 | vertical-align: top; | ||
53 | } | ||
54 | } | ||
55 | |||
56 | @media screen and (max-width: $small-view) { | ||
57 | .actor-info { | ||
58 | padding-bottom: 10px; | ||
59 | text-align: center; | ||
60 | |||
61 | .actor-names { | ||
62 | flex-direction: column; | ||
63 | align-items: center !important; | ||
64 | margin: auto; | ||
65 | } | ||
86 | } | 66 | } |
87 | } | 67 | } |
88 | } | 68 | } |
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss index 9e510771e..2f436d787 100644 --- a/client/src/sass/include/_mixins.scss +++ b/client/src/sass/include/_mixins.scss | |||
@@ -653,12 +653,15 @@ | |||
653 | @include button-with-icon(20px, 5px, -1px); | 653 | @include button-with-icon(20px, 5px, -1px); |
654 | } | 654 | } |
655 | 655 | ||
656 | @mixin row-blocks ($column-responsive: true) { | 656 | @mixin row-blocks ($column-responsive: true, $min-height: 130px, $separator: true) { |
657 | display: flex; | 657 | display: flex; |
658 | min-height: 130px; | 658 | min-height: $min-height; |
659 | padding-bottom: 20px; | 659 | padding-bottom: 20px; |
660 | margin-bottom: 20px; | 660 | margin-bottom: 20px; |
661 | border-bottom: 1px solid #C6C6C6; | 661 | |
662 | @if $separator { | ||
663 | border-bottom: 1px solid #C6C6C6; | ||
664 | } | ||
662 | 665 | ||
663 | @media screen and (max-width: $small-view) { | 666 | @media screen and (max-width: $small-view) { |
664 | @if $column-responsive { | 667 | @if $column-responsive { |