diff options
Diffstat (limited to 'client/src/app/+my-library')
25 files changed, 223 insertions, 275 deletions
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss index 22de103d1..667726c22 100644 --- a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss +++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss | |||
@@ -66,7 +66,8 @@ textarea { | |||
66 | width: auto !important; | 66 | width: auto !important; |
67 | } | 67 | } |
68 | 68 | ||
69 | label[for=name] + div, textarea { | 69 | label[for=name] + div, |
70 | textarea { | ||
70 | width: 100%; | 71 | width: 100%; |
71 | } | 72 | } |
72 | } | 73 | } |
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 b704a1cc6..e41cbe921 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 | |||
@@ -1,18 +1,11 @@ | |||
1 | <h1> | 1 | <h1> |
2 | <span> | 2 | <my-global-icon iconName="channel" aria-hidden="true"></my-global-icon> |
3 | <my-global-icon iconName="channel" aria-hidden="true"></my-global-icon> | 3 | <ng-container i18n>My channels</ng-container> |
4 | <ng-container i18n>My channels</ng-container> | 4 | <span class="badge badge-secondary">{{ totalItems }}</span> |
5 | <span class="badge badge-secondary">{{ totalItems }}</span> | ||
6 | </span> | ||
7 | </h1> | 5 | </h1> |
8 | 6 | ||
9 | <div class="video-channels-header d-flex justify-content-between"> | 7 | <div class="video-channels-header d-flex justify-content-between"> |
10 | <div class="has-feedback has-clear"> | 8 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
11 | <input type="text" placeholder="Search your channels" i18n-placeholder [(ngModel)]="channelsSearch" | ||
12 | (ngModelChange)="onChannelsSearchChanged()" /> | ||
13 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
14 | <span class="sr-only" i18n>Clear filters</span> | ||
15 | </div> | ||
16 | 9 | ||
17 | <a class="create-button" routerLink="create"> | 10 | <a class="create-button" routerLink="create"> |
18 | <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> | 11 | <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> |
@@ -20,11 +13,11 @@ | |||
20 | </a> | 13 | </a> |
21 | </div> | 14 | </div> |
22 | 15 | ||
16 | <div class="no-results" i18n *ngIf="totalItems === 0">No channel found.</div> | ||
17 | |||
23 | <div class="video-channels"> | 18 | <div class="video-channels"> |
24 | <div *ngFor="let videoChannel of videoChannels; let i = index" class="video-channel"> | 19 | <div *ngFor="let videoChannel of videoChannels; let i = index" class="video-channel"> |
25 | <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]"> | 20 | <my-actor-avatar [channel]="videoChannel" [internalHref]="[ '/video-channels', videoChannel.nameWithHost ]"></my-actor-avatar> |
26 | <img [src]="videoChannel.avatarUrl" alt="Avatar" /> | ||
27 | </a> | ||
28 | 21 | ||
29 | <div class="video-channel-info"> | 22 | <div class="video-channel-info"> |
30 | <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]" class="video-channel-names" i18n-title title="Channel page"> | 23 | <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]" class="video-channel-names" i18n-title title="Channel page"> |
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 8804fa95c..191c5169d 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 | |||
@@ -1,6 +1,11 @@ | |||
1 | @import '_variables'; | 1 | @import '_variables'; |
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | h1 my-global-icon { | ||
5 | position: relative; | ||
6 | top: -2px; | ||
7 | } | ||
8 | |||
4 | .create-button { | 9 | .create-button { |
5 | @include create-button; | 10 | @include create-button; |
6 | } | 11 | } |
@@ -9,10 +14,8 @@ input[type=text] { | |||
9 | @include peertube-input-text(300px); | 14 | @include peertube-input-text(300px); |
10 | } | 15 | } |
11 | 16 | ||
12 | ::ng-deep .action-button { | 17 | my-edit-button { |
13 | &.action-button-edit { | 18 | margin-right: 10px; |
14 | margin-right: 10px; | ||
15 | } | ||
16 | } | 19 | } |
17 | 20 | ||
18 | .video-channel { | 21 | .video-channel { |
@@ -20,40 +23,40 @@ input[type=text] { | |||
20 | 23 | ||
21 | padding-bottom: 0; | 24 | padding-bottom: 0; |
22 | 25 | ||
23 | img { | 26 | my-actor-avatar { |
24 | @include channel-avatar(80px); | 27 | @include actor-avatar-size(80px); |
25 | 28 | ||
26 | margin-right: 10px; | 29 | margin-right: 10px; |
27 | } | 30 | } |
31 | } | ||
28 | 32 | ||
29 | .video-channel-info { | 33 | .video-channel-info { |
30 | flex-grow: 1; | 34 | flex-grow: 1; |
31 | 35 | } | |
32 | a.video-channel-names { | ||
33 | @include disable-default-a-behaviour; | ||
34 | |||
35 | width: fit-content; | ||
36 | display: flex; | ||
37 | align-items: baseline; | ||
38 | color: pvar(--mainForegroundColor); | ||
39 | |||
40 | .video-channel-display-name { | ||
41 | font-weight: $font-semibold; | ||
42 | font-size: 18px; | ||
43 | } | ||
44 | |||
45 | .video-channel-name { | ||
46 | font-size: 14px; | ||
47 | color: $grey-actor-name; | ||
48 | margin-left: 5px; | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | 36 | ||
53 | .video-channel-buttons { | 37 | .video-channel-names { |
54 | margin-top: 10px; | 38 | @include disable-default-a-behaviour; |
55 | min-width: 190px; | 39 | |
56 | } | 40 | width: fit-content; |
41 | display: flex; | ||
42 | align-items: baseline; | ||
43 | color: pvar(--mainForegroundColor); | ||
44 | } | ||
45 | |||
46 | .video-channel-display-name { | ||
47 | font-weight: $font-semibold; | ||
48 | font-size: 18px; | ||
49 | } | ||
50 | |||
51 | .video-channel-name { | ||
52 | font-size: 14px; | ||
53 | color: $grey-actor-name; | ||
54 | margin-left: 5px; | ||
55 | } | ||
56 | |||
57 | .video-channel-buttons { | ||
58 | margin-top: 10px; | ||
59 | min-width: 190px; | ||
57 | } | 60 | } |
58 | 61 | ||
59 | ::ng-deep .chartjs-render-monitor { | 62 | ::ng-deep .chartjs-render-monitor { |
@@ -73,21 +76,6 @@ input[type=text] { | |||
73 | .video-channel { | 76 | .video-channel { |
74 | padding-bottom: 10px; | 77 | padding-bottom: 10px; |
75 | 78 | ||
76 | .video-channel-info { | ||
77 | padding-bottom: 10px; | ||
78 | text-align: center; | ||
79 | |||
80 | .video-channel-names { | ||
81 | flex-direction: column; | ||
82 | align-items: center !important; | ||
83 | margin: auto; | ||
84 | |||
85 | .video-channel-name { | ||
86 | margin-left: 0px !important; | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | img { | 79 | img { |
92 | margin-right: 0; | 80 | margin-right: 0; |
93 | } | 81 | } |
@@ -96,6 +84,21 @@ input[type=text] { | |||
96 | align-self: center; | 84 | align-self: center; |
97 | } | 85 | } |
98 | } | 86 | } |
87 | |||
88 | .video-channel-info { | ||
89 | padding-bottom: 10px; | ||
90 | text-align: center; | ||
91 | } | ||
92 | |||
93 | .video-channel-names { | ||
94 | flex-direction: column; | ||
95 | align-items: center !important; | ||
96 | margin: auto; | ||
97 | } | ||
98 | |||
99 | .video-channel-name { | ||
100 | margin-left: 0 !important; | ||
101 | } | ||
99 | } | 102 | } |
100 | 103 | ||
101 | @media screen and (max-width: $mobile-view) { | 104 | @media screen and (max-width: $mobile-view) { |
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts index f6ba50a48..9e3bf35b4 100644 --- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts +++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts | |||
@@ -1,29 +1,26 @@ | |||
1 | import { ChartData } from 'chart.js' | 1 | import { ChartData } from 'chart.js' |
2 | import { max, maxBy, min, minBy } from 'lodash-es' | 2 | import { max, maxBy, min, minBy } from 'lodash-es' |
3 | import { Subject } from 'rxjs' | 3 | import { mergeMap } from 'rxjs/operators' |
4 | import { debounceTime, mergeMap } from 'rxjs/operators' | 4 | import { Component } from '@angular/core' |
5 | import { Component, OnInit } from '@angular/core' | 5 | import { AuthService, ConfirmService, Notifier, ScreenService } from '@app/core' |
6 | import { AuthService, ConfirmService, Notifier, ScreenService, User } from '@app/core' | ||
7 | import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' | 6 | import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' |
8 | 7 | ||
9 | @Component({ | 8 | @Component({ |
10 | templateUrl: './my-video-channels.component.html', | 9 | templateUrl: './my-video-channels.component.html', |
11 | styleUrls: [ './my-video-channels.component.scss' ] | 10 | styleUrls: [ './my-video-channels.component.scss' ] |
12 | }) | 11 | }) |
13 | export class MyVideoChannelsComponent implements OnInit { | 12 | export class MyVideoChannelsComponent { |
14 | totalItems: number | 13 | totalItems: number |
15 | 14 | ||
16 | videoChannels: VideoChannel[] = [] | 15 | videoChannels: VideoChannel[] = [] |
16 | |||
17 | videoChannelsChartData: ChartData[] | 17 | videoChannelsChartData: ChartData[] |
18 | videoChannelsMinimumDailyViews = 0 | 18 | videoChannelsMinimumDailyViews = 0 |
19 | videoChannelsMaximumDailyViews: number | 19 | videoChannelsMaximumDailyViews: number |
20 | 20 | ||
21 | channelsSearch: string | ||
22 | channelsSearchChanged = new Subject<string>() | ||
23 | |||
24 | chartOptions: any | 21 | chartOptions: any |
25 | 22 | ||
26 | private user: User | 23 | search: string |
27 | 24 | ||
28 | constructor ( | 25 | constructor ( |
29 | private authService: AuthService, | 26 | private authService: AuthService, |
@@ -31,31 +28,15 @@ export class MyVideoChannelsComponent implements OnInit { | |||
31 | private confirmService: ConfirmService, | 28 | private confirmService: ConfirmService, |
32 | private videoChannelService: VideoChannelService, | 29 | private videoChannelService: VideoChannelService, |
33 | private screenService: ScreenService | 30 | private screenService: ScreenService |
34 | ) {} | 31 | ) {} |
35 | |||
36 | ngOnInit () { | ||
37 | this.user = this.authService.getUser() | ||
38 | |||
39 | this.loadVideoChannels() | ||
40 | |||
41 | this.channelsSearchChanged | ||
42 | .pipe(debounceTime(500)) | ||
43 | .subscribe(() => { | ||
44 | this.loadVideoChannels() | ||
45 | }) | ||
46 | } | ||
47 | 32 | ||
48 | get isInSmallView () { | 33 | get isInSmallView () { |
49 | return this.screenService.isInSmallView() | 34 | return this.screenService.isInSmallView() |
50 | } | 35 | } |
51 | 36 | ||
52 | resetSearch () { | 37 | onSearch (search: string) { |
53 | this.channelsSearch = '' | 38 | this.search = search |
54 | this.onChannelsSearchChanged() | 39 | this.loadVideoChannels() |
55 | } | ||
56 | |||
57 | onChannelsSearchChanged () { | ||
58 | this.channelsSearchChanged.next() | ||
59 | } | 40 | } |
60 | 41 | ||
61 | async deleteVideoChannel (videoChannel: VideoChannel) { | 42 | async deleteVideoChannel (videoChannel: VideoChannel) { |
@@ -85,8 +66,11 @@ channel with the same name (${videoChannel.name})!`, | |||
85 | 66 | ||
86 | private loadVideoChannels () { | 67 | private loadVideoChannels () { |
87 | this.authService.userInformationLoaded | 68 | this.authService.userInformationLoaded |
88 | .pipe(mergeMap(() => this.videoChannelService.listAccountVideoChannels(this.user.account, null, true, this.channelsSearch))) | 69 | .pipe(mergeMap(() => { |
89 | .subscribe(res => { | 70 | const user = this.authService.getUser() |
71 | |||
72 | return this.videoChannelService.listAccountVideoChannels(user.account, null, true, this.search) | ||
73 | })).subscribe(res => { | ||
90 | this.videoChannels = res.data | 74 | this.videoChannels = res.data |
91 | this.totalItems = res.total | 75 | this.totalItems = res.total |
92 | 76 | ||
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts b/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts index 53557ca02..c775bfdee 100644 --- a/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts +++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { ChartModule } from 'primeng/chart' | 1 | import { ChartModule } from 'primeng/chart' |
2 | import { NgModule } from '@angular/core' | 2 | import { NgModule } from '@angular/core' |
3 | import { SharedActorImageModule } from '@app/shared/shared-actor-image' | 3 | import { SharedActorImageEditModule } from '@app/shared/shared-actor-image-edit' |
4 | import { SharedFormModule } from '@app/shared/shared-forms' | 4 | import { SharedFormModule } from '@app/shared/shared-forms' |
5 | import { SharedGlobalIconModule } from '@app/shared/shared-icons' | 5 | import { SharedGlobalIconModule } from '@app/shared/shared-icons' |
6 | import { SharedMainModule } from '@app/shared/shared-main' | 6 | import { SharedMainModule } from '@app/shared/shared-main' |
@@ -8,6 +8,7 @@ import { MyVideoChannelCreateComponent } from './my-video-channel-create.compone | |||
8 | import { MyVideoChannelUpdateComponent } from './my-video-channel-update.component' | 8 | import { MyVideoChannelUpdateComponent } from './my-video-channel-update.component' |
9 | import { MyVideoChannelsRoutingModule } from './my-video-channels-routing.module' | 9 | import { MyVideoChannelsRoutingModule } from './my-video-channels-routing.module' |
10 | import { MyVideoChannelsComponent } from './my-video-channels.component' | 10 | import { MyVideoChannelsComponent } from './my-video-channels.component' |
11 | import { SharedActorImageModule } from '@app/shared/shared-actor-image/shared-actor-image.module' | ||
11 | 12 | ||
12 | @NgModule({ | 13 | @NgModule({ |
13 | imports: [ | 14 | imports: [ |
@@ -18,6 +19,7 @@ import { MyVideoChannelsComponent } from './my-video-channels.component' | |||
18 | SharedMainModule, | 19 | SharedMainModule, |
19 | SharedFormModule, | 20 | SharedFormModule, |
20 | SharedGlobalIconModule, | 21 | SharedGlobalIconModule, |
22 | SharedActorImageEditModule, | ||
21 | SharedActorImageModule | 23 | SharedActorImageModule |
22 | ], | 24 | ], |
23 | 25 | ||
diff --git a/client/src/app/+my-library/my-history/my-history.component.html b/client/src/app/+my-library/my-history/my-history.component.html index 9dec64645..45ca37e0d 100644 --- a/client/src/app/+my-library/my-history/my-history.component.html +++ b/client/src/app/+my-library/my-history/my-history.component.html | |||
@@ -5,14 +5,7 @@ | |||
5 | 5 | ||
6 | <div class="top-buttons"> | 6 | <div class="top-buttons"> |
7 | <div class="search-wrapper"> | 7 | <div class="search-wrapper"> |
8 | <div class="input-group has-feedback has-clear"> | 8 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
9 | <input | ||
10 | type="text" name="history-search" id="history-search" i18n-placeholder placeholder="Search your history" | ||
11 | (keyup)="onSearch($event)" | ||
12 | > | ||
13 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
14 | <span class="sr-only" i18n>Clear filters</span> | ||
15 | </div> | ||
16 | </div> | 9 | </div> |
17 | 10 | ||
18 | <div class="history-switch"> | 11 | <div class="history-switch"> |
@@ -26,14 +19,15 @@ | |||
26 | </button> | 19 | </button> |
27 | </div> | 20 | </div> |
28 | 21 | ||
29 | 22 | <my-videos-selection | |
30 | <div class="no-history" i18n *ngIf="hasDoneFirstQuery && videos.length === 0">You don't have any video in your watch history yet.</div> | 23 | [pagination]="pagination" |
31 | 24 | [(videosModel)]="videos" | |
32 | <div myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()" class="videos"> | 25 | [miniatureDisplayOptions]="miniatureDisplayOptions" |
33 | <div class="video" *ngFor="let video of videos"> | 26 | [titlePage]="titlePage" |
34 | <my-video-miniature | 27 | [getVideosObservableFunction]="getVideosObservableFunction" |
35 | [video]="video" [displayAsRow]="true" | 28 | [user]="user" |
36 | (videoRemoved)="removeVideoFromArray(video)" (videoBlocked)="removeVideoFromArray(video)" | 29 | [loadOnInit]="false" |
37 | ></my-video-miniature> | 30 | i18n-noResultMessage noResultMessage="You don't have any video in your watch history yet." |
38 | </div> | 31 | [enableSelection]="false" |
39 | </div> | 32 | #videosSelection |
33 | ></my-videos-selection> | ||
diff --git a/client/src/app/+my-library/my-history/my-history.component.scss b/client/src/app/+my-library/my-history/my-history.component.scss index af4a34b4b..28b809f71 100644 --- a/client/src/app/+my-library/my-history/my-history.component.scss +++ b/client/src/app/+my-library/my-history/my-history.component.scss | |||
@@ -39,12 +39,12 @@ | |||
39 | } | 39 | } |
40 | 40 | ||
41 | .delete-history { | 41 | .delete-history { |
42 | grid-column: 4; | ||
43 | |||
44 | @include peertube-button; | 42 | @include peertube-button; |
45 | @include grey-button; | 43 | @include grey-button; |
46 | @include button-with-icon; | 44 | @include button-with-icon; |
47 | 45 | ||
46 | grid-column: 4; | ||
47 | |||
48 | font-size: 15px; | 48 | font-size: 15px; |
49 | } | 49 | } |
50 | } | 50 | } |
diff --git a/client/src/app/+my-library/my-history/my-history.component.ts b/client/src/app/+my-library/my-history/my-history.component.ts index 1695bd7ad..ad83db7ab 100644 --- a/client/src/app/+my-library/my-history/my-history.component.ts +++ b/client/src/app/+my-library/my-history/my-history.component.ts | |||
@@ -1,36 +1,55 @@ | |||
1 | import { Component, ComponentFactoryResolver, OnDestroy, OnInit } from '@angular/core' | 1 | import { Subject } from 'rxjs' |
2 | import { tap } from 'rxjs/operators' | ||
3 | import { Component, ComponentFactoryResolver, OnInit, ViewChild } from '@angular/core' | ||
2 | import { ActivatedRoute, Router } from '@angular/router' | 4 | import { ActivatedRoute, Router } from '@angular/router' |
3 | import { | 5 | import { |
4 | AuthService, | 6 | AuthService, |
5 | ComponentPagination, | 7 | ComponentPagination, |
6 | ConfirmService, | 8 | ConfirmService, |
9 | DisableForReuseHook, | ||
7 | LocalStorageService, | 10 | LocalStorageService, |
8 | Notifier, | 11 | Notifier, |
9 | ScreenService, | 12 | ScreenService, |
10 | ServerService, | 13 | ServerService, |
14 | User, | ||
11 | UserService | 15 | UserService |
12 | } from '@app/core' | 16 | } from '@app/core' |
13 | import { immutableAssign } from '@app/helpers' | 17 | import { immutableAssign } from '@app/helpers' |
14 | import { UserHistoryService } from '@app/shared/shared-main' | 18 | import { UserHistoryService, Video } from '@app/shared/shared-main' |
15 | import { AbstractVideoList } from '@app/shared/shared-video-miniature' | 19 | import { MiniatureDisplayOptions, VideosSelectionComponent } from '@app/shared/shared-video-miniature' |
16 | import { Subject } from 'rxjs' | ||
17 | import { debounceTime, tap, distinctUntilChanged } from 'rxjs/operators' | ||
18 | 20 | ||
19 | @Component({ | 21 | @Component({ |
20 | templateUrl: './my-history.component.html', | 22 | templateUrl: './my-history.component.html', |
21 | styleUrls: [ './my-history.component.scss' ] | 23 | styleUrls: [ './my-history.component.scss' ] |
22 | }) | 24 | }) |
23 | export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnDestroy { | 25 | export class MyHistoryComponent implements OnInit, DisableForReuseHook { |
26 | @ViewChild('videosSelection', { static: true }) videosSelection: VideosSelectionComponent | ||
27 | |||
24 | titlePage: string | 28 | titlePage: string |
25 | pagination: ComponentPagination = { | 29 | pagination: ComponentPagination = { |
26 | currentPage: 1, | 30 | currentPage: 1, |
27 | itemsPerPage: 5, | 31 | itemsPerPage: 5, |
28 | totalItems: null | 32 | totalItems: null |
29 | } | 33 | } |
34 | |||
30 | videosHistoryEnabled: boolean | 35 | videosHistoryEnabled: boolean |
31 | search: string | ||
32 | 36 | ||
33 | protected searchStream: Subject<string> | 37 | miniatureDisplayOptions: MiniatureDisplayOptions = { |
38 | date: true, | ||
39 | views: true, | ||
40 | by: true, | ||
41 | privacyLabel: false, | ||
42 | privacyText: true, | ||
43 | state: true, | ||
44 | blacklistInfo: true | ||
45 | } | ||
46 | |||
47 | getVideosObservableFunction = this.getVideosObservable.bind(this) | ||
48 | |||
49 | user: User | ||
50 | |||
51 | videos: Video[] = [] | ||
52 | search: string | ||
34 | 53 | ||
35 | constructor ( | 54 | constructor ( |
36 | protected router: Router, | 55 | protected router: Router, |
@@ -45,45 +64,31 @@ export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnD | |||
45 | private userHistoryService: UserHistoryService, | 64 | private userHistoryService: UserHistoryService, |
46 | protected cfr: ComponentFactoryResolver | 65 | protected cfr: ComponentFactoryResolver |
47 | ) { | 66 | ) { |
48 | super() | ||
49 | |||
50 | this.titlePage = $localize`My watch history` | 67 | this.titlePage = $localize`My watch history` |
51 | } | 68 | } |
52 | 69 | ||
53 | ngOnInit () { | 70 | ngOnInit () { |
54 | super.ngOnInit() | 71 | this.user = this.authService.getUser() |
55 | 72 | ||
56 | this.authService.userInformationLoaded | 73 | this.authService.userInformationLoaded |
57 | .subscribe(() => { | 74 | .subscribe(() => this.videosHistoryEnabled = this.user.videosHistoryEnabled) |
58 | this.videosHistoryEnabled = this.authService.getUser().videosHistoryEnabled | 75 | } |
59 | }) | ||
60 | |||
61 | this.searchStream = new Subject() | ||
62 | 76 | ||
63 | this.searchStream | 77 | disableForReuse () { |
64 | .pipe( | 78 | this.videosSelection.disableForReuse() |
65 | debounceTime(400), | ||
66 | distinctUntilChanged() | ||
67 | ) | ||
68 | .subscribe(search => { | ||
69 | this.search = search | ||
70 | this.reloadVideos() | ||
71 | }) | ||
72 | } | 79 | } |
73 | 80 | ||
74 | onSearch (event: Event) { | 81 | enabledForReuse () { |
75 | const target = event.target as HTMLInputElement | 82 | this.videosSelection.enabledForReuse() |
76 | this.searchStream.next(target.value) | ||
77 | } | 83 | } |
78 | 84 | ||
79 | resetSearch () { | 85 | reloadData () { |
80 | const searchInput = document.getElementById('history-search') as HTMLInputElement | 86 | this.videosSelection.reloadVideos() |
81 | searchInput.value = '' | ||
82 | this.searchStream.next('') | ||
83 | } | 87 | } |
84 | 88 | ||
85 | ngOnDestroy () { | 89 | onSearch (search: string) { |
86 | super.ngOnDestroy() | 90 | this.search = search |
91 | this.reloadData() | ||
87 | } | 92 | } |
88 | 93 | ||
89 | getVideosObservable (page: number) { | 94 | getVideosObservable (page: number) { |
@@ -129,7 +134,7 @@ export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnD | |||
129 | () => { | 134 | () => { |
130 | this.notifier.success($localize`Videos history deleted`) | 135 | this.notifier.success($localize`Videos history deleted`) |
131 | 136 | ||
132 | this.reloadVideos() | 137 | this.reloadData() |
133 | }, | 138 | }, |
134 | 139 | ||
135 | err => this.notifier.error(err.message) | 140 | err => this.notifier.error(err.message) |
diff --git a/client/src/app/+my-library/my-library.component.scss b/client/src/app/+my-library/my-library.component.scss index a5bb499b4..b32bc84e7 100644 --- a/client/src/app/+my-library/my-library.component.scss +++ b/client/src/app/+my-library/my-library.component.scss | |||
@@ -2,12 +2,12 @@ | |||
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | .row { | 4 | .row { |
5 | @include sub-menu-h1; | ||
6 | |||
5 | flex-direction: column; | 7 | flex-direction: column; |
6 | width: 100%; | 8 | width: 100%; |
7 | 9 | ||
8 | & > my-top-menu-dropdown:nth-child(1) { | 10 | > my-top-menu-dropdown:nth-child(1) { |
9 | flex-grow: 1; | 11 | flex-grow: 1; |
10 | } | 12 | } |
11 | |||
12 | @include sub-menu-h1; | ||
13 | } | 13 | } |
diff --git a/client/src/app/+my-library/my-library.module.ts b/client/src/app/+my-library/my-library.module.ts index a1d706f0b..264ad03f7 100644 --- a/client/src/app/+my-library/my-library.module.ts +++ b/client/src/app/+my-library/my-library.module.ts | |||
@@ -26,7 +26,7 @@ import { MyVideoPlaylistUpdateComponent } from './my-video-playlists/my-video-pl | |||
26 | import { MyVideoPlaylistsComponent } from './my-video-playlists/my-video-playlists.component' | 26 | import { MyVideoPlaylistsComponent } from './my-video-playlists/my-video-playlists.component' |
27 | import { VideoChangeOwnershipComponent } from './my-videos/modals/video-change-ownership.component' | 27 | import { VideoChangeOwnershipComponent } from './my-videos/modals/video-change-ownership.component' |
28 | import { MyVideosComponent } from './my-videos/my-videos.component' | 28 | import { MyVideosComponent } from './my-videos/my-videos.component' |
29 | import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module' | 29 | import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module' |
30 | 30 | ||
31 | @NgModule({ | 31 | @NgModule({ |
32 | imports: [ | 32 | imports: [ |
@@ -47,7 +47,7 @@ import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/share | |||
47 | SharedAbuseListModule, | 47 | SharedAbuseListModule, |
48 | SharedShareModal, | 48 | SharedShareModal, |
49 | SharedVideoLiveModule, | 49 | SharedVideoLiveModule, |
50 | SharedAccountAvatarModule | 50 | SharedActorImageModule |
51 | ], | 51 | ], |
52 | 52 | ||
53 | declarations: [ | 53 | declarations: [ |
diff --git a/client/src/app/+my-library/my-ownership/my-ownership.component.html b/client/src/app/+my-library/my-ownership/my-ownership.component.html index d0eff0521..4c02c78fc 100644 --- a/client/src/app/+my-library/my-ownership/my-ownership.component.html +++ b/client/src/app/+my-library/my-ownership/my-ownership.component.html | |||
@@ -37,7 +37,7 @@ | |||
37 | <td> | 37 | <td> |
38 | <a [href]="videoChangeOwnership.initiatorAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> | 38 | <a [href]="videoChangeOwnership.initiatorAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> |
39 | <div class="chip two-lines"> | 39 | <div class="chip two-lines"> |
40 | <my-account-avatar [account]="videoChangeOwnership.initiatorAccount"></my-account-avatar> | 40 | <my-actor-avatar [account]="videoChangeOwnership.initiatorAccount"></my-actor-avatar> |
41 | <div> | 41 | <div> |
42 | {{ videoChangeOwnership.initiatorAccount.displayName }} | 42 | {{ videoChangeOwnership.initiatorAccount.displayName }} |
43 | <span class="text-muted">{{ videoChangeOwnership.initiatorAccount.nameWithHost }}</span> | 43 | <span class="text-muted">{{ videoChangeOwnership.initiatorAccount.nameWithHost }}</span> |
diff --git a/client/src/app/+my-library/my-ownership/my-ownership.component.scss b/client/src/app/+my-library/my-ownership/my-ownership.component.scss index 7cac9c9f3..dfc8fc99e 100644 --- a/client/src/app/+my-library/my-ownership/my-ownership.component.scss +++ b/client/src/app/+my-library/my-ownership/my-ownership.component.scss | |||
@@ -13,15 +13,15 @@ | |||
13 | display: inline-flex; | 13 | display: inline-flex; |
14 | 14 | ||
15 | .video-table-video-image { | 15 | .video-table-video-image { |
16 | @include miniature-thumbnail; | ||
17 | |||
18 | $image-height: 45px; | 16 | $image-height: 45px; |
19 | 17 | ||
18 | @include miniature-thumbnail; | ||
19 | |||
20 | height: $image-height; | 20 | height: $image-height; |
21 | width: #{(16/9) * $image-height}; | 21 | width: #{(16/9) * $image-height}; |
22 | margin-right: 0.5rem; | 22 | margin-right: 0.5rem; |
23 | border-radius: 2px; | 23 | border-radius: 2px; |
24 | border: none; | 24 | border: 0; |
25 | background: transparent; | 25 | background: transparent; |
26 | display: inline-flex; | 26 | display: inline-flex; |
27 | justify-content: center; | 27 | justify-content: center; |
@@ -60,7 +60,7 @@ | |||
60 | 60 | ||
61 | div .glyphicon { | 61 | div .glyphicon { |
62 | font-size: 80%; | 62 | font-size: 80%; |
63 | color: gray; | 63 | color: #808080; |
64 | margin-left: 0.1rem; | 64 | margin-left: 0.1rem; |
65 | } | 65 | } |
66 | 66 | ||
diff --git a/client/src/app/+my-library/my-ownership/my-ownership.component.ts b/client/src/app/+my-library/my-ownership/my-ownership.component.ts index a938023b4..aaf028474 100644 --- a/client/src/app/+my-library/my-ownership/my-ownership.component.ts +++ b/client/src/app/+my-library/my-ownership/my-ownership.component.ts | |||
@@ -48,18 +48,18 @@ export class MyOwnershipComponent extends RestTable implements OnInit { | |||
48 | } | 48 | } |
49 | 49 | ||
50 | accepted () { | 50 | accepted () { |
51 | this.loadData() | 51 | this.reloadData() |
52 | } | 52 | } |
53 | 53 | ||
54 | refuse (videoChangeOwnership: VideoChangeOwnership) { | 54 | refuse (videoChangeOwnership: VideoChangeOwnership) { |
55 | this.videoOwnershipService.refuseOwnership(videoChangeOwnership.id) | 55 | this.videoOwnershipService.refuseOwnership(videoChangeOwnership.id) |
56 | .subscribe( | 56 | .subscribe( |
57 | () => this.loadData(), | 57 | () => this.reloadData(), |
58 | err => this.notifier.error(err.message) | 58 | err => this.notifier.error(err.message) |
59 | ) | 59 | ) |
60 | } | 60 | } |
61 | 61 | ||
62 | protected loadData () { | 62 | protected reloadData () { |
63 | return this.videoOwnershipService.getOwnershipChanges(this.pagination, this.sort) | 63 | return this.videoOwnershipService.getOwnershipChanges(this.pagination, this.sort) |
64 | .subscribe( | 64 | .subscribe( |
65 | resultList => { | 65 | resultList => { |
diff --git a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html index ff448ad87..f91cebacf 100644 --- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html +++ b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html | |||
@@ -7,21 +7,14 @@ | |||
7 | </h1> | 7 | </h1> |
8 | 8 | ||
9 | <div class="video-subscriptions-header"> | 9 | <div class="video-subscriptions-header"> |
10 | <div class="has-feedback has-clear"> | 10 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
11 | <input type="text" placeholder="Search your subscriptions" i18n-placeholder [(ngModel)]="subscriptionsSearch" | ||
12 | (ngModelChange)="onSubscriptionsSearchChanged()" /> | ||
13 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
14 | <span class="sr-only" i18n>Clear filters</span> | ||
15 | </div> | ||
16 | </div> | 11 | </div> |
17 | 12 | ||
18 | <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> |
19 | 14 | ||
20 | <div class="video-channels" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()"> | 15 | <div class="video-channels" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()"> |
21 | <div *ngFor="let videoChannel of videoChannels" class="video-channel"> | 16 | <div *ngFor="let videoChannel of videoChannels" class="video-channel"> |
22 | <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]"> | 17 | <my-actor-avatar [channel]="videoChannel" [internalHref]="[ '/video-channels', videoChannel.nameWithHost ]"></my-actor-avatar> |
23 | <img [src]="videoChannel.avatarUrl" alt="Avatar" /> | ||
24 | </a> | ||
25 | 18 | ||
26 | <div class="video-channel-info"> | 19 | <div class="video-channel-info"> |
27 | <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]" class="video-channel-names" i18n-title title="Channel page"> | 20 | <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]" class="video-channel-names" i18n-title title="Channel page"> |
@@ -33,7 +26,8 @@ | |||
33 | 26 | ||
34 | <a [routerLink]="[ '/accounts', videoChannel.ownerBy ]" i18n-title title="Owner account page" class="actor-owner"> | 27 | <a [routerLink]="[ '/accounts', videoChannel.ownerBy ]" i18n-title title="Owner account page" class="actor-owner"> |
35 | <span i18n>Created by {{ videoChannel.ownerBy }}</span> | 28 | <span i18n>Created by {{ videoChannel.ownerBy }}</span> |
36 | <img [src]="videoChannel.ownerAvatarUrl" alt="Owner account avatar" /> | 29 | |
30 | <my-actor-avatar [account]="videoChannel.ownerAccount" size="18"></my-actor-avatar> | ||
37 | </a> | 31 | </a> |
38 | </div> | 32 | </div> |
39 | 33 | ||
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 index 3c1a4d2ad..6c1ddf716 100644 --- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss +++ b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss | |||
@@ -8,8 +8,8 @@ input[type=text] { | |||
8 | .video-channel { | 8 | .video-channel { |
9 | @include row-blocks; | 9 | @include row-blocks; |
10 | 10 | ||
11 | img { | 11 | > my-actor-avatar { |
12 | @include channel-avatar(80px); | 12 | @include actor-avatar-size(80px); |
13 | 13 | ||
14 | margin-right: 10px; | 14 | margin-right: 10px; |
15 | } | 15 | } |
@@ -40,13 +40,25 @@ input[type=text] { | |||
40 | } | 40 | } |
41 | 41 | ||
42 | .actor-owner { | 42 | .actor-owner { |
43 | @include actor-owner; | 43 | @include disable-default-a-behaviour; |
44 | |||
45 | font-size: 13px; | ||
46 | color: pvar(--mainForegroundColor); | ||
44 | 47 | ||
45 | margin-top: 0; | 48 | span:hover { |
49 | opacity: 0.8; | ||
50 | } | ||
51 | |||
52 | my-actor-avatar { | ||
53 | margin-left: 7px; | ||
54 | display: inline-block; | ||
55 | vertical-align: top; | ||
56 | } | ||
46 | } | 57 | } |
47 | 58 | ||
48 | .video-subscriptions-header { | 59 | .video-subscriptions-header { |
49 | margin-bottom: 30px; | 60 | margin-bottom: 30px; |
61 | display: flex; | ||
50 | } | 62 | } |
51 | 63 | ||
52 | @media screen and (max-width: $small-view) { | 64 | @media screen and (max-width: $small-view) { |
diff --git a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts index 3b748eccf..1f4a931a0 100644 --- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts +++ b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import { Subject } from 'rxjs' | 1 | import { Subject } from 'rxjs' |
2 | import { debounceTime } from 'rxjs/operators' | 2 | import { Component } from '@angular/core' |
3 | import { Component, OnInit } from '@angular/core' | ||
4 | import { ComponentPagination, Notifier } from '@app/core' | 3 | import { ComponentPagination, Notifier } from '@app/core' |
5 | import { VideoChannel } from '@app/shared/shared-main' | 4 | import { VideoChannel } from '@app/shared/shared-main' |
6 | import { UserSubscriptionService } from '@app/shared/shared-user-subscription' | 5 | import { UserSubscriptionService } from '@app/shared/shared-user-subscription' |
@@ -9,7 +8,7 @@ import { UserSubscriptionService } from '@app/shared/shared-user-subscription' | |||
9 | templateUrl: './my-subscriptions.component.html', | 8 | templateUrl: './my-subscriptions.component.html', |
10 | styleUrls: [ './my-subscriptions.component.scss' ] | 9 | styleUrls: [ './my-subscriptions.component.scss' ] |
11 | }) | 10 | }) |
12 | export class MySubscriptionsComponent implements OnInit { | 11 | export class MySubscriptionsComponent { |
13 | videoChannels: VideoChannel[] = [] | 12 | videoChannels: VideoChannel[] = [] |
14 | 13 | ||
15 | pagination: ComponentPagination = { | 14 | pagination: ComponentPagination = { |
@@ -20,34 +19,13 @@ export class MySubscriptionsComponent implements OnInit { | |||
20 | 19 | ||
21 | onDataSubject = new Subject<any[]>() | 20 | onDataSubject = new Subject<any[]>() |
22 | 21 | ||
23 | subscriptionsSearch: string | 22 | search: string |
24 | subscriptionsSearchChanged = new Subject<string>() | ||
25 | 23 | ||
26 | constructor ( | 24 | constructor ( |
27 | private userSubscriptionService: UserSubscriptionService, | 25 | private userSubscriptionService: UserSubscriptionService, |
28 | private notifier: Notifier | 26 | private notifier: Notifier |
29 | ) {} | 27 | ) {} |
30 | 28 | ||
31 | ngOnInit () { | ||
32 | this.loadSubscriptions() | ||
33 | |||
34 | this.subscriptionsSearchChanged | ||
35 | .pipe(debounceTime(500)) | ||
36 | .subscribe(() => { | ||
37 | this.pagination.currentPage = 1 | ||
38 | this.loadSubscriptions(false) | ||
39 | }) | ||
40 | } | ||
41 | |||
42 | resetSearch () { | ||
43 | this.subscriptionsSearch = '' | ||
44 | this.onSubscriptionsSearchChanged() | ||
45 | } | ||
46 | |||
47 | onSubscriptionsSearchChanged () { | ||
48 | this.subscriptionsSearchChanged.next() | ||
49 | } | ||
50 | |||
51 | onNearOfBottom () { | 29 | onNearOfBottom () { |
52 | // Last page | 30 | // Last page |
53 | if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return | 31 | if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return |
@@ -56,8 +34,13 @@ export class MySubscriptionsComponent implements OnInit { | |||
56 | this.loadSubscriptions() | 34 | this.loadSubscriptions() |
57 | } | 35 | } |
58 | 36 | ||
37 | onSearch (search: string) { | ||
38 | this.search = search | ||
39 | this.loadSubscriptions(false) | ||
40 | } | ||
41 | |||
59 | private loadSubscriptions (more = true) { | 42 | private loadSubscriptions (more = true) { |
60 | this.userSubscriptionService.listSubscriptions({ pagination: this.pagination, search: this.subscriptionsSearch }) | 43 | this.userSubscriptionService.listSubscriptions({ pagination: this.pagination, search: this.search }) |
61 | .subscribe( | 44 | .subscribe( |
62 | res => { | 45 | res => { |
63 | this.videoChannels = more | 46 | this.videoChannels = more |
diff --git a/client/src/app/+my-library/my-video-imports/my-video-imports.component.scss b/client/src/app/+my-library/my-video-imports/my-video-imports.component.scss index a93c28028..c4b847c3d 100644 --- a/client/src/app/+my-library/my-video-imports/my-video-imports.component.scss +++ b/client/src/app/+my-library/my-video-imports/my-video-imports.component.scss | |||
@@ -6,7 +6,7 @@ pre { | |||
6 | } | 6 | } |
7 | 7 | ||
8 | .video-import-error { | 8 | .video-import-error { |
9 | color: red; | 9 | color: #ff0000; |
10 | } | 10 | } |
11 | 11 | ||
12 | .badge { | 12 | .badge { |
diff --git a/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts b/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts index d6d7d7a1b..359535526 100644 --- a/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts +++ b/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts | |||
@@ -62,7 +62,7 @@ export class MyVideoImportsComponent extends RestTable implements OnInit { | |||
62 | return '/videos/update/' + video.uuid | 62 | return '/videos/update/' + video.uuid |
63 | } | 63 | } |
64 | 64 | ||
65 | protected loadData () { | 65 | protected reloadData () { |
66 | this.videoImportService.getMyVideoImports(this.pagination, this.sort) | 66 | this.videoImportService.getMyVideoImports(this.pagination, this.sort) |
67 | .subscribe( | 67 | .subscribe( |
68 | resultList => { | 68 | resultList => { |
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss index 0c68dedf6..67587a58a 100644 --- a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss +++ b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss | |||
@@ -25,8 +25,8 @@ | |||
25 | } | 25 | } |
26 | 26 | ||
27 | .playlist-buttons { | 27 | .playlist-buttons { |
28 | display:flex; | 28 | display: flex; |
29 | margin: 30px 0 10px 0; | 29 | margin: 30px 0 10px; |
30 | 30 | ||
31 | .share-button { | 31 | .share-button { |
32 | @include peertube-button; | 32 | @include peertube-button; |
@@ -42,9 +42,10 @@ | |||
42 | .cdk-drag-preview { | 42 | .cdk-drag-preview { |
43 | box-sizing: border-box; | 43 | box-sizing: border-box; |
44 | border-radius: 4px; | 44 | border-radius: 4px; |
45 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), | 45 | box-shadow: |
46 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), | 46 | 0 5px 5px -3px rgba(0, 0, 0, 0.2), |
47 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); | 47 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), |
48 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); | ||
48 | } | 49 | } |
49 | 50 | ||
50 | .cdk-drag-placeholder { | 51 | .cdk-drag-placeholder { |
@@ -56,7 +57,7 @@ | |||
56 | } | 57 | } |
57 | 58 | ||
58 | .video:last-child { | 59 | .video:last-child { |
59 | border: none; | 60 | border: 0; |
60 | } | 61 | } |
61 | 62 | ||
62 | .videos.cdk-drop-list-dragging .video:not(.cdk-drag-placeholder) { | 63 | .videos.cdk-drop-list-dragging .video:not(.cdk-drag-placeholder) { |
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html index b88ea3db7..309afcf13 100644 --- a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html +++ b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html | |||
@@ -4,12 +4,7 @@ | |||
4 | </h1> | 4 | </h1> |
5 | 5 | ||
6 | <div class="video-playlists-header d-flex justify-content-between"> | 6 | <div class="video-playlists-header d-flex justify-content-between"> |
7 | <div class="has-feedback has-clear"> | 7 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
8 | <input type="text" placeholder="Search your playlists" i18n-placeholder [(ngModel)]="videoPlaylistsSearch" | ||
9 | (ngModelChange)="onVideoPlaylistSearchChanged()" /> | ||
10 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
11 | <span class="sr-only" i18n>Clear filters</span> | ||
12 | </div> | ||
13 | 8 | ||
14 | <a class="create-button" routerLink="create"> | 9 | <a class="create-button" routerLink="create"> |
15 | <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> | 10 | <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> |
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.ts b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.ts index f6d394923..d90102693 100644 --- a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.ts +++ b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Subject } from 'rxjs' | 1 | import { Subject } from 'rxjs' |
2 | import { debounceTime, mergeMap } from 'rxjs/operators' | 2 | import { mergeMap } from 'rxjs/operators' |
3 | import { Component, OnInit } from '@angular/core' | 3 | import { Component } from '@angular/core' |
4 | import { AuthService, ComponentPagination, ConfirmService, Notifier, User } from '@app/core' | 4 | import { AuthService, ComponentPagination, ConfirmService, Notifier } from '@app/core' |
5 | import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' | 5 | import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' |
6 | import { VideoPlaylistType } from '@shared/models' | 6 | import { VideoPlaylistType } from '@shared/models' |
7 | 7 | ||
@@ -9,10 +9,8 @@ import { VideoPlaylistType } from '@shared/models' | |||
9 | templateUrl: './my-video-playlists.component.html', | 9 | templateUrl: './my-video-playlists.component.html', |
10 | styleUrls: [ './my-video-playlists.component.scss' ] | 10 | styleUrls: [ './my-video-playlists.component.scss' ] |
11 | }) | 11 | }) |
12 | export class MyVideoPlaylistsComponent implements OnInit { | 12 | export class MyVideoPlaylistsComponent { |
13 | videoPlaylistsSearch: string | ||
14 | videoPlaylists: VideoPlaylist[] = [] | 13 | videoPlaylists: VideoPlaylist[] = [] |
15 | videoPlaylistSearchChanged = new Subject<string>() | ||
16 | 14 | ||
17 | pagination: ComponentPagination = { | 15 | pagination: ComponentPagination = { |
18 | currentPage: 1, | 16 | currentPage: 1, |
@@ -22,27 +20,14 @@ export class MyVideoPlaylistsComponent implements OnInit { | |||
22 | 20 | ||
23 | onDataSubject = new Subject<any[]>() | 21 | onDataSubject = new Subject<any[]>() |
24 | 22 | ||
25 | private user: User | 23 | search: string |
26 | 24 | ||
27 | constructor ( | 25 | constructor ( |
28 | private authService: AuthService, | 26 | private authService: AuthService, |
29 | private notifier: Notifier, | 27 | private notifier: Notifier, |
30 | private confirmService: ConfirmService, | 28 | private confirmService: ConfirmService, |
31 | private videoPlaylistService: VideoPlaylistService | 29 | private videoPlaylistService: VideoPlaylistService |
32 | ) {} | 30 | ) {} |
33 | |||
34 | ngOnInit () { | ||
35 | this.user = this.authService.getUser() | ||
36 | |||
37 | this.loadVideoPlaylists() | ||
38 | |||
39 | this.videoPlaylistSearchChanged | ||
40 | .pipe( | ||
41 | debounceTime(500)) | ||
42 | .subscribe(() => { | ||
43 | this.loadVideoPlaylists(true) | ||
44 | }) | ||
45 | } | ||
46 | 31 | ||
47 | async deleteVideoPlaylist (videoPlaylist: VideoPlaylist) { | 32 | async deleteVideoPlaylist (videoPlaylist: VideoPlaylist) { |
48 | const res = await this.confirmService.confirm( | 33 | const res = await this.confirmService.confirm( |
@@ -76,22 +61,20 @@ export class MyVideoPlaylistsComponent implements OnInit { | |||
76 | this.loadVideoPlaylists() | 61 | this.loadVideoPlaylists() |
77 | } | 62 | } |
78 | 63 | ||
79 | resetSearch () { | 64 | onSearch (search: string) { |
80 | this.videoPlaylistsSearch = '' | 65 | this.search = search |
81 | this.onVideoPlaylistSearchChanged() | 66 | this.loadVideoPlaylists(true) |
82 | } | ||
83 | |||
84 | onVideoPlaylistSearchChanged () { | ||
85 | this.videoPlaylistSearchChanged.next() | ||
86 | } | 67 | } |
87 | 68 | ||
88 | private loadVideoPlaylists (reset = false) { | 69 | private loadVideoPlaylists (reset = false) { |
89 | this.authService.userInformationLoaded | 70 | this.authService.userInformationLoaded |
90 | .pipe(mergeMap(() => { | 71 | .pipe(mergeMap(() => { |
91 | return this.videoPlaylistService.listAccountPlaylists(this.user.account, this.pagination, '-updatedAt', this.videoPlaylistsSearch) | 72 | const user = this.authService.getUser() |
92 | })) | 73 | |
93 | .subscribe(res => { | 74 | return this.videoPlaylistService.listAccountPlaylists(user.account, this.pagination, '-updatedAt', this.search) |
75 | })).subscribe(res => { | ||
94 | if (reset) this.videoPlaylists = [] | 76 | if (reset) this.videoPlaylists = [] |
77 | |||
95 | this.videoPlaylists = this.videoPlaylists.concat(res.data) | 78 | this.videoPlaylists = this.videoPlaylists.concat(res.data) |
96 | this.pagination.totalItems = res.total | 79 | this.pagination.totalItems = res.total |
97 | 80 | ||
diff --git a/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.scss b/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.scss index a79fec179..16187bc4a 100644 --- a/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.scss +++ b/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.scss | |||
@@ -7,4 +7,4 @@ p-autocomplete { | |||
7 | 7 | ||
8 | .form-group { | 8 | .form-group { |
9 | margin: 20px 0; | 9 | margin: 20px 0; |
10 | } \ No newline at end of file | 10 | } |
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.html b/client/src/app/+my-library/my-videos/my-videos.component.html index e9f436378..8d8b482ad 100644 --- a/client/src/app/+my-library/my-videos/my-videos.component.html +++ b/client/src/app/+my-library/my-videos/my-videos.component.html | |||
@@ -19,12 +19,7 @@ | |||
19 | </h1> | 19 | </h1> |
20 | 20 | ||
21 | <div class="videos-header d-flex justify-content-between"> | 21 | <div class="videos-header d-flex justify-content-between"> |
22 | <div class="has-feedback has-clear"> | 22 | <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter> |
23 | <input type="text" placeholder="Search your videos" i18n-placeholder [(ngModel)]="videosSearch" | ||
24 | (ngModelChange)="onVideosSearchChanged()" /> | ||
25 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
26 | <span class="sr-only" i18n>Clear filters</span> | ||
27 | </div> | ||
28 | 23 | ||
29 | <div class="peertube-select-container peertube-select-button"> | 24 | <div class="peertube-select-container peertube-select-button"> |
30 | <select [(ngModel)]="sort" (ngModelChange)="onChangeSortColumn()" class="form-control"> | 25 | <select [(ngModel)]="sort" (ngModelChange)="onChangeSortColumn()" class="form-control"> |
@@ -46,6 +41,7 @@ | |||
46 | [titlePage]="titlePage" | 41 | [titlePage]="titlePage" |
47 | [getVideosObservableFunction]="getVideosObservableFunction" | 42 | [getVideosObservableFunction]="getVideosObservableFunction" |
48 | [user]="user" | 43 | [user]="user" |
44 | [loadOnInit]="false" | ||
49 | #videosSelection | 45 | #videosSelection |
50 | > | 46 | > |
51 | <ng-template ptTemplate="globalButtons"> | 47 | <ng-template ptTemplate="globalButtons"> |
@@ -64,6 +60,5 @@ | |||
64 | </ng-template> | 60 | </ng-template> |
65 | </my-videos-selection> | 61 | </my-videos-selection> |
66 | 62 | ||
67 | |||
68 | <my-video-change-ownership #videoChangeOwnershipModal></my-video-change-ownership> | 63 | <my-video-change-ownership #videoChangeOwnershipModal></my-video-change-ownership> |
69 | <my-live-stream-information #liveStreamInformationModal></my-live-stream-information> | 64 | <my-live-stream-information #liveStreamInformationModal></my-live-stream-information> |
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.scss b/client/src/app/+my-library/my-videos/my-videos.component.scss index aaf21126b..57623c36f 100644 --- a/client/src/app/+my-library/my-videos/my-videos.component.scss +++ b/client/src/app/+my-library/my-videos/my-videos.component.scss | |||
@@ -26,12 +26,12 @@ h1 { | |||
26 | } | 26 | } |
27 | 27 | ||
28 | .action-button-delete-selection { | 28 | .action-button-delete-selection { |
29 | display: inline-block; | ||
30 | |||
31 | @include peertube-button; | 29 | @include peertube-button; |
32 | @include orange-button; | 30 | @include orange-button; |
33 | @include button-with-icon(21px); | 31 | @include button-with-icon(21px); |
34 | 32 | ||
33 | display: inline-block; | ||
34 | |||
35 | my-global-icon { | 35 | my-global-icon { |
36 | @include apply-svg-color(#fff); | 36 | @include apply-svg-color(#fff); |
37 | } | 37 | } |
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.ts b/client/src/app/+my-library/my-videos/my-videos.component.ts index 356e158d6..1e4a4406d 100644 --- a/client/src/app/+my-library/my-videos/my-videos.component.ts +++ b/client/src/app/+my-library/my-videos/my-videos.component.ts | |||
@@ -1,10 +1,11 @@ | |||
1 | import { concat, Observable, Subject } from 'rxjs' | 1 | import { concat, Observable } from 'rxjs' |
2 | import { debounceTime, tap, toArray } from 'rxjs/operators' | 2 | import { tap, toArray } from 'rxjs/operators' |
3 | import { Component, OnInit, ViewChild } from '@angular/core' | 3 | import { Component, OnInit, ViewChild } from '@angular/core' |
4 | import { ActivatedRoute, Router } from '@angular/router' | 4 | import { ActivatedRoute, Router } from '@angular/router' |
5 | import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User } from '@app/core' | 5 | import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User } from '@app/core' |
6 | import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' | 6 | import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' |
7 | import { immutableAssign } from '@app/helpers' | 7 | import { immutableAssign } from '@app/helpers' |
8 | import { AdvancedInputFilter } from '@app/shared/shared-forms' | ||
8 | import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' | 9 | import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' |
9 | import { LiveStreamInformationComponent } from '@app/shared/shared-video-live' | 10 | import { LiveStreamInformationComponent } from '@app/shared/shared-video-live' |
10 | import { MiniatureDisplayOptions, SelectionType, VideosSelectionComponent } from '@app/shared/shared-video-miniature' | 11 | import { MiniatureDisplayOptions, SelectionType, VideosSelectionComponent } from '@app/shared/shared-video-miniature' |
@@ -40,13 +41,21 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook { | |||
40 | videoActions: DropdownAction<{ video: Video }>[] = [] | 41 | videoActions: DropdownAction<{ video: Video }>[] = [] |
41 | 42 | ||
42 | videos: Video[] = [] | 43 | videos: Video[] = [] |
43 | videosSearch: string | ||
44 | videosSearchChanged = new Subject<string>() | ||
45 | getVideosObservableFunction = this.getVideosObservable.bind(this) | 44 | getVideosObservableFunction = this.getVideosObservable.bind(this) |
45 | |||
46 | sort: VideoSortField = '-publishedAt' | 46 | sort: VideoSortField = '-publishedAt' |
47 | 47 | ||
48 | user: User | 48 | user: User |
49 | 49 | ||
50 | inputFilters: AdvancedInputFilter[] = [ | ||
51 | { | ||
52 | queryParams: { 'search': 'isLive:true' }, | ||
53 | label: $localize`Only live videos` | ||
54 | } | ||
55 | ] | ||
56 | |||
57 | private search: string | ||
58 | |||
50 | constructor ( | 59 | constructor ( |
51 | protected router: Router, | 60 | protected router: Router, |
52 | protected serverService: ServerService, | 61 | protected serverService: ServerService, |
@@ -64,21 +73,15 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook { | |||
64 | this.buildActions() | 73 | this.buildActions() |
65 | 74 | ||
66 | this.user = this.authService.getUser() | 75 | this.user = this.authService.getUser() |
67 | |||
68 | this.videosSearchChanged | ||
69 | .pipe(debounceTime(500)) | ||
70 | .subscribe(() => { | ||
71 | this.videosSelection.reloadVideos() | ||
72 | }) | ||
73 | } | 76 | } |
74 | 77 | ||
75 | resetSearch () { | 78 | onSearch (search: string) { |
76 | this.videosSearch = '' | 79 | this.search = search |
77 | this.onVideosSearchChanged() | 80 | this.reloadData() |
78 | } | 81 | } |
79 | 82 | ||
80 | onVideosSearchChanged () { | 83 | reloadData () { |
81 | this.videosSearchChanged.next() | 84 | this.videosSelection.reloadVideos() |
82 | } | 85 | } |
83 | 86 | ||
84 | onChangeSortColumn () { | 87 | onChangeSortColumn () { |
@@ -96,7 +99,7 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook { | |||
96 | getVideosObservable (page: number) { | 99 | getVideosObservable (page: number) { |
97 | const newPagination = immutableAssign(this.pagination, { currentPage: page }) | 100 | const newPagination = immutableAssign(this.pagination, { currentPage: page }) |
98 | 101 | ||
99 | return this.videoService.getMyVideos(newPagination, this.sort, this.videosSearch) | 102 | return this.videoService.getMyVideos(newPagination, this.sort, this.search) |
100 | .pipe( | 103 | .pipe( |
101 | tap(res => this.pagination.totalItems = res.total) | 104 | tap(res => this.pagination.totalItems = res.total) |
102 | ) | 105 | ) |