aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app')
-rw-r--r--client/src/app/+about/about-follows/about-follows.component.html2
-rw-r--r--client/src/app/+about/about-follows/about-follows.component.scss4
-rw-r--r--client/src/app/+about/about-instance/about-instance.component.scss6
-rw-r--r--client/src/app/+about/about-peertube/about-peertube.component.scss3
-rw-r--r--client/src/app/+accounts/account-video-channels/account-video-channels.component.html7
-rw-r--r--client/src/app/+accounts/account-video-channels/account-video-channels.component.scss14
-rw-r--r--client/src/app/+accounts/accounts.component.html2
-rw-r--r--client/src/app/+accounts/accounts.component.scss6
-rw-r--r--client/src/app/+accounts/accounts.module.ts4
-rw-r--r--client/src/app/+admin/admin.module.ts6
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss5
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html4
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.html14
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.scss11
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.ts6
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.html12
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.scss11
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.ts6
-rw-r--r--client/src/app/+admin/follows/follows.component.scss2
-rw-r--r--client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.scss3
-rw-r--r--client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts6
-rw-r--r--client/src/app/+admin/moderation/abuse-list/abuse-list.component.html2
-rw-r--r--client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html16
-rw-r--r--client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss2
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.html25
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss17
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts48
-rw-r--r--client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html27
-rw-r--r--client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss20
-rw-r--r--client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts27
-rw-r--r--client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.html14
-rw-r--r--client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.scss2
-rw-r--r--client/src/app/+admin/plugins/plugin-search/plugin-search.component.html11
-rw-r--r--client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss3
-rw-r--r--client/src/app/+admin/plugins/plugins.component.scss28
-rw-r--r--client/src/app/+admin/plugins/plugins.component.ts3
-rw-r--r--client/src/app/+admin/plugins/shared/plugin-list.component.scss16
-rw-r--r--client/src/app/+admin/system/jobs/jobs.component.scss2
-rw-r--r--client/src/app/+admin/system/jobs/jobs.component.ts6
-rw-r--r--client/src/app/+admin/system/logs/logs.component.scss4
-rw-r--r--client/src/app/+admin/users/user-edit/user-edit.component.scss3
-rw-r--r--client/src/app/+admin/users/user-edit/user-password.component.scss2
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.html27
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.scss24
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.ts25
-rw-r--r--client/src/app/+login/login.component.scss19
-rw-r--r--client/src/app/+my-account/my-account-abuses/my-account-abuses-list.component.html2
-rw-r--r--client/src/app/+my-account/my-account-applications/my-account-applications.component.scss2
-rw-r--r--client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss3
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.scss2
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.scss2
-rw-r--r--client/src/app/+my-account/my-account.component.scss6
-rw-r--r--client/src/app/+my-account/my-account.module.ts8
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss3
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.component.html21
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss99
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts46
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts4
-rw-r--r--client/src/app/+my-library/my-history/my-history.component.html32
-rw-r--r--client/src/app/+my-library/my-history/my-history.component.scss4
-rw-r--r--client/src/app/+my-library/my-history/my-history.component.ts75
-rw-r--r--client/src/app/+my-library/my-library.component.scss6
-rw-r--r--client/src/app/+my-library/my-library.module.ts4
-rw-r--r--client/src/app/+my-library/my-ownership/my-ownership.component.html2
-rw-r--r--client/src/app/+my-library/my-ownership/my-ownership.component.scss8
-rw-r--r--client/src/app/+my-library/my-ownership/my-ownership.component.ts6
-rw-r--r--client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html14
-rw-r--r--client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss20
-rw-r--r--client/src/app/+my-library/my-subscriptions/my-subscriptions.component.ts35
-rw-r--r--client/src/app/+my-library/my-video-imports/my-video-imports.component.scss2
-rw-r--r--client/src/app/+my-library/my-video-imports/my-video-imports.component.ts2
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss13
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html7
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlists.component.ts45
-rw-r--r--client/src/app/+my-library/my-videos/modals/video-change-ownership.component.scss2
-rw-r--r--client/src/app/+my-library/my-videos/my-videos.component.html9
-rw-r--r--client/src/app/+my-library/my-videos/my-videos.component.scss4
-rw-r--r--client/src/app/+my-library/my-videos/my-videos.component.ts35
-rw-r--r--client/src/app/+search/search-filters.component.scss2
-rw-r--r--client/src/app/+search/search.component.html11
-rw-r--r--client/src/app/+search/search.component.scss18
-rw-r--r--client/src/app/+search/search.component.ts26
-rw-r--r--client/src/app/+search/search.module.ts2
-rw-r--r--client/src/app/+signup/+register/register.component.scss2
-rw-r--r--client/src/app/+signup/shared/signup-success.component.scss22
-rw-r--r--client/src/app/+video-channels/video-channels.component.html20
-rw-r--r--client/src/app/+video-channels/video-channels.component.scss22
-rw-r--r--client/src/app/+video-channels/video-channels.module.ts4
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.scss4
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.html2
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.scss6
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-send.scss6
-rw-r--r--client/src/app/+videos/+video-edit/video-add.component.scss2
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment-add.component.html2
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss9
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment.component.html8
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment.component.scss17
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment.component.ts4
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comments.component.scss5
-rw-r--r--client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html4
-rw-r--r--client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss3
-rw-r--r--client/src/app/+videos/+video-watch/video-avatar-channel.component.html30
-rw-r--r--client/src/app/+videos/+video-watch/video-avatar-channel.component.scss56
-rw-r--r--client/src/app/+videos/+video-watch/video-avatar-channel.component.ts1
-rw-r--r--client/src/app/+videos/+video-watch/video-watch-playlist.component.scss2
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.html2
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.scss23
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.module.ts4
-rw-r--r--client/src/app/+videos/video-list/overview/video-overview.component.html2
-rw-r--r--client/src/app/+videos/video-list/overview/video-overview.component.scss15
-rw-r--r--client/src/app/+videos/video-list/overview/video-overview.component.ts4
-rw-r--r--client/src/app/+videos/video-list/trending/video-trending-header.component.scss2
-rw-r--r--client/src/app/+videos/videos.module.ts4
-rw-r--r--client/src/app/app.component.scss2
-rw-r--r--client/src/app/app.module.ts4
-rw-r--r--client/src/app/core/hotkeys/hotkeys.component.scss5
-rw-r--r--client/src/app/core/rest/rest-table.ts81
-rw-r--r--client/src/app/core/rest/rest.service.ts12
-rw-r--r--client/src/app/core/users/user.service.ts8
-rw-r--r--client/src/app/core/wrappers/screen.service.ts6
-rw-r--r--client/src/app/header/search-typeahead.component.scss13
-rw-r--r--client/src/app/header/suggestion.component.scss8
-rw-r--r--client/src/app/menu/language-chooser.component.scss4
-rw-r--r--client/src/app/menu/menu.component.html2
-rw-r--r--client/src/app/menu/menu.component.scss35
-rw-r--r--client/src/app/menu/notification.component.scss25
-rw-r--r--client/src/app/modal/welcome-modal.component.scss2
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-details.component.html16
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-details.component.ts2
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.html32
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts42
-rw-r--r--client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts4
-rw-r--r--client/src/app/shared/shared-account-avatar/account-avatar.component.html15
-rw-r--r--client/src/app/shared/shared-account-avatar/account-avatar.component.scss22
-rw-r--r--client/src/app/shared/shared-account-avatar/account-avatar.component.ts39
-rw-r--r--client/src/app/shared/shared-account-avatar/index.ts2
-rw-r--r--client/src/app/shared/shared-account-avatar/shared-account-avatar.module.ts23
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html (renamed from client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html)3
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss (renamed from client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss)10
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts (renamed from client/src/app/shared/shared-actor-image/actor-avatar-edit.component.ts)12
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html (renamed from client/src/app/shared/shared-actor-image/actor-banner-edit.component.html)0
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss (renamed from client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss)0
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts (renamed from client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts)0
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-image-edit.scss (renamed from client/src/app/shared/shared-actor-image/actor-image-edit.scss)0
-rw-r--r--client/src/app/shared/shared-actor-image-edit/index.ts1
-rw-r--r--client/src/app/shared/shared-actor-image-edit/shared-actor-image-edit.module.ts31
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar.component.html19
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar.component.scss101
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar.component.ts111
-rw-r--r--client/src/app/shared/shared-actor-image/shared-actor-image.module.ts14
-rw-r--r--client/src/app/shared/shared-forms/advanced-input-filter.component.html24
-rw-r--r--client/src/app/shared/shared-forms/advanced-input-filter.component.scss10
-rw-r--r--client/src/app/shared/shared-forms/advanced-input-filter.component.ts116
-rw-r--r--client/src/app/shared/shared-forms/index.ts10
-rw-r--r--client/src/app/shared/shared-forms/input-switch.component.scss8
-rw-r--r--client/src/app/shared/shared-forms/markdown-textarea.component.scss25
-rw-r--r--client/src/app/shared/shared-forms/peertube-checkbox.component.scss6
-rw-r--r--client/src/app/shared/shared-forms/preview-upload.component.scss2
-rw-r--r--client/src/app/shared/shared-forms/select/select-shared.component.scss6
-rw-r--r--client/src/app/shared/shared-forms/shared-form.module.ts9
-rw-r--r--client/src/app/shared/shared-forms/timestamp-input.component.scss3
-rw-r--r--client/src/app/shared/shared-instance/instance-about-accordion.component.scss4
-rw-r--r--client/src/app/shared/shared-instance/instance-features-table.component.scss2
-rw-r--r--client/src/app/shared/shared-main/account/account.model.ts11
-rw-r--r--client/src/app/shared/shared-main/account/actor.model.ts1
-rw-r--r--client/src/app/shared/shared-main/buttons/action-dropdown.component.scss13
-rw-r--r--client/src/app/shared/shared-main/buttons/button.component.scss22
-rw-r--r--client/src/app/shared/shared-main/buttons/button.component.ts6
-rw-r--r--client/src/app/shared/shared-main/buttons/delete-button.component.html5
-rw-r--r--client/src/app/shared/shared-main/buttons/delete-button.component.ts1
-rw-r--r--client/src/app/shared/shared-main/buttons/edit-button.component.html5
-rw-r--r--client/src/app/shared/shared-main/buttons/edit-button.component.ts2
-rw-r--r--client/src/app/shared/shared-main/date/date-toggle.component.scss2
-rw-r--r--client/src/app/shared/shared-main/feeds/feed.component.scss6
-rw-r--r--client/src/app/shared/shared-main/loaders/loader.component.scss2
-rw-r--r--client/src/app/shared/shared-main/misc/help.component.scss11
-rw-r--r--client/src/app/shared/shared-main/misc/list-overflow.component.html8
-rw-r--r--client/src/app/shared/shared-main/misc/list-overflow.component.scss6
-rw-r--r--client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss4
-rw-r--r--client/src/app/shared/shared-main/users/user-notification.model.ts4
-rw-r--r--client/src/app/shared/shared-main/users/user-notifications.component.scss11
-rw-r--r--client/src/app/shared/shared-main/users/user-quota.component.scss3
-rw-r--r--client/src/app/shared/shared-main/video-channel/video-channel.model.ts5
-rw-r--r--client/src/app/shared/shared-main/video/video.model.ts3
-rw-r--r--client/src/app/shared/shared-main/video/video.service.ts12
-rw-r--r--client/src/app/shared/shared-moderation/account-blocklist.component.html11
-rw-r--r--client/src/app/shared/shared-moderation/account-blocklist.component.scss11
-rw-r--r--client/src/app/shared/shared-moderation/account-blocklist.component.ts4
-rw-r--r--client/src/app/shared/shared-moderation/moderation.scss39
-rw-r--r--client/src/app/shared/shared-moderation/server-blocklist.component.html14
-rw-r--r--client/src/app/shared/shared-moderation/server-blocklist.component.scss21
-rw-r--r--client/src/app/shared/shared-moderation/server-blocklist.component.ts6
-rw-r--r--client/src/app/shared/shared-moderation/shared-moderation.module.ts4
-rw-r--r--client/src/app/shared/shared-moderation/video-block.component.scss2
-rw-r--r--client/src/app/shared/shared-search/advanced-search.model.ts6
-rw-r--r--client/src/app/shared/shared-thumbnail/video-thumbnail.component.scss10
-rw-r--r--client/src/app/shared/shared-user-settings/user-video-settings.component.html4
-rw-r--r--client/src/app/shared/shared-user-settings/user-video-settings.component.ts69
-rw-r--r--client/src/app/shared/shared-user-subscription/remote-subscribe.component.scss2
-rw-r--r--client/src/app/shared/shared-user-subscription/subscribe-button.component.scss14
-rw-r--r--client/src/app/shared/shared-video-comment/video-comment.service.ts8
-rw-r--r--client/src/app/shared/shared-video-miniature/abstract-video-list.scss17
-rw-r--r--client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts4
-rw-r--r--client/src/app/shared/shared-video-miniature/video-download.component.scss4
-rw-r--r--client/src/app/shared/shared-video-miniature/video-miniature.component.html13
-rw-r--r--client/src/app/shared/shared-video-miniature/video-miniature.component.scss11
-rw-r--r--client/src/app/shared/shared-video-miniature/video-miniature.component.ts11
-rw-r--r--client/src/app/shared/shared-video-miniature/videos-selection.component.html4
-rw-r--r--client/src/app/shared/shared-video-miniature/videos-selection.component.ts3
-rw-r--r--client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss12
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist.model.ts4
213 files changed, 1397 insertions, 1365 deletions
diff --git a/client/src/app/+about/about-follows/about-follows.component.html b/client/src/app/+about/about-follows/about-follows.component.html
index e9139b503..f81465f88 100644
--- a/client/src/app/+about/about-follows/about-follows.component.html
+++ b/client/src/app/+about/about-follows/about-follows.component.html
@@ -21,7 +21,7 @@
21 {{ following }} 21 {{ following }}
22 </a> 22 </a>
23 23
24 <button i18n class="showMore" *ngIf="!loadedAllFollowings && canLoadMoreFollowings()" (click)="loadAllFollowings()">Show full list</button> 24 <button i18n class="show-more" *ngIf="!loadedAllFollowings && canLoadMoreFollowings()" (click)="loadAllFollowings()">Show full list</button>
25 </div> 25 </div>
26 26
27</div> 27</div>
diff --git a/client/src/app/+about/about-follows/about-follows.component.scss b/client/src/app/+about/about-follows/about-follows.component.scss
index a393c9d92..83241e727 100644
--- a/client/src/app/+about/about-follows/about-follows.component.scss
+++ b/client/src/app/+about/about-follows/about-follows.component.scss
@@ -17,9 +17,9 @@ a {
17 justify-content: flex-start; 17 justify-content: flex-start;
18} 18}
19 19
20.showMore { 20.show-more {
21 @include peertube-button-link; 21 @include peertube-button-link;
22 @include grey-button; 22 @include grey-button;
23 23
24 margin-top: 1%; 24 margin-top: 1%;
25} 25}
diff --git a/client/src/app/+about/about-instance/about-instance.component.scss b/client/src/app/+about/about-instance/about-instance.component.scss
index 7158a3a79..9886bdfef 100644
--- a/client/src/app/+about/about-instance/about-instance.component.scss
+++ b/client/src/app/+about/about-instance/about-instance.component.scss
@@ -63,7 +63,8 @@
63 63
64 position: relative; 64 position: relative;
65 65
66 &:hover, &:active { 66 &:hover,
67 &:active {
67 &::after { 68 &::after {
68 content: '#'; 69 content: '#';
69 display: inline-block; 70 display: inline-block;
@@ -71,7 +72,8 @@
71 } 72 }
72 } 73 }
73 74
74 .middle-title, .section-title { 75 .middle-title,
76 .section-title {
75 display: inline-block; 77 display: inline-block;
76 } 78 }
77 79
diff --git a/client/src/app/+about/about-peertube/about-peertube.component.scss b/client/src/app/+about/about-peertube/about-peertube.component.scss
index e67252410..e5d2bc5b8 100644
--- a/client/src/app/+about/about-peertube/about-peertube.component.scss
+++ b/client/src/app/+about/about-peertube/about-peertube.component.scss
@@ -45,7 +45,8 @@
45.p2p-privacy, 45.p2p-privacy,
46my-about-peertube-contributors { 46my-about-peertube-contributors {
47 ::ng-deep { 47 ::ng-deep {
48 p, li { 48 p,
49 li {
49 font-size: 15px; 50 font-size: 15px;
50 } 51 }
51 } 52 }
diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html
index 19a4b3c9c..922608127 100644
--- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html
+++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html
@@ -8,9 +8,10 @@
8 <div class="channel" *ngFor="let videoChannel of videoChannels"> 8 <div class="channel" *ngFor="let videoChannel of videoChannels">
9 9
10 <div class="channel-avatar-row"> 10 <div class="channel-avatar-row">
11 <a class="avatar-link" [routerLink]="getVideoChannelLink(videoChannel)" i18n-title title="See this video channel"> 11 <my-actor-avatar
12 <img [src]="videoChannel.avatarUrl" alt="Avatar" /> 12 [channel]="videoChannel" [internalHref]="getVideoChannelLink(videoChannel)"
13 </a> 13 i18n-title title="See this video channel"
14 ></my-actor-avatar>
14 15
15 <h2> 16 <h2>
16 <a [routerLink]="getVideoChannelLink(videoChannel)" i18n-title title="See this video channel"> 17 <a [routerLink]="getVideoChannelLink(videoChannel)" i18n-title title="See this video channel">
diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss b/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss
index 7e88802f3..f9d097644 100644
--- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss
+++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss
@@ -27,14 +27,12 @@
27 grid-template-columns: auto auto 1fr; 27 grid-template-columns: auto auto 1fr;
28 grid-template-rows: auto 1fr; 28 grid-template-rows: auto 1fr;
29 29
30 .avatar-link { 30 my-actor-avatar {
31 @include actor-avatar-size(75px);
32
31 grid-column: 1; 33 grid-column: 1;
32 grid-row: 1 / 3; 34 grid-row: 1 / 3;
33 margin-right: 30px; 35 margin-right: 15px;
34 }
35
36 img {
37 @include channel-avatar(75px);
38 } 36 }
39 37
40 a { 38 a {
@@ -67,13 +65,13 @@
67 } 65 }
68 66
69 .description-html { 67 .description-html {
68 @include fade-text(30px, pvar(--channelBackgroundColor));
69
70 grid-column: 2 / 4; 70 grid-column: 2 / 4;
71 grid-row: 2; 71 grid-row: 2;
72 72
73 max-height: 80px; 73 max-height: 80px;
74 font-size: 16px; 74 font-size: 16px;
75
76 @include fade-text(30px, pvar(--channelBackgroundColor));
77 } 75 }
78} 76}
79 77
diff --git a/client/src/app/+accounts/accounts.component.html b/client/src/app/+accounts/accounts.component.html
index ea7a317eb..350c77f1e 100644
--- a/client/src/app/+accounts/accounts.component.html
+++ b/client/src/app/+accounts/accounts.component.html
@@ -2,7 +2,7 @@
2 <div class="account-info"> 2 <div class="account-info">
3 3
4 <div class="account-avatar-row"> 4 <div class="account-avatar-row">
5 <my-account-avatar [account]="account" size="120"></my-account-avatar> 5 <my-actor-avatar class="main-avatar" [account]="account"></my-actor-avatar>
6 6
7 <div> 7 <div>
8 <div class="section-label" i18n>PEERTUBE ACCOUNT</div> 8 <div class="section-label" i18n>PEERTUBE ACCOUNT</div>
diff --git a/client/src/app/+accounts/accounts.component.scss b/client/src/app/+accounts/accounts.component.scss
index 56927dea6..2e99fe8a5 100644
--- a/client/src/app/+accounts/accounts.component.scss
+++ b/client/src/app/+accounts/accounts.component.scss
@@ -40,7 +40,7 @@ my-user-moderation-dropdown,
40} 40}
41 41
42.copy-button { 42.copy-button {
43 border: none; 43 border: 0;
44} 44}
45 45
46.account-info { 46.account-info {
@@ -104,9 +104,9 @@ my-user-moderation-dropdown,
104 } 104 }
105 105
106 .description:not(.expanded) { 106 .description:not(.expanded) {
107 max-height: 70px;
108
109 @include fade-text(30px, pvar(--submenuBackgroundColor)); 107 @include fade-text(30px, pvar(--submenuBackgroundColor));
108
109 max-height: 70px;
110 } 110 }
111 111
112 .show-more { 112 .show-more {
diff --git a/client/src/app/+accounts/accounts.module.ts b/client/src/app/+accounts/accounts.module.ts
index 22cdd0642..1bafc5141 100644
--- a/client/src/app/+accounts/accounts.module.ts
+++ b/client/src/app/+accounts/accounts.module.ts
@@ -10,7 +10,7 @@ import { AccountVideoChannelsComponent } from './account-video-channels/account-
10import { AccountVideosComponent } from './account-videos/account-videos.component' 10import { AccountVideosComponent } from './account-videos/account-videos.component'
11import { AccountsRoutingModule } from './accounts-routing.module' 11import { AccountsRoutingModule } from './accounts-routing.module'
12import { AccountsComponent } from './accounts.component' 12import { AccountsComponent } from './accounts.component'
13import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module' 13import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module'
14 14
15@NgModule({ 15@NgModule({
16 imports: [ 16 imports: [
@@ -22,7 +22,7 @@ import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/share
22 SharedModerationModule, 22 SharedModerationModule,
23 SharedVideoMiniatureModule, 23 SharedVideoMiniatureModule,
24 SharedGlobalIconModule, 24 SharedGlobalIconModule,
25 SharedAccountAvatarModule 25 SharedActorImageModule
26 ], 26 ],
27 27
28 declarations: [ 28 declarations: [
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts
index 8d1c3eadb..45366f9ec 100644
--- a/client/src/app/+admin/admin.module.ts
+++ b/client/src/app/+admin/admin.module.ts
@@ -3,12 +3,13 @@ import { SelectButtonModule } from 'primeng/selectbutton'
3import { TableModule } from 'primeng/table' 3import { TableModule } from 'primeng/table'
4import { NgModule } from '@angular/core' 4import { NgModule } from '@angular/core'
5import { SharedAbuseListModule } from '@app/shared/shared-abuse-list' 5import { SharedAbuseListModule } from '@app/shared/shared-abuse-list'
6import { SharedActorImageModule } from '@app/shared/shared-actor-image' 6import { SharedActorImageEditModule } from '@app/shared/shared-actor-image-edit'
7import { SharedFormModule } from '@app/shared/shared-forms' 7import { SharedFormModule } from '@app/shared/shared-forms'
8import { SharedGlobalIconModule } from '@app/shared/shared-icons' 8import { SharedGlobalIconModule } from '@app/shared/shared-icons'
9import { SharedMainModule } from '@app/shared/shared-main' 9import { SharedMainModule } from '@app/shared/shared-main'
10import { SharedModerationModule } from '@app/shared/shared-moderation' 10import { SharedModerationModule } from '@app/shared/shared-moderation'
11import { SharedVideoCommentModule } from '@app/shared/shared-video-comment' 11import { SharedVideoCommentModule } from '@app/shared/shared-video-comment'
12import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module'
12import { AdminRoutingModule } from './admin-routing.module' 13import { AdminRoutingModule } from './admin-routing.module'
13import { AdminComponent } from './admin.component' 14import { AdminComponent } from './admin.component'
14import { 15import {
@@ -39,7 +40,6 @@ import { JobService, LogsComponent, LogsService, SystemComponent } from './syste
39import { DebugComponent, DebugService } from './system/debug' 40import { DebugComponent, DebugService } from './system/debug'
40import { JobsComponent } from './system/jobs/jobs.component' 41import { JobsComponent } from './system/jobs/jobs.component'
41import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersComponent, UserUpdateComponent } from './users' 42import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersComponent, UserUpdateComponent } from './users'
42import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module'
43 43
44@NgModule({ 44@NgModule({
45 imports: [ 45 imports: [
@@ -51,8 +51,8 @@ import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/share
51 SharedGlobalIconModule, 51 SharedGlobalIconModule,
52 SharedAbuseListModule, 52 SharedAbuseListModule,
53 SharedVideoCommentModule, 53 SharedVideoCommentModule,
54 SharedAccountAvatarModule,
55 SharedActorImageModule, 54 SharedActorImageModule,
55 SharedActorImageEditModule,
56 56
57 TableModule, 57 TableModule,
58 SelectButtonModule, 58 SelectButtonModule,
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
index c12465d45..cc2a98a17 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
@@ -57,7 +57,7 @@ input[type=submit] {
57 display: flex; 57 display: flex;
58 margin-left: auto; 58 margin-left: auto;
59 59
60 & + .form-error { 60 + .form-error {
61 display: inline; 61 display: inline;
62 margin-left: 5px; 62 margin-left: 5px;
63 } 63 }
@@ -84,7 +84,8 @@ textarea {
84} 84}
85 85
86.disabled-checkbox-extra { 86.disabled-checkbox-extra {
87 &, ::ng-deep label { 87 &,
88 ::ng-deep label {
88 opacity: .5; 89 opacity: .5;
89 pointer-events: none; 90 pointer-events: none;
90 } 91 }
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html b/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html
index 35b42e742..cbff26e5a 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html
@@ -104,7 +104,7 @@
104 <my-help> 104 <my-help>
105 <ng-template ptTemplate="customHtml"> 105 <ng-template ptTemplate="customHtml">
106 <ng-container i18n> 106 <ng-container i18n>
107 With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video. 107 With <strong>Hide</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video.
108 </ng-container> 108 </ng-container>
109 </ng-template> 109 </ng-template>
110 </my-help> 110 </my-help>
@@ -112,7 +112,7 @@
112 <div class="peertube-select-container"> 112 <div class="peertube-select-container">
113 <select id="instanceDefaultNSFWPolicy" formControlName="defaultNSFWPolicy" class="form-control"> 113 <select id="instanceDefaultNSFWPolicy" formControlName="defaultNSFWPolicy" class="form-control">
114 <option i18n value="undefined" disabled>Policy for sensitive videos</option> 114 <option i18n value="undefined" disabled>Policy for sensitive videos</option>
115 <option i18n value="do_not_list">Do not list</option> 115 <option i18n value="do_not_list">Hide</option>
116 <option i18n value="blur">Blur thumbnails</option> 116 <option i18n value="blur">Blur thumbnails</option>
117 <option i18n value="display">Display</option> 117 <option i18n value="display">Display</option>
118 </select> 118 </select>
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.html b/client/src/app/+admin/follows/followers-list/followers-list.component.html
index 633de9677..c2e9a4df6 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.html
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.html
@@ -4,20 +4,16 @@
4</h1> 4</h1>
5 5
6<p-table 6<p-table
7 [value]="followers" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 7 [value]="followers" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
8 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)" 8 [sortField]="sort.field" [sortOrder]="sort.order" (onPage)="onPage($event)"
9 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
9 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 10 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
10 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} followers" 11 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} followers"
11> 12>
12 <ng-template pTemplate="caption"> 13 <ng-template pTemplate="caption">
13 <div class="caption"> 14 <div class="caption">
14 <div class="ml-auto has-feedback has-clear"> 15 <div class="ml-auto">
15 <input 16 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
16 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
17 (keyup)="onSearch($event)"
18 >
19 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
20 <span class="sr-only" i18n>Clear filters</span>
21 </div> 17 </div>
22 </div> 18 </div>
23 </ng-template> 19 </ng-template>
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.scss b/client/src/app/+admin/follows/followers-list/followers-list.component.scss
index f2d752eb5..35f38aae0 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.scss
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.scss
@@ -1,19 +1,12 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4.caption {
5 justify-content: flex-end;
6
7 input {
8 @include peertube-input-text(250px);
9 }
10}
11
12a { 4a {
13 @include disable-default-a-behaviour; 5 @include disable-default-a-behaviour;
14 display: inline-block; 6 display: inline-block;
15 7
16 &, &:hover { 8 &,
9 &:hover {
17 color: pvar(--mainForegroundColor); 10 color: pvar(--mainForegroundColor);
18 } 11 }
19 12
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.ts b/client/src/app/+admin/follows/followers-list/followers-list.component.ts
index 904e3c338..4a312f6aa 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.ts
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.ts
@@ -59,7 +59,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
59 const handle = follow.follower.name + '@' + follow.follower.host 59 const handle = follow.follower.name + '@' + follow.follower.host
60 this.notifier.success($localize`${handle} rejected from instance followers`) 60 this.notifier.success($localize`${handle} rejected from instance followers`)
61 61
62 this.loadData() 62 this.reloadData()
63 }, 63 },
64 64
65 err => { 65 err => {
@@ -80,14 +80,14 @@ export class FollowersListComponent extends RestTable implements OnInit {
80 const handle = follow.follower.name + '@' + follow.follower.host 80 const handle = follow.follower.name + '@' + follow.follower.host
81 this.notifier.success($localize`${handle} removed from instance followers`) 81 this.notifier.success($localize`${handle} removed from instance followers`)
82 82
83 this.loadData() 83 this.reloadData()
84 }, 84 },
85 85
86 err => this.notifier.error(err.message) 86 err => this.notifier.error(err.message)
87 ) 87 )
88 } 88 }
89 89
90 protected loadData () { 90 protected reloadData () {
91 this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search }) 91 this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search })
92 .subscribe( 92 .subscribe(
93 resultList => { 93 resultList => {
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.html b/client/src/app/+admin/follows/following-list/following-list.component.html
index f4e6a60fe..e7c0c9088 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.html
+++ b/client/src/app/+admin/follows/following-list/following-list.component.html
@@ -4,8 +4,9 @@
4</h1> 4</h1>
5 5
6<p-table 6<p-table
7 [value]="following" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 7 [value]="following" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
8 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)" 8 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)"
9 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
9 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 10 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
10 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} hosts" 11 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} hosts"
11> 12>
@@ -18,13 +19,8 @@
18 </a> 19 </a>
19 </div> 20 </div>
20 21
21 <div class="ml-auto has-feedback has-clear"> 22 <div class="ml-auto">
22 <input 23 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
23 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
24 (keyup)="onSearch($event)"
25 >
26 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
27 <span class="sr-only" i18n>Clear filters</span>
28 </div> 24 </div>
29 </div> 25 </div>
30 </ng-template> 26 </ng-template>
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.scss b/client/src/app/+admin/follows/following-list/following-list.component.scss
index b108218b8..9474b0a23 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.scss
+++ b/client/src/app/+admin/follows/following-list/following-list.component.scss
@@ -5,7 +5,8 @@ a {
5 @include disable-default-a-behaviour; 5 @include disable-default-a-behaviour;
6 display: inline-block; 6 display: inline-block;
7 7
8 &, &:hover { 8 &,
9 &:hover {
9 color: pvar(--mainForegroundColor); 10 color: pvar(--mainForegroundColor);
10 } 11 }
11 12
@@ -15,14 +16,6 @@ a {
15 } 16 }
16} 17}
17 18
18.caption {
19 justify-content: flex-end;
20
21 input {
22 @include peertube-input-text(250px);
23 }
24}
25
26.follow-button { 19.follow-button {
27 @include create-button; 20 @include create-button;
28} 21}
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts
index f34490cc8..b63fe08c0 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.ts
+++ b/client/src/app/+admin/follows/following-list/following-list.component.ts
@@ -45,7 +45,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
45 this.followService.follow(hosts).subscribe( 45 this.followService.follow(hosts).subscribe(
46 () => { 46 () => {
47 this.notifier.success($localize`Follow request(s) sent!`) 47 this.notifier.success($localize`Follow request(s) sent!`)
48 this.loadData() 48 this.reloadData()
49 }, 49 },
50 50
51 err => this.notifier.error(err.message) 51 err => this.notifier.error(err.message)
@@ -62,14 +62,14 @@ export class FollowingListComponent extends RestTable implements OnInit {
62 this.followService.unfollow(follow).subscribe( 62 this.followService.unfollow(follow).subscribe(
63 () => { 63 () => {
64 this.notifier.success($localize`You are not following ${follow.following.host} anymore.`) 64 this.notifier.success($localize`You are not following ${follow.following.host} anymore.`)
65 this.loadData() 65 this.reloadData()
66 }, 66 },
67 67
68 err => this.notifier.error(err.message) 68 err => this.notifier.error(err.message)
69 ) 69 )
70 } 70 }
71 71
72 protected loadData () { 72 protected reloadData () {
73 this.followService.getFollowing({ pagination: this.pagination, sort: this.sort, search: this.search }) 73 this.followService.getFollowing({ pagination: this.pagination, sort: this.sort, search: this.search })
74 .subscribe( 74 .subscribe(
75 resultList => { 75 resultList => {
diff --git a/client/src/app/+admin/follows/follows.component.scss b/client/src/app/+admin/follows/follows.component.scss
index 33ff17539..1ed0d925f 100644
--- a/client/src/app/+admin/follows/follows.component.scss
+++ b/client/src/app/+admin/follows/follows.component.scss
@@ -1,4 +1,4 @@
1@import "mixins"; 1@import 'mixins';
2 2
3.form-sub-title { 3.form-sub-title {
4 flex-grow: 0; 4 flex-grow: 0;
diff --git a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.scss b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.scss
index adcf2037e..30b9f2147 100644
--- a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.scss
+++ b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.scss
@@ -5,7 +5,8 @@ a {
5 @include disable-default-a-behaviour; 5 @include disable-default-a-behaviour;
6 display: inline-block; 6 display: inline-block;
7 7
8 &, &:hover { 8 &,
9 &:hover {
9 color: pvar(--mainForegroundColor); 10 color: pvar(--mainForegroundColor);
10 } 11 }
11 12
diff --git a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts
index d6fd1a1ab..3cd65dd6e 100644
--- a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts
+++ b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts
@@ -78,7 +78,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
78 this.pagination.start = 0 78 this.pagination.start = 0
79 this.saveSelectLocalStorage() 79 this.saveSelectLocalStorage()
80 80
81 this.loadData() 81 this.reloadData()
82 } 82 }
83 83
84 getRedundancyStrategy (redundancy: VideoRedundancy) { 84 getRedundancyStrategy (redundancy: VideoRedundancy) {
@@ -145,7 +145,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
145 .subscribe( 145 .subscribe(
146 () => { 146 () => {
147 this.notifier.success($localize`Video redundancies removed!`) 147 this.notifier.success($localize`Video redundancies removed!`)
148 this.loadData() 148 this.reloadData()
149 }, 149 },
150 150
151 err => this.notifier.error(err.message) 151 err => this.notifier.error(err.message)
@@ -153,7 +153,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
153 153
154 } 154 }
155 155
156 protected loadData () { 156 protected reloadData () {
157 const options = { 157 const options = {
158 pagination: this.pagination, 158 pagination: this.pagination,
159 sort: this.sort, 159 sort: this.sort,
diff --git a/client/src/app/+admin/moderation/abuse-list/abuse-list.component.html b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.html
index 9a6c124e1..a9e0931f8 100644
--- a/client/src/app/+admin/moderation/abuse-list/abuse-list.component.html
+++ b/client/src/app/+admin/moderation/abuse-list/abuse-list.component.html
@@ -3,4 +3,4 @@
3 <ng-container i18n>Reports</ng-container> 3 <ng-container i18n>Reports</ng-container>
4</h1> 4</h1>
5 5
6<my-abuse-list-table viewType="admin" baseRoute="/admin/moderation/abuses/list"></my-abuse-list-table> 6<my-abuse-list-table viewType="admin"></my-abuse-list-table>
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html
index f5cf93adb..e3a3a8320 100644
--- a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html
+++ b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html
@@ -1,18 +1,14 @@
1<p-table 1<p-table
2 [value]="blockedAccounts" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 2 [value]="blockedAccounts" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
3 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)" 3 [sortField]="sort.field" [sortOrder]="sort.order" (onPage)="onPage($event)"
4 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
4 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 5 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
5 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted accounts" 6 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted accounts"
6> 7>
7 <ng-template pTemplate="caption"> 8 <ng-template pTemplate="caption">
8 <div class="caption"> 9 <div class="caption">
9 <div class="ml-auto has-feedback has-clear"> 10 <div class="ml-auto">
10 <input 11 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
11 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
12 (keyup)="onSearch($event)"
13 >
14 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
15 <span class="sr-only" i18n>Clear filters</span>
16 </div> 12 </div>
17 </div> 13 </div>
18 </ng-template> 14 </ng-template>
@@ -34,7 +30,7 @@
34 <td> 30 <td>
35 <a [href]="accountBlock.blockedAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> 31 <a [href]="accountBlock.blockedAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
36 <div class="chip two-lines"> 32 <div class="chip two-lines">
37 <my-account-avatar [account]="accountBlock.blockedAccount"></my-account-avatar> 33 <my-actor-avatar [account]="accountBlock.blockedAccount"></my-actor-avatar>
38 <div> 34 <div>
39 {{ accountBlock.blockedAccount.displayName }} 35 {{ accountBlock.blockedAccount.displayName }}
40 <span class="text-muted">{{ accountBlock.blockedAccount.nameWithHost }}</span> 36 <span class="text-muted">{{ accountBlock.blockedAccount.nameWithHost }}</span>
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss
index 6028b75ea..1d98e44d9 100644
--- a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss
+++ b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss
@@ -4,4 +4,4 @@
4.unblock-button { 4.unblock-button {
5 @include peertube-button; 5 @include peertube-button;
6 @include grey-button; 6 @include grey-button;
7} \ No newline at end of file 7}
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html
index c7275de1b..d89c8f244 100644
--- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html
@@ -4,8 +4,9 @@
4</h1> 4</h1>
5 5
6<p-table 6<p-table
7 [value]="blocklist" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 7 [value]="blocklist" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
8 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" 8 [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id"
9 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
9 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 10 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
10 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} blocked videos" 11 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} blocked videos"
11 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" 12 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
@@ -13,25 +14,7 @@
13 <ng-template pTemplate="caption"> 14 <ng-template pTemplate="caption">
14 <div class="caption"> 15 <div class="caption">
15 <div class="ml-auto"> 16 <div class="ml-auto">
16 <div class="input-group has-feedback has-clear"> 17 <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter>
17 <div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
18 <div class="input-group-text" ngbDropdownToggle>
19 <span class="caret" aria-haspopup="menu" role="button"></span>
20 </div>
21
22 <div role="menu" ngbDropdownMenu>
23 <h6 class="dropdown-header" i18n>Advanced block filters</h6>
24 <a [routerLink]="[ '/admin/moderation/video-blocks/list' ]" [queryParams]="{ 'search': 'type:auto' }" class="dropdown-item" i18n>Automatic blocks</a>
25 <a [routerLink]="[ '/admin/moderation/video-blocks/list' ]" [queryParams]="{ 'search': 'type:manual' }" class="dropdown-item" i18n>Manual blocks</a>
26 </div>
27 </div>
28 <input
29 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
30 (keyup)="onSearch($event)"
31 >
32 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetTableFilter()"></a>
33 <span class="sr-only" i18n>Clear filters</span>
34 </div>
35 </div> 18 </div>
36 </div> 19 </div>
37 </ng-template> 20 </ng-template>
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss
index b67e33cc1..068aa2aee 100644
--- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.scss
@@ -5,23 +5,6 @@ my-global-icon {
5 height: 24px; 5 height: 24px;
6} 6}
7 7
8.input-group {
9 @include peertube-input-group(300px);
10
11 .dropdown-toggle::after {
12 margin-left: 0;
13 }
14}
15
16.caption {
17 justify-content: flex-end;
18
19 input {
20 @include peertube-input-text(250px);
21 flex-grow: 1;
22 }
23}
24
25.badge { 8.badge {
26 @include table-badge; 9 @include table-badge;
27} 10}
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
index d6aca10e7..498d8321a 100644
--- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
@@ -2,10 +2,11 @@ import { SortMeta } from 'primeng/api'
2import { switchMap } from 'rxjs/operators' 2import { switchMap } from 'rxjs/operators'
3import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' 3import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
4import { environment } from 'src/environments/environment' 4import { environment } from 'src/environments/environment'
5import { AfterViewInit, Component, OnInit } from '@angular/core' 5import { Component, OnInit } from '@angular/core'
6import { DomSanitizer } from '@angular/platform-browser' 6import { DomSanitizer } from '@angular/platform-browser'
7import { ActivatedRoute, Params, Router } from '@angular/router' 7import { ActivatedRoute, Router } from '@angular/router'
8import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' 8import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
9import { AdvancedInputFilter } from '@app/shared/shared-forms'
9import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' 10import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
10import { VideoBlockService } from '@app/shared/shared-moderation' 11import { VideoBlockService } from '@app/shared/shared-moderation'
11import { VideoBlacklist, VideoBlacklistType } from '@shared/models' 12import { VideoBlacklist, VideoBlacklistType } from '@shared/models'
@@ -15,7 +16,7 @@ import { VideoBlacklist, VideoBlacklistType } from '@shared/models'
15 templateUrl: './video-block-list.component.html', 16 templateUrl: './video-block-list.component.html',
16 styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-block-list.component.scss' ] 17 styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-block-list.component.scss' ]
17}) 18})
18export class VideoBlockListComponent extends RestTable implements OnInit, AfterViewInit { 19export class VideoBlockListComponent extends RestTable implements OnInit {
19 blocklist: (VideoBlacklist & { reasonHtml?: string, embedHtml?: string })[] = [] 20 blocklist: (VideoBlacklist & { reasonHtml?: string, embedHtml?: string })[] = []
20 totalRecords = 0 21 totalRecords = 0
21 sort: SortMeta = { field: 'createdAt', order: -1 } 22 sort: SortMeta = { field: 'createdAt', order: -1 }
@@ -24,6 +25,17 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
24 25
25 videoBlocklistActions: DropdownAction<VideoBlacklist>[][] = [] 26 videoBlocklistActions: DropdownAction<VideoBlacklist>[][] = []
26 27
28 inputFilters: AdvancedInputFilter[] = [
29 {
30 queryParams: { 'search': 'type:auto' },
31 label: $localize`Automatic blocks`
32 },
33 {
34 queryParams: { 'search': 'type:manual' },
35 label: $localize`Manual blocks`
36 }
37 ]
38
27 constructor ( 39 constructor (
28 protected route: ActivatedRoute, 40 protected route: ActivatedRoute,
29 protected router: Router, 41 protected router: Router,
@@ -52,7 +64,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
52 ).subscribe( 64 ).subscribe(
53 () => { 65 () => {
54 this.notifier.success($localize`Video ${videoBlock.video.name} switched to manual block.`) 66 this.notifier.success($localize`Video ${videoBlock.video.name} switched to manual block.`)
55 this.loadData() 67 this.reloadData()
56 }, 68 },
57 69
58 err => this.notifier.error(err.message) 70 err => this.notifier.error(err.message)
@@ -104,31 +116,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
104 }) 116 })
105 117
106 this.initialize() 118 this.initialize()
107 this.listenToSearchChange()
108 }
109
110 ngAfterViewInit () {
111 if (this.search) this.setTableFilter(this.search, false)
112 }
113
114 /* Table filter functions */
115 onBlockSearch (event: Event) {
116 this.onSearch(event)
117 this.setQueryParams((event.target as HTMLInputElement).value)
118 }
119
120 setQueryParams (search: string) {
121 const queryParams: Params = {}
122 if (search) Object.assign(queryParams, { search })
123 this.router.navigate([ '/admin/moderation/video-blocks/list' ], { queryParams })
124 }
125
126 resetTableFilter () {
127 this.setTableFilter('')
128 this.setQueryParams('')
129 this.resetSearch()
130 } 119 }
131 /* END Table filter functions */
132 120
133 getIdentifier () { 121 getIdentifier () {
134 return 'VideoBlockListComponent' 122 return 'VideoBlockListComponent'
@@ -151,7 +139,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
151 this.videoBlocklistService.unblockVideo(entry.video.id).subscribe( 139 this.videoBlocklistService.unblockVideo(entry.video.id).subscribe(
152 () => { 140 () => {
153 this.notifier.success($localize`Video ${entry.video.name} unblocked.`) 141 this.notifier.success($localize`Video ${entry.video.name} unblocked.`)
154 this.loadData() 142 this.reloadData()
155 }, 143 },
156 144
157 err => this.notifier.error(err.message) 145 err => this.notifier.error(err.message)
@@ -169,7 +157,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
169 ) 157 )
170 } 158 }
171 159
172 protected loadData () { 160 protected reloadData () {
173 this.videoBlocklistService.listBlocks({ 161 this.videoBlocklistService.listBlocks({
174 pagination: this.pagination, 162 pagination: this.pagination,
175 sort: this.sort, 163 sort: this.sort,
diff --git a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html
index d360c3c51..9d9283536 100644
--- a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html
+++ b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html
@@ -8,8 +8,9 @@
8<em i18n>This view also shows comments from muted accounts.</em> 8<em i18n>This view also shows comments from muted accounts.</em>
9 9
10<p-table 10<p-table
11 [value]="comments" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 11 [value]="comments" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
12 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" 12 [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id"
13 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
13 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 14 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
14 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} comments" 15 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} comments"
15 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" 16 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
@@ -26,25 +27,7 @@
26 </div> 27 </div>
27 28
28 <div class="ml-auto"> 29 <div class="ml-auto">
29 <div class="input-group has-feedback has-clear"> 30 <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter>
30 <div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
31 <div class="input-group-text" ngbDropdownToggle>
32 <span class="caret" aria-haspopup="menu" role="button"></span>
33 </div>
34
35 <div role="menu" ngbDropdownMenu>
36 <h6 class="dropdown-header" i18n>Advanced comments filters</h6>
37 <a [routerLink]="[ '/admin/moderation/video-comments/list' ]" [queryParams]="{ 'search': 'local:true' }" class="dropdown-item" i18n>Local comments</a>
38 <a [routerLink]="[ '/admin/moderation/video-comments/list' ]" [queryParams]="{ 'search': 'local:false' }" class="dropdown-item" i18n>Remote comments</a>
39 </div>
40 </div>
41 <input
42 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
43 (keyup)="onSearch($event)"
44 >
45 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetTableFilter()"></a>
46 <span class="sr-only" i18n>Clear filters</span>
47 </div>
48 </div> 31 </div>
49 </div> 32 </div>
50 </ng-template> 33 </ng-template>
@@ -86,7 +69,7 @@
86 <td> 69 <td>
87 <a [href]="videoComment.account.localUrl" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> 70 <a [href]="videoComment.account.localUrl" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
88 <div class="chip two-lines"> 71 <div class="chip two-lines">
89 <my-account-avatar [account]="videoComment.account"></my-account-avatar> 72 <my-actor-avatar [account]="videoComment.account"></my-actor-avatar>
90 <div> 73 <div>
91 {{ videoComment.account.displayName }} 74 {{ videoComment.account.displayName }}
92 <span>{{ videoComment.by }}</span> 75 <span>{{ videoComment.by }}</span>
diff --git a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss
index c9262da09..a6f931e3b 100644
--- a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss
+++ b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss
@@ -11,23 +11,6 @@ my-global-icon {
11 height: 24px; 11 height: 24px;
12} 12}
13 13
14.input-group {
15 @include peertube-input-group(300px);
16
17 .dropdown-toggle::after {
18 margin-left: 0;
19 }
20}
21
22.caption {
23 justify-content: flex-end;
24
25 input {
26 @include peertube-input-text(250px);
27 flex-grow: 1;
28 }
29}
30
31.video { 14.video {
32 display: flex; 15 display: flex;
33 flex-direction: column; 16 flex-direction: column;
@@ -49,7 +32,8 @@ my-global-icon {
49 max-height: 22px; 32 max-height: 22px;
50 } 33 }
51 34
52 div, p { 35 div,
36 p {
53 @include ellipsis; 37 @include ellipsis;
54 } 38 }
55 39
diff --git a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts
index 63493d00d..e2ae993b0 100644
--- a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts
+++ b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts
@@ -2,6 +2,7 @@ import { SortMeta } from 'primeng/api'
2import { AfterViewInit, Component, OnInit } from '@angular/core' 2import { AfterViewInit, Component, OnInit } from '@angular/core'
3import { ActivatedRoute, Router } from '@angular/router' 3import { ActivatedRoute, Router } from '@angular/router'
4import { AuthService, ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' 4import { AuthService, ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core'
5import { AdvancedInputFilter } from '@app/shared/shared-forms'
5import { DropdownAction } from '@app/shared/shared-main' 6import { DropdownAction } from '@app/shared/shared-main'
6import { BulkService } from '@app/shared/shared-moderation' 7import { BulkService } from '@app/shared/shared-moderation'
7import { VideoCommentAdmin, VideoCommentService } from '@app/shared/shared-video-comment' 8import { VideoCommentAdmin, VideoCommentService } from '@app/shared/shared-video-comment'
@@ -12,9 +13,7 @@ import { FeedFormat, UserRight } from '@shared/models'
12 templateUrl: './video-comment-list.component.html', 13 templateUrl: './video-comment-list.component.html',
13 styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-comment-list.component.scss' ] 14 styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-comment-list.component.scss' ]
14}) 15})
15export class VideoCommentListComponent extends RestTable implements OnInit, AfterViewInit { 16export class VideoCommentListComponent extends RestTable implements OnInit {
16 baseRoute = '/admin/moderation/video-comments/list'
17
18 comments: VideoCommentAdmin[] 17 comments: VideoCommentAdmin[]
19 totalRecords = 0 18 totalRecords = 0
20 sort: SortMeta = { field: 'createdAt', order: -1 } 19 sort: SortMeta = { field: 'createdAt', order: -1 }
@@ -43,6 +42,17 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte
43 selectedComments: VideoCommentAdmin[] = [] 42 selectedComments: VideoCommentAdmin[] = []
44 bulkCommentActions: DropdownAction<VideoCommentAdmin[]>[] = [] 43 bulkCommentActions: DropdownAction<VideoCommentAdmin[]>[] = []
45 44
45 inputFilters: AdvancedInputFilter[] = [
46 {
47 queryParams: { 'search': 'local:true' },
48 label: $localize`Local comments`
49 },
50 {
51 queryParams: { 'search': 'local:false' },
52 label: $localize`Remote comments`
53 }
54 ]
55
46 get authUser () { 56 get authUser () {
47 return this.auth.getUser() 57 return this.auth.getUser()
48 } 58 }
@@ -79,7 +89,6 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte
79 89
80 ngOnInit () { 90 ngOnInit () {
81 this.initialize() 91 this.initialize()
82 this.listenToSearchChange()
83 92
84 this.bulkCommentActions = [ 93 this.bulkCommentActions = [
85 { 94 {
@@ -91,10 +100,6 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte
91 ] 100 ]
92 } 101 }
93 102
94 ngAfterViewInit () {
95 if (this.search) this.setTableFilter(this.search, false)
96 }
97
98 getIdentifier () { 103 getIdentifier () {
99 return 'VideoCommentListComponent' 104 return 'VideoCommentListComponent'
100 } 105 }
@@ -107,7 +112,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte
107 return this.selectedComments.length !== 0 112 return this.selectedComments.length !== 0
108 } 113 }
109 114
110 protected loadData () { 115 protected reloadData () {
111 this.videoCommentService.getAdminVideoComments({ 116 this.videoCommentService.getAdminVideoComments({
112 pagination: this.pagination, 117 pagination: this.pagination,
113 sort: this.sort, 118 sort: this.sort,
@@ -135,7 +140,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte
135 this.videoCommentService.deleteVideoComments(commentArgs).subscribe( 140 this.videoCommentService.deleteVideoComments(commentArgs).subscribe(
136 () => { 141 () => {
137 this.notifier.success($localize`${commentArgs.length} comments deleted.`) 142 this.notifier.success($localize`${commentArgs.length} comments deleted.`)
138 this.loadData() 143 this.reloadData()
139 }, 144 },
140 145
141 err => this.notifier.error(err.message), 146 err => this.notifier.error(err.message),
@@ -147,7 +152,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit, Afte
147 private deleteComment (comment: VideoCommentAdmin) { 152 private deleteComment (comment: VideoCommentAdmin) {
148 this.videoCommentService.deleteVideoComment(comment.video.id, comment.id) 153 this.videoCommentService.deleteVideoComment(comment.video.id, comment.id)
149 .subscribe( 154 .subscribe(
150 () => this.loadData(), 155 () => this.reloadData(),
151 156
152 err => this.notifier.error(err.message) 157 err => this.notifier.error(err.message)
153 ) 158 )
diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.html b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.html
index 9cbec03a1..bc4c2ef88 100644
--- a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.html
+++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.html
@@ -23,13 +23,17 @@
23 </a> 23 </a>
24 24
25 <div class="buttons"> 25 <div class="buttons">
26 <my-edit-button *ngIf="!isTheme(plugin)" [routerLink]="getShowRouterLink(plugin)" label="Settings" i18n-label></my-edit-button> 26 <my-edit-button
27 27 *ngIf="!isTheme(plugin)" [routerLink]="getShowRouterLink(plugin)" label="Settings" i18n-label
28 <my-button class="update-button" *ngIf="isUpdateAvailable(plugin)" (click)="update(plugin)" [loading]="isUpdating(plugin)" 28 [responsiveLabel]="true"
29 [label]="getUpdateLabel(plugin)" icon="refresh" [attr.disabled]="isUpdating(plugin)" 29 ></my-edit-button>
30
31 <my-button
32 class="update-button" *ngIf="isUpdateAvailable(plugin)" (click)="update(plugin)" [loading]="isUpdating(plugin)"
33 [label]="getUpdateLabel(plugin)" icon="refresh" [attr.disabled]="isUpdating(plugin)" [responsiveLabel]="true"
30 ></my-button> 34 ></my-button>
31 35
32 <my-delete-button (click)="uninstall(plugin)" label="Uninstall" i18n-label></my-delete-button> 36 <my-delete-button (click)="uninstall(plugin)" label="Uninstall" i18n-label [responsiveLabel]="true"></my-delete-button>
33 </div> 37 </div>
34 </div> 38 </div>
35 39
diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.scss b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.scss
index 9376e38b0..22d4a59ab 100644
--- a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.scss
+++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.scss
@@ -1,6 +1,6 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4.update-button[disabled="true"] ::ng-deep .action-button { 4.update-button[disabled=true] ::ng-deep .action-button {
5 cursor: default !important; 5 cursor: default !important;
6} 6}
diff --git a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html
index 727633399..6900e8717 100644
--- a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html
+++ b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html
@@ -48,10 +48,15 @@
48 <span *ngIf="plugin.installed" class="badge badge-success">Installed</span> 48 <span *ngIf="plugin.installed" class="badge badge-success">Installed</span>
49 49
50 <div class="buttons"> 50 <div class="buttons">
51 <my-edit-button *ngIf="plugin.installed === true && !isThemeSearch()" [routerLink]="getShowRouterLink(plugin)" label="Settings" i18n-label></my-edit-button> 51 <my-edit-button
52 *ngIf="plugin.installed === true && !isThemeSearch()" [routerLink]="getShowRouterLink(plugin)"
53 label="Settings" i18n-label [responsiveLabel]="true"
54 ></my-edit-button>
52 55
53 <my-button class="update-button" *ngIf="plugin.installed === false" (click)="install(plugin)" [loading]="isInstalling(plugin)" 56 <my-button
54 label="Install" icon="cloud-download" [attr.disabled]="isInstalling(plugin)" 57 class="update-button" *ngIf="plugin.installed === false" (click)="install(plugin)"
58 [loading]="isInstalling(plugin)" label="Install" [responsiveLabel]="true"
59 icon="cloud-download" [attr.disabled]="isInstalling(plugin)"
55 ></my-button> 60 ></my-button>
56 </div> 61 </div>
57 </div> 62 </div>
diff --git a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss
index 5ab6e5f1b..6b7b84e29 100644
--- a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss
+++ b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss
@@ -5,7 +5,8 @@ h2 {
5 margin-bottom: 20px; 5 margin-bottom: 20px;
6} 6}
7 7
8input[type=submit], button { 8input[type=submit],
9button {
9 @include peertube-button; 10 @include peertube-button;
10 @include orange-button; 11 @include orange-button;
11 12
diff --git a/client/src/app/+admin/plugins/plugins.component.scss b/client/src/app/+admin/plugins/plugins.component.scss
deleted file mode 100644
index ce9825f53..000000000
--- a/client/src/app/+admin/plugins/plugins.component.scss
+++ /dev/null
@@ -1,28 +0,0 @@
1@import '_variables';
2@import '_mixins';
3
4@media screen and (max-width: $small-view) {
5 ::ng-deep .plugins .plugin .first-row {
6 flex-wrap: wrap;
7
8 .plugin-name,
9 .plugin-version,
10 .plugin-icon {
11 margin-bottom: 10px;
12 }
13
14 .buttons {
15 my-edit-button,
16 my-delete-button,
17 my-button {
18 .action-button {
19 padding: 0 13px;
20 }
21
22 .button-label {
23 display: none;
24 }
25 }
26 }
27 }
28}
diff --git a/client/src/app/+admin/plugins/plugins.component.ts b/client/src/app/+admin/plugins/plugins.component.ts
index 6ec6fa4a1..de06c0671 100644
--- a/client/src/app/+admin/plugins/plugins.component.ts
+++ b/client/src/app/+admin/plugins/plugins.component.ts
@@ -1,8 +1,7 @@
1import { Component } from '@angular/core' 1import { Component } from '@angular/core'
2 2
3@Component({ 3@Component({
4 templateUrl: './plugins.component.html', 4 templateUrl: './plugins.component.html'
5 styleUrls: [ './plugins.component.scss' ]
6}) 5})
7export class PluginsComponent { 6export class PluginsComponent {
8} 7}
diff --git a/client/src/app/+admin/plugins/shared/plugin-list.component.scss b/client/src/app/+admin/plugins/shared/plugin-list.component.scss
index f59a01b74..47cb1e6e5 100644
--- a/client/src/app/+admin/plugins/shared/plugin-list.component.scss
+++ b/client/src/app/+admin/plugins/shared/plugin-list.component.scss
@@ -27,7 +27,7 @@
27 my-global-icon { 27 my-global-icon {
28 @include apply-svg-color(pvar(--greyForegroundColor)); 28 @include apply-svg-color(pvar(--greyForegroundColor));
29 29
30 &[iconName="npm"] { 30 &[iconName=npm] {
31 @include fill-svg-color(pvar(--greyForegroundColor)); 31 @include fill-svg-color(pvar(--greyForegroundColor));
32 } 32 }
33 } 33 }
@@ -36,6 +36,7 @@
36 .buttons { 36 .buttons {
37 margin-left: auto; 37 margin-left: auto;
38 width: max-content; 38 width: max-content;
39
39 > *:not(:last-child) { 40 > *:not(:last-child) {
40 margin-right: 10px; 41 margin-right: 10px;
41 } 42 }
@@ -49,7 +50,7 @@
49 justify-content: space-between; 50 justify-content: space-between;
50 51
51 .description { 52 .description {
52 opacity: 0.8 53 opacity: 0.8;
53 } 54 }
54} 55}
55 56
@@ -57,3 +58,14 @@
57 @include peertube-button-link; 58 @include peertube-button-link;
58 @include button-with-icon(21px, 0, -2px); 59 @include button-with-icon(21px, 0, -2px);
59} 60}
61
62@media screen and (max-width: $small-view) {
63 .first-row {
64 flex-wrap: wrap;
65
66 .buttons {
67 flex-basis: 100%;
68 margin-top: 10px;
69 }
70 }
71}
diff --git a/client/src/app/+admin/system/jobs/jobs.component.scss b/client/src/app/+admin/system/jobs/jobs.component.scss
index 7c6159420..65ee6ec5f 100644
--- a/client/src/app/+admin/system/jobs/jobs.component.scss
+++ b/client/src/app/+admin/system/jobs/jobs.component.scss
@@ -51,7 +51,7 @@ pre {
51} 51}
52 52
53.job-error { 53.job-error {
54 color: red; 54 color: #ff0000;
55} 55}
56 56
57.badge { 57.badge {
diff --git a/client/src/app/+admin/system/jobs/jobs.component.ts b/client/src/app/+admin/system/jobs/jobs.component.ts
index 43578eedd..29ba95c5c 100644
--- a/client/src/app/+admin/system/jobs/jobs.component.ts
+++ b/client/src/app/+admin/system/jobs/jobs.component.ts
@@ -86,7 +86,7 @@ export class JobsComponent extends RestTable implements OnInit {
86 onJobStateOrTypeChanged () { 86 onJobStateOrTypeChanged () {
87 this.pagination.start = 0 87 this.pagination.start = 0
88 88
89 this.loadData() 89 this.reloadData()
90 this.saveJobStateAndType() 90 this.saveJobStateAndType()
91 } 91 }
92 92
@@ -104,10 +104,10 @@ export class JobsComponent extends RestTable implements OnInit {
104 this.jobs = [] 104 this.jobs = []
105 this.totalRecords = 0 105 this.totalRecords = 0
106 106
107 this.loadData() 107 this.reloadData()
108 } 108 }
109 109
110 protected loadData () { 110 protected reloadData () {
111 let jobState = this.jobState as JobState 111 let jobState = this.jobState as JobState
112 if (this.jobState === 'all') jobState = null 112 if (this.jobState === 'all') jobState = null
113 113
diff --git a/client/src/app/+admin/system/logs/logs.component.scss b/client/src/app/+admin/system/logs/logs.component.scss
index 587a9795c..1a7c3be75 100644
--- a/client/src/app/+admin/system/logs/logs.component.scss
+++ b/client/src/app/+admin/system/logs/logs.component.scss
@@ -66,7 +66,7 @@
66 ng-select, 66 ng-select,
67 my-button { 67 my-button {
68 width: 100% !important; 68 width: 100% !important;
69 margin-left: 0px !important; 69 margin-left: 0 !important;
70 margin-bottom: 10px !important; 70 margin-bottom: 10px !important;
71 } 71 }
72 72
@@ -85,7 +85,7 @@
85 ng-select, 85 ng-select,
86 my-button { 86 my-button {
87 width: 100% !important; 87 width: 100% !important;
88 margin-left: 0px !important; 88 margin-left: 0 !important;
89 margin-bottom: 10px !important; 89 margin-bottom: 10px !important;
90 } 90 }
91 91
diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.scss b/client/src/app/+admin/users/user-edit/user-edit.component.scss
index 8b0ac8783..145177fb9 100644
--- a/client/src/app/+admin/users/user-edit/user-edit.component.scss
+++ b/client/src/app/+admin/users/user-edit/user-edit.component.scss
@@ -37,7 +37,8 @@ my-select-custom-value {
37 display: block; 37 display: block;
38} 38}
39 39
40input[type=submit], button { 40input[type=submit],
41button {
41 @include peertube-button; 42 @include peertube-button;
42 @include orange-button; 43 @include orange-button;
43 44
diff --git a/client/src/app/+admin/users/user-edit/user-password.component.scss b/client/src/app/+admin/users/user-edit/user-password.component.scss
index 1f0d49227..66d15ee9c 100644
--- a/client/src/app/+admin/users/user-edit/user-password.component.scss
+++ b/client/src/app/+admin/users/user-edit/user-password.component.scss
@@ -7,7 +7,7 @@ input:not([type=submit]):not([type=checkbox]) {
7 display: block; 7 display: block;
8 border-top-right-radius: 0; 8 border-top-right-radius: 0;
9 border-bottom-right-radius: 0; 9 border-bottom-right-radius: 0;
10 border-right: none; 10 border-right: 0;
11} 11}
12 12
13input[type=submit] { 13input[type=submit] {
diff --git a/client/src/app/+admin/users/user-list/user-list.component.html b/client/src/app/+admin/users/user-list/user-list.component.html
index e114f3425..44d8a7e87 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.html
+++ b/client/src/app/+admin/users/user-list/user-list.component.html
@@ -1,7 +1,7 @@
1<p-table 1<p-table
2 [value]="users" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 2 [value]="users" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
3 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" [resizableColumns]="true" 3 [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true" [(selection)]="selectedUsers"
4 [(selection)]="selectedUsers" 4 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
5 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 5 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
6 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} users" 6 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} users"
7 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" 7 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
@@ -22,24 +22,7 @@
22 </div> 22 </div>
23 23
24 <div class="ml-auto"> 24 <div class="ml-auto">
25 <div class="input-group has-feedback has-clear"> 25 <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter>
26 <div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
27 <div class="input-group-text" ngbDropdownToggle>
28 <span class="caret" aria-haspopup="menu" role="button"></span>
29 </div>
30
31 <div role="menu" ngbDropdownMenu>
32 <h6 class="dropdown-header" i18n>Advanced user filters</h6>
33 <a [routerLink]="[ '/admin/users/list' ]" [queryParams]="{ 'search': 'banned:true' }" class="dropdown-item" i18n>Banned users</a>
34 </div>
35 </div>
36 <input
37 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
38 (keyup)="onSearch($event)"
39 >
40 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetTableFilter()"></a>
41 <span class="sr-only" i18n>Clear filters</span>
42 </div>
43 </div> 26 </div>
44 27
45 </div> 28 </div>
@@ -106,7 +89,7 @@
106 <td *ngIf="isSelected('username')"> 89 <td *ngIf="isSelected('username')">
107 <a i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer" [routerLink]="[ '/accounts/' + user.username ]"> 90 <a i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer" [routerLink]="[ '/accounts/' + user.username ]">
108 <div class="chip two-lines"> 91 <div class="chip two-lines">
109 <my-account-avatar [account]="user?.account"></my-account-avatar> 92 <my-actor-avatar [account]="user?.account" size="32"></my-actor-avatar>
110 <div> 93 <div>
111 <span class="user-table-primary-text">{{ user.account.displayName }}</span> 94 <span class="user-table-primary-text">{{ user.account.displayName }}</span>
112 <span class="text-muted">{{ user.username }}</span> 95 <span class="text-muted">{{ user.username }}</span>
diff --git a/client/src/app/+admin/users/user-list/user-list.component.scss b/client/src/app/+admin/users/user-list/user-list.component.scss
index 50080bad6..db4979a51 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.scss
+++ b/client/src/app/+admin/users/user-list/user-list.component.scss
@@ -11,6 +11,7 @@ tr.banned > td {
11 11
12.table-email { 12.table-email {
13 @include disable-default-a-behaviour; 13 @include disable-default-a-behaviour;
14
14 color: pvar(--mainForegroundColor); 15 color: pvar(--mainForegroundColor);
15} 16}
16 17
@@ -24,18 +25,10 @@ tr.banned > td {
24 25
25.user-table-primary-text .glyphicon { 26.user-table-primary-text .glyphicon {
26 font-size: 80%; 27 font-size: 80%;
27 color: gray; 28 color: #808080;
28 margin-left: 0.1rem; 29 margin-left: 0.1rem;
29} 30}
30 31
31.caption {
32 justify-content: space-between;
33
34 input {
35 @include peertube-input-text(250px);
36 }
37}
38
39p-tableCheckbox { 32p-tableCheckbox {
40 position: relative; 33 position: relative;
41 top: -2.5px; 34 top: -2.5px;
@@ -55,18 +48,7 @@ my-global-icon {
55 48
56.progress { 49.progress {
57 @include progressbar($small: true); 50 @include progressbar($small: true);
51
58 width: auto; 52 width: auto;
59 max-width: 100%; 53 max-width: 100%;
60} 54}
61
62.input-group {
63 @include peertube-input-group(300px);
64
65 input {
66 flex: 1;
67 }
68
69 .dropdown-toggle::after {
70 margin-left: 0;
71 }
72}
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts
index 339e18206..1c60adf89 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.ts
+++ b/client/src/app/+admin/users/user-list/user-list.component.ts
@@ -1,8 +1,9 @@
1import { SortMeta } from 'primeng/api' 1import { SortMeta } from 'primeng/api'
2import { Component, OnInit, ViewChild } from '@angular/core' 2import { Component, OnInit, ViewChild } from '@angular/core'
3import { ActivatedRoute, Params, Router } from '@angular/router' 3import { ActivatedRoute, Router } from '@angular/router'
4import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService, UserService } from '@app/core' 4import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService, UserService } from '@app/core'
5import { Account, DropdownAction } from '@app/shared/shared-main' 5import { AdvancedInputFilter } from '@app/shared/shared-forms'
6import { DropdownAction } from '@app/shared/shared-main'
6import { UserBanModalComponent } from '@app/shared/shared-moderation' 7import { UserBanModalComponent } from '@app/shared/shared-moderation'
7import { ServerConfig, User, UserRole } from '@shared/models' 8import { ServerConfig, User, UserRole } from '@shared/models'
8 9
@@ -22,15 +23,24 @@ export class UserListComponent extends RestTable implements OnInit {
22 @ViewChild('userBanModal', { static: true }) userBanModal: UserBanModalComponent 23 @ViewChild('userBanModal', { static: true }) userBanModal: UserBanModalComponent
23 24
24 users: User[] = [] 25 users: User[] = []
26
25 totalRecords = 0 27 totalRecords = 0
26 sort: SortMeta = { field: 'createdAt', order: 1 } 28 sort: SortMeta = { field: 'createdAt', order: 1 }
27 pagination: RestPagination = { count: this.rowsPerPage, start: 0 } 29 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
30
28 highlightBannedUsers = false 31 highlightBannedUsers = false
29 32
30 selectedUsers: User[] = [] 33 selectedUsers: User[] = []
31 bulkUserActions: DropdownAction<User[]>[][] = [] 34 bulkUserActions: DropdownAction<User[]>[][] = []
32 columns: { id: string, label: string }[] 35 columns: { id: string, label: string }[]
33 36
37 inputFilters: AdvancedInputFilter[] = [
38 {
39 queryParams: { 'search': 'banned:true' },
40 label: $localize`Banned users`
41 }
42 ]
43
34 private _selectedColumns: string[] 44 private _selectedColumns: string[]
35 private serverConfig: ServerConfig 45 private serverConfig: ServerConfig
36 46
@@ -68,7 +78,6 @@ export class UserListComponent extends RestTable implements OnInit {
68 .subscribe(config => this.serverConfig = config) 78 .subscribe(config => this.serverConfig = config)
69 79
70 this.initialize() 80 this.initialize()
71 this.listenToSearchChange()
72 81
73 this.bulkUserActions = [ 82 this.bulkUserActions = [
74 [ 83 [
@@ -160,7 +169,7 @@ export class UserListComponent extends RestTable implements OnInit {
160 } 169 }
161 170
162 onUserChanged () { 171 onUserChanged () {
163 this.loadData() 172 this.reloadData()
164 } 173 }
165 174
166 async unbanUsers (users: User[]) { 175 async unbanUsers (users: User[]) {
@@ -171,7 +180,7 @@ export class UserListComponent extends RestTable implements OnInit {
171 .subscribe( 180 .subscribe(
172 () => { 181 () => {
173 this.notifier.success($localize`${users.length} users unbanned.`) 182 this.notifier.success($localize`${users.length} users unbanned.`)
174 this.loadData() 183 this.reloadData()
175 }, 184 },
176 185
177 err => this.notifier.error(err.message) 186 err => this.notifier.error(err.message)
@@ -193,7 +202,7 @@ export class UserListComponent extends RestTable implements OnInit {
193 this.userService.removeUser(users).subscribe( 202 this.userService.removeUser(users).subscribe(
194 () => { 203 () => {
195 this.notifier.success($localize`${users.length} users deleted.`) 204 this.notifier.success($localize`${users.length} users deleted.`)
196 this.loadData() 205 this.reloadData()
197 }, 206 },
198 207
199 err => this.notifier.error(err.message) 208 err => this.notifier.error(err.message)
@@ -204,7 +213,7 @@ export class UserListComponent extends RestTable implements OnInit {
204 this.userService.updateUsers(users, { emailVerified: true }).subscribe( 213 this.userService.updateUsers(users, { emailVerified: true }).subscribe(
205 () => { 214 () => {
206 this.notifier.success($localize`${users.length} users email set as verified.`) 215 this.notifier.success($localize`${users.length} users email set as verified.`)
207 this.loadData() 216 this.reloadData()
208 }, 217 },
209 218
210 err => this.notifier.error(err.message) 219 err => this.notifier.error(err.message)
@@ -215,7 +224,7 @@ export class UserListComponent extends RestTable implements OnInit {
215 return this.selectedUsers.length !== 0 224 return this.selectedUsers.length !== 0
216 } 225 }
217 226
218 protected loadData () { 227 protected reloadData () {
219 this.selectedUsers = [] 228 this.selectedUsers = []
220 229
221 this.userService.getUsers({ 230 this.userService.getUsers({
diff --git a/client/src/app/+login/login.component.scss b/client/src/app/+login/login.component.scss
index eddaff542..62ae722c3 100644
--- a/client/src/app/+login/login.component.scss
+++ b/client/src/app/+login/login.component.scss
@@ -33,7 +33,8 @@ input[type=email] {
33 } 33 }
34} 34}
35 35
36.create-an-account, .forgot-password-button { 36.create-an-account,
37.forgot-password-button {
37 color: pvar(--mainForegroundColor); 38 color: pvar(--mainForegroundColor);
38 cursor: pointer; 39 cursor: pointer;
39 transition: opacity cubic-bezier(0.39, 0.575, 0.565, 1); 40 transition: opacity cubic-bezier(0.39, 0.575, 0.565, 1);
@@ -49,7 +50,7 @@ input[type=email] {
49 justify-content: space-around; 50 justify-content: space-around;
50 flex-wrap: wrap; 51 flex-wrap: wrap;
51 52
52 & > div { 53 > div {
53 flex: 1 1; 54 flex: 1 1;
54 } 55 }
55 56
@@ -65,7 +66,8 @@ input[type=email] {
65 form { 66 form {
66 margin: 0; 67 margin: 0;
67 68
68 &, input { 69 &,
70 input {
69 width: 100%; 71 width: 100%;
70 } 72 }
71 73
@@ -82,7 +84,8 @@ input[type=email] {
82 84
83 color: var(--mainColor); 85 color: var(--mainColor);
84 86
85 &:hover, &:active { 87 &:hover,
88 &:active {
86 color: var(--mainHoverColor); 89 color: var(--mainHoverColor);
87 } 90 }
88 } 91 }
@@ -111,7 +114,7 @@ input[type=email] {
111 min-width: 100px; 114 min-width: 100px;
112 115
113 &:hover { 116 &:hover {
114 background-color: rgba(209, 215, 224, 0.5) 117 background-color: rgba(209, 215, 224, 0.5);
115 } 118 }
116 } 119 }
117 } 120 }
@@ -138,7 +141,7 @@ input[type=email] {
138 } 141 }
139} 142}
140 143
141@mixin columnReverseDisplay { 144@mixin column-reverse-display {
142 flex-direction: column-reverse; 145 flex-direction: column-reverse;
143 146
144 .login-form-and-externals, 147 .login-form-and-externals,
@@ -168,14 +171,14 @@ input[type=email] {
168 171
169@media screen and (max-width: breakpoint(md)) { 172@media screen and (max-width: breakpoint(md)) {
170 .wrapper { 173 .wrapper {
171 @include columnReverseDisplay(); 174 @include column-reverse-display();
172 } 175 }
173} 176}
174 177
175@media screen and (max-width: breakpoint(md) + $menu-width) { 178@media screen and (max-width: breakpoint(md) + $menu-width) {
176 :host-context(.main-col:not(.expanded)) { 179 :host-context(.main-col:not(.expanded)) {
177 .wrapper { 180 .wrapper {
178 @include columnReverseDisplay(); 181 @include column-reverse-display();
179 } 182 }
180 } 183 }
181} 184}
diff --git a/client/src/app/+my-account/my-account-abuses/my-account-abuses-list.component.html b/client/src/app/+my-account/my-account-abuses/my-account-abuses-list.component.html
index 59ca61be6..e83b59019 100644
--- a/client/src/app/+my-account/my-account-abuses/my-account-abuses-list.component.html
+++ b/client/src/app/+my-account/my-account-abuses/my-account-abuses-list.component.html
@@ -3,4 +3,4 @@
3 <ng-container i18n>Reports</ng-container> 3 <ng-container i18n>Reports</ng-container>
4</h1> 4</h1>
5 5
6<my-abuse-list-table viewType="user" baseRoute="/my-account/abuses"></my-abuse-list-table> 6<my-abuse-list-table viewType="user"></my-abuse-list-table>
diff --git a/client/src/app/+my-account/my-account-applications/my-account-applications.component.scss b/client/src/app/+my-account/my-account-applications/my-account-applications.component.scss
index 704132c03..c1e1f2432 100644
--- a/client/src/app/+my-account/my-account-applications/my-account-applications.component.scss
+++ b/client/src/app/+my-account/my-account-applications/my-account-applications.component.scss
@@ -21,7 +21,7 @@ input[type=submit] {
21 display: flex; 21 display: flex;
22 margin-left: auto; 22 margin-left: auto;
23 23
24 & + .form-error { 24 + .form-error {
25 display: inline; 25 display: inline;
26 margin-left: 5px; 26 margin-left: 5px;
27 } 27 }
diff --git a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss
index 035fa2b27..abf52504a 100644
--- a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss
+++ b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss
@@ -32,7 +32,8 @@ my-user-notifications {
32 .header { 32 .header {
33 flex-direction: column; 33 flex-direction: column;
34 34
35 & >:first-child, .peertube-select-container { 35 > :first-child,
36 .peertube-select-container {
36 margin-bottom: 15px; 37 margin-bottom: 15px;
37 } 38 }
38 39
diff --git a/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.scss b/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.scss
index d79ff690b..64f960964 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.scss
+++ b/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.scss
@@ -7,4 +7,4 @@
7 @include danger-button; 7 @include danger-button;
8 @include disable-outline; 8 @include disable-outline;
9 } 9 }
10} \ No newline at end of file 10}
diff --git a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.scss b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.scss
index b06d8b16d..42319400d 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.scss
+++ b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.scss
@@ -10,7 +10,7 @@
10 font-size: 16px; 10 font-size: 16px;
11 } 11 }
12 12
13 & > div { 13 > div {
14 padding: 10px; 14 padding: 10px;
15 15
16 &:first-child { 16 &:first-child {
diff --git a/client/src/app/+my-account/my-account.component.scss b/client/src/app/+my-account/my-account.component.scss
index a5bb499b4..b32bc84e7 100644
--- a/client/src/app/+my-account/my-account.component.scss
+++ b/client/src/app/+my-account/my-account.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-account/my-account.module.ts b/client/src/app/+my-account/my-account.module.ts
index 050cd4b34..4081e4f01 100644
--- a/client/src/app/+my-account/my-account.module.ts
+++ b/client/src/app/+my-account/my-account.module.ts
@@ -3,13 +3,14 @@ import { TableModule } from 'primeng/table'
3import { DragDropModule } from '@angular/cdk/drag-drop' 3import { DragDropModule } from '@angular/cdk/drag-drop'
4import { NgModule } from '@angular/core' 4import { NgModule } from '@angular/core'
5import { SharedAbuseListModule } from '@app/shared/shared-abuse-list' 5import { SharedAbuseListModule } from '@app/shared/shared-abuse-list'
6import { SharedActorImageModule } from '@app/shared/shared-actor-image' 6import { SharedActorImageEditModule } from '@app/shared/shared-actor-image-edit'
7import { SharedFormModule } from '@app/shared/shared-forms' 7import { SharedFormModule } from '@app/shared/shared-forms'
8import { SharedGlobalIconModule } from '@app/shared/shared-icons' 8import { SharedGlobalIconModule } from '@app/shared/shared-icons'
9import { SharedMainModule } from '@app/shared/shared-main' 9import { SharedMainModule } from '@app/shared/shared-main'
10import { SharedModerationModule } from '@app/shared/shared-moderation' 10import { SharedModerationModule } from '@app/shared/shared-moderation'
11import { SharedShareModal } from '@app/shared/shared-share-modal' 11import { SharedShareModal } from '@app/shared/shared-share-modal'
12import { SharedUserInterfaceSettingsModule } from '@app/shared/shared-user-settings' 12import { SharedUserInterfaceSettingsModule } from '@app/shared/shared-user-settings'
13import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module'
13import { MyAccountAbusesListComponent } from './my-account-abuses/my-account-abuses-list.component' 14import { MyAccountAbusesListComponent } from './my-account-abuses/my-account-abuses-list.component'
14import { MyAccountApplicationsComponent } from './my-account-applications/my-account-applications.component' 15import { MyAccountApplicationsComponent } from './my-account-applications/my-account-applications.component'
15import { MyAccountBlocklistComponent } from './my-account-blocklist/my-account-blocklist.component' 16import { MyAccountBlocklistComponent } from './my-account-blocklist/my-account-blocklist.component'
@@ -23,7 +24,6 @@ import { MyAccountNotificationPreferencesComponent } from './my-account-settings
23import { MyAccountProfileComponent } from './my-account-settings/my-account-profile/my-account-profile.component' 24import { MyAccountProfileComponent } from './my-account-settings/my-account-profile/my-account-profile.component'
24import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component' 25import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component'
25import { MyAccountComponent } from './my-account.component' 26import { MyAccountComponent } from './my-account.component'
26import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module'
27 27
28@NgModule({ 28@NgModule({
29 imports: [ 29 imports: [
@@ -40,8 +40,8 @@ import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/share
40 SharedGlobalIconModule, 40 SharedGlobalIconModule,
41 SharedAbuseListModule, 41 SharedAbuseListModule,
42 SharedShareModal, 42 SharedShareModal,
43 SharedAccountAvatarModule, 43 SharedActorImageModule,
44 SharedActorImageModule 44 SharedActorImageEditModule
45 ], 45 ],
46 46
47 declarations: [ 47 declarations: [
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
4h1 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 { 17my-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 @@
1import { ChartData } from 'chart.js' 1import { ChartData } from 'chart.js'
2import { max, maxBy, min, minBy } from 'lodash-es' 2import { max, maxBy, min, minBy } from 'lodash-es'
3import { Subject } from 'rxjs' 3import { mergeMap } from 'rxjs/operators'
4import { debounceTime, mergeMap } from 'rxjs/operators' 4import { Component } from '@angular/core'
5import { Component, OnInit } from '@angular/core' 5import { AuthService, ConfirmService, Notifier, ScreenService } from '@app/core'
6import { AuthService, ConfirmService, Notifier, ScreenService, User } from '@app/core'
7import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' 6import { 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})
13export class MyVideoChannelsComponent implements OnInit { 12export 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 @@
1import { ChartModule } from 'primeng/chart' 1import { ChartModule } from 'primeng/chart'
2import { NgModule } from '@angular/core' 2import { NgModule } from '@angular/core'
3import { SharedActorImageModule } from '@app/shared/shared-actor-image' 3import { SharedActorImageEditModule } from '@app/shared/shared-actor-image-edit'
4import { SharedFormModule } from '@app/shared/shared-forms' 4import { SharedFormModule } from '@app/shared/shared-forms'
5import { SharedGlobalIconModule } from '@app/shared/shared-icons' 5import { SharedGlobalIconModule } from '@app/shared/shared-icons'
6import { SharedMainModule } from '@app/shared/shared-main' 6import { SharedMainModule } from '@app/shared/shared-main'
@@ -8,6 +8,7 @@ import { MyVideoChannelCreateComponent } from './my-video-channel-create.compone
8import { MyVideoChannelUpdateComponent } from './my-video-channel-update.component' 8import { MyVideoChannelUpdateComponent } from './my-video-channel-update.component'
9import { MyVideoChannelsRoutingModule } from './my-video-channels-routing.module' 9import { MyVideoChannelsRoutingModule } from './my-video-channels-routing.module'
10import { MyVideoChannelsComponent } from './my-video-channels.component' 10import { MyVideoChannelsComponent } from './my-video-channels.component'
11import { 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 @@
1import { Component, ComponentFactoryResolver, OnDestroy, OnInit } from '@angular/core' 1import { Subject } from 'rxjs'
2import { tap } from 'rxjs/operators'
3import { Component, ComponentFactoryResolver, OnInit, ViewChild } from '@angular/core'
2import { ActivatedRoute, Router } from '@angular/router' 4import { ActivatedRoute, Router } from '@angular/router'
3import { 5import {
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'
13import { immutableAssign } from '@app/helpers' 17import { immutableAssign } from '@app/helpers'
14import { UserHistoryService } from '@app/shared/shared-main' 18import { UserHistoryService, Video } from '@app/shared/shared-main'
15import { AbstractVideoList } from '@app/shared/shared-video-miniature' 19import { MiniatureDisplayOptions, VideosSelectionComponent } from '@app/shared/shared-video-miniature'
16import { Subject } from 'rxjs'
17import { 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})
23export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnDestroy { 25export 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
26import { MyVideoPlaylistsComponent } from './my-video-playlists/my-video-playlists.component' 26import { MyVideoPlaylistsComponent } from './my-video-playlists/my-video-playlists.component'
27import { VideoChangeOwnershipComponent } from './my-videos/modals/video-change-ownership.component' 27import { VideoChangeOwnershipComponent } from './my-videos/modals/video-change-ownership.component'
28import { MyVideosComponent } from './my-videos/my-videos.component' 28import { MyVideosComponent } from './my-videos/my-videos.component'
29import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module' 29import { 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 @@
1import { Subject } from 'rxjs' 1import { Subject } from 'rxjs'
2import { debounceTime } from 'rxjs/operators' 2import { Component } from '@angular/core'
3import { Component, OnInit } from '@angular/core'
4import { ComponentPagination, Notifier } from '@app/core' 3import { ComponentPagination, Notifier } from '@app/core'
5import { VideoChannel } from '@app/shared/shared-main' 4import { VideoChannel } from '@app/shared/shared-main'
6import { UserSubscriptionService } from '@app/shared/shared-user-subscription' 5import { 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})
12export class MySubscriptionsComponent implements OnInit { 11export 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 @@
1import { Subject } from 'rxjs' 1import { Subject } from 'rxjs'
2import { debounceTime, mergeMap } from 'rxjs/operators' 2import { mergeMap } from 'rxjs/operators'
3import { Component, OnInit } from '@angular/core' 3import { Component } from '@angular/core'
4import { AuthService, ComponentPagination, ConfirmService, Notifier, User } from '@app/core' 4import { AuthService, ComponentPagination, ConfirmService, Notifier } from '@app/core'
5import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' 5import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
6import { VideoPlaylistType } from '@shared/models' 6import { 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})
12export class MyVideoPlaylistsComponent implements OnInit { 12export 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 @@
1import { concat, Observable, Subject } from 'rxjs' 1import { concat, Observable } from 'rxjs'
2import { debounceTime, tap, toArray } from 'rxjs/operators' 2import { tap, toArray } from 'rxjs/operators'
3import { Component, OnInit, ViewChild } from '@angular/core' 3import { Component, OnInit, ViewChild } from '@angular/core'
4import { ActivatedRoute, Router } from '@angular/router' 4import { ActivatedRoute, Router } from '@angular/router'
5import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User } from '@app/core' 5import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User } from '@app/core'
6import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' 6import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
7import { immutableAssign } from '@app/helpers' 7import { immutableAssign } from '@app/helpers'
8import { AdvancedInputFilter } from '@app/shared/shared-forms'
8import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' 9import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
9import { LiveStreamInformationComponent } from '@app/shared/shared-video-live' 10import { LiveStreamInformationComponent } from '@app/shared/shared-video-live'
10import { MiniatureDisplayOptions, SelectionType, VideosSelectionComponent } from '@app/shared/shared-video-miniature' 11import { 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 )
diff --git a/client/src/app/+search/search-filters.component.scss b/client/src/app/+search/search-filters.component.scss
index 68ac6d021..cfb7a1d98 100644
--- a/client/src/app/+search/search-filters.component.scss
+++ b/client/src/app/+search/search-filters.component.scss
@@ -46,7 +46,7 @@ input[type=submit] {
46 46
47 font-weight: $font-semibold; 47 font-weight: $font-semibold;
48 display: inline-block; 48 display: inline-block;
49 padding: 0 10px 0 10px; 49 padding: 0 10px;
50 white-space: nowrap; 50 white-space: nowrap;
51 background: transparent; 51 background: transparent;
52 52
diff --git a/client/src/app/+search/search.component.html b/client/src/app/+search/search.component.html
index 65d4b6ecd..130be75fc 100644
--- a/client/src/app/+search/search.component.html
+++ b/client/src/app/+search/search.component.html
@@ -33,20 +33,15 @@
33 33
34 <ng-container *ngFor="let result of results"> 34 <ng-container *ngFor="let result of results">
35 <div *ngIf="isVideoChannel(result)" class="entry video-channel"> 35 <div *ngIf="isVideoChannel(result)" class="entry video-channel">
36 <a class="link-avatar" *ngIf="!isExternalChannelUrl()" [routerLink]="getChannelUrl(result)">
37 <img [src]="result.avatarUrl" alt="Avatar" />
38 </a>
39 36
40 <a class="link-avatar" *ngIf="isExternalChannelUrl()" [href]="getChannelUrl(result)" target="_blank"> 37 <my-actor-avatar [channel]="result" [internalHref]="getInternalChannelUrl(result)" [href]="getExternalChannelUrl(result)"></my-actor-avatar>
41 <img [src]="result.avatarUrl" alt="Avatar" />
42 </a>
43 38
44 <div class="video-channel-info"> 39 <div class="video-channel-info">
45 <a *ngIf="!isExternalChannelUrl()" [routerLink]="getChannelUrl(result)" class="video-channel-names"> 40 <a *ngIf="!isExternalChannelUrl()" [routerLink]="getInternalChannelUrl(result)" class="video-channel-names">
46 <ng-container *ngTemplateOutlet="aContent"></ng-container> 41 <ng-container *ngTemplateOutlet="aContent"></ng-container>
47 </a> 42 </a>
48 43
49 <a *ngIf="isExternalChannelUrl()" [href]="getChannelUrl(result)" target="_blank" class="video-channel-names"> 44 <a *ngIf="isExternalChannelUrl()" [href]="getExternalChannelUrl(result)" target="_blank" class="video-channel-names">
50 <ng-container *ngTemplateOutlet="aContent"></ng-container> 45 <ng-container *ngTemplateOutlet="aContent"></ng-container>
51 </a> 46 </a>
52 47
diff --git a/client/src/app/+search/search.component.scss b/client/src/app/+search/search.component.scss
index 91c8272d7..a8002ba88 100644
--- a/client/src/app/+search/search.component.scss
+++ b/client/src/app/+search/search.component.scss
@@ -5,7 +5,7 @@
5 $image-size: min(130px, $video-img-width); 5 $image-size: min(130px, $video-img-width);
6 $margin-size: ($video-img-width - $image-size) / 2; // So we have the same width than the video miniature 6 $margin-size: ($video-img-width - $image-size) / 2; // So we have the same width than the video miniature
7 7
8 @include channel-avatar($image-size); 8 @include actor-avatar-size($image-size);
9 9
10 margin: 0 $margin-size 0 $margin-size; 10 margin: 0 $margin-size 0 $margin-size;
11} 11}
@@ -53,10 +53,8 @@
53 max-width: 800px; 53 max-width: 800px;
54} 54}
55 55
56.video-channel { 56.video-channel my-actor-avatar {
57 img { 57 @include build-channel-img-size($video-thumbnail-width);
58 @include build-channel-img-size($video-thumbnail-width);
59 }
60} 58}
61 59
62.video-channel-info { 60.video-channel-info {
@@ -92,14 +90,12 @@
92 grid-template-columns: auto 1fr; 90 grid-template-columns: auto 1fr;
93 grid-template-rows: auto auto; 91 grid-template-rows: auto auto;
94 92
95 .link-avatar { 93 my-actor-avatar {
94 @include build-channel-img-size($video-thumbnail-medium-width);
95
96 grid-column: 1; 96 grid-column: 1;
97 grid-row: 1 / -1; 97 grid-row: 1 / -1;
98 } 98 }
99
100 img {
101 @include build-channel-img-size($video-thumbnail-medium-width);
102 }
103 } 99 }
104 100
105 .video-channel-info { 101 .video-channel-info {
@@ -115,7 +111,7 @@
115} 111}
116 112
117@include on-mobile-main-col { 113@include on-mobile-main-col {
118 .video-channel img { 114 .video-channel my-actor-avatar {
119 @include build-channel-img-size($video-thumbnail-small-width); 115 @include build-channel-img-size($video-thumbnail-small-width);
120 } 116 }
121} 117}
diff --git a/client/src/app/+search/search.component.ts b/client/src/app/+search/search.component.ts
index 2be952e16..ecede19a3 100644
--- a/client/src/app/+search/search.component.ts
+++ b/client/src/app/+search/search.component.ts
@@ -132,10 +132,6 @@ export class SearchComponent implements OnInit, OnDestroy {
132 return 'internal' 132 return 'internal'
133 } 133 }
134 134
135 isExternalChannelUrl () {
136 return this.getVideoLinkType() === 'external'
137 }
138
139 search () { 135 search () {
140 forkJoin([ 136 forkJoin([
141 this.getVideosObs(), 137 this.getVideosObs(),
@@ -200,17 +196,33 @@ export class SearchComponent implements OnInit, OnDestroy {
200 this.results = this.results.filter(r => !this.isVideo(r) || r.id !== video.id) 196 this.results = this.results.filter(r => !this.isVideo(r) || r.id !== video.id)
201 } 197 }
202 198
203 getChannelUrl (channel: VideoChannel) { 199 isExternalChannelUrl () {
200 return this.getVideoLinkType() === 'external'
201 }
202
203 getExternalChannelUrl (channel: VideoChannel) {
204 // Same algorithm than videos 204 // Same algorithm than videos
205 if (this.getVideoLinkType() === 'external') { 205 if (this.getVideoLinkType() === 'external') {
206 return channel.url 206 return channel.url
207 } 207 }
208 208
209 if (this.getVideoLinkType() === 'internal') { 209 // lazy-load or internal
210 return undefined
211 }
212
213 getInternalChannelUrl (channel: VideoChannel) {
214 const linkType = this.getVideoLinkType()
215
216 if (linkType === 'internal') {
210 return [ '/video-channels', channel.nameWithHost ] 217 return [ '/video-channels', channel.nameWithHost ]
211 } 218 }
212 219
213 return [ '/search/lazy-load-channel', { url: channel.url } ] 220 if (linkType === 'lazy-load') {
221 return [ '/search/lazy-load-channel', { url: channel.url } ]
222 }
223
224 // external
225 return undefined
214 } 226 }
215 227
216 hideActions () { 228 hideActions () {
diff --git a/client/src/app/+search/search.module.ts b/client/src/app/+search/search.module.ts
index e85ae07d0..390833abc 100644
--- a/client/src/app/+search/search.module.ts
+++ b/client/src/app/+search/search.module.ts
@@ -1,4 +1,5 @@
1import { NgModule } from '@angular/core' 1import { NgModule } from '@angular/core'
2import { SharedActorImageModule } from '@app/shared/shared-actor-image/shared-actor-image.module'
2import { SharedFormModule } from '@app/shared/shared-forms' 3import { SharedFormModule } from '@app/shared/shared-forms'
3import { SharedMainModule } from '@app/shared/shared-main' 4import { SharedMainModule } from '@app/shared/shared-main'
4import { SharedSearchModule } from '@app/shared/shared-search' 5import { SharedSearchModule } from '@app/shared/shared-search'
@@ -18,6 +19,7 @@ import { VideoLazyLoadResolver } from './video-lazy-load.resolver'
18 SharedMainModule, 19 SharedMainModule,
19 SharedSearchModule, 20 SharedSearchModule,
20 SharedFormModule, 21 SharedFormModule,
22 SharedActorImageModule,
21 SharedUserSubscriptionModule, 23 SharedUserSubscriptionModule,
22 SharedVideoMiniatureModule 24 SharedVideoMiniatureModule
23 ], 25 ],
diff --git a/client/src/app/+signup/+register/register.component.scss b/client/src/app/+signup/+register/register.component.scss
index 16ba9e2c0..f6a846ffa 100644
--- a/client/src/app/+signup/+register/register.component.scss
+++ b/client/src/app/+signup/+register/register.component.scss
@@ -84,7 +84,7 @@ button {
84 border-color: pvar(--mainColor) transparent transparent transparent; 84 border-color: pvar(--mainColor) transparent transparent transparent;
85 } 85 }
86 86
87 & + div { 87 + div {
88 font-size: 15px; 88 font-size: 15px;
89 } 89 }
90 } 90 }
diff --git a/client/src/app/+signup/shared/signup-success.component.scss b/client/src/app/+signup/shared/signup-success.component.scss
index fbc27c8bc..b302366e2 100644
--- a/client/src/app/+signup/shared/signup-success.component.scss
+++ b/client/src/app/+signup/shared/signup-success.component.scss
@@ -9,19 +9,16 @@ svg {
9 stroke-dashoffset: 0; 9 stroke-dashoffset: 0;
10 10
11 &.circle { 11 &.circle {
12 -webkit-animation: dash .9s ease-in-out;
13 animation: dash .9s ease-in-out; 12 animation: dash .9s ease-in-out;
14 } 13 }
15 14
16 &.line { 15 &.line {
17 stroke-dashoffset: 1000; 16 stroke-dashoffset: 1000;
18 -webkit-animation: dash .9s .35s ease-in-out forwards;
19 animation: dash .9s .35s ease-in-out forwards; 17 animation: dash .9s .35s ease-in-out forwards;
20 } 18 }
21 19
22 &.check { 20 &.check {
23 stroke-dashoffset: -100; 21 stroke-dashoffset: -100;
24 -webkit-animation: dash-check .9s .35s ease-in-out forwards;
25 animation: dash-check .9s .35s ease-in-out forwards; 22 animation: dash-check .9s .35s ease-in-out forwards;
26 } 23 }
27} 24}
@@ -38,16 +35,6 @@ svg {
38 text-align: center; 35 text-align: center;
39} 36}
40 37
41
42@-webkit-keyframes dash {
43 0% {
44 stroke-dashoffset: 1000;
45 }
46 100% {
47 stroke-dashoffset: 0;
48 }
49}
50
51@keyframes dash { 38@keyframes dash {
52 0% { 39 0% {
53 stroke-dashoffset: 1000; 40 stroke-dashoffset: 1000;
@@ -57,15 +44,6 @@ svg {
57 } 44 }
58} 45}
59 46
60@-webkit-keyframes dash-check {
61 0% {
62 stroke-dashoffset: -100;
63 }
64 100% {
65 stroke-dashoffset: 900;
66 }
67}
68
69@keyframes dash-check { 47@keyframes dash-check {
70 0% { 48 0% {
71 stroke-dashoffset: -100; 49 stroke-dashoffset: -100;
diff --git a/client/src/app/+video-channels/video-channels.component.html b/client/src/app/+video-channels/video-channels.component.html
index 9308d5bb6..b4d81fe39 100644
--- a/client/src/app/+video-channels/video-channels.component.html
+++ b/client/src/app/+video-channels/video-channels.component.html
@@ -6,16 +6,16 @@
6 <div class="channel-info"> 6 <div class="channel-info">
7 7
8 <ng-template #buttonsTemplate> 8 <ng-template #buttonsTemplate>
9 <a *ngIf="isManageable()" [routerLink]="[ '/my-library/video-channels/update', videoChannel.nameWithHost ]" class="peertube-button-link orange-button" i18n> 9 <a *ngIf="isManageable()" [routerLink]="[ '/my-library/video-channels/update', videoChannel.nameWithHost ]" class="peertube-button-link orange-button" i18n>
10 Manage channel 10 Manage channel
11 </a> 11 </a>
12 12
13 <my-subscribe-button *ngIf="!isManageable()" #subscribeButton [videoChannels]="[videoChannel]"></my-subscribe-button> 13 <my-subscribe-button *ngIf="!isManageable()" #subscribeButton [videoChannels]="[videoChannel]"></my-subscribe-button>
14 14
15 <button *ngIf="videoChannel.support" (click)="showSupportModal()" class="support-button peertube-button orange-button-inverted"> 15 <button *ngIf="videoChannel.support" (click)="showSupportModal()" class="support-button peertube-button orange-button-inverted">
16 <my-global-icon iconName="support" aria-hidden="true"></my-global-icon> 16 <my-global-icon iconName="support" aria-hidden="true"></my-global-icon>
17 <span class="icon-text" i18n>Support</span> 17 <span class="icon-text" i18n>Support</span>
18 </button> 18 </button>
19 </ng-template> 19 </ng-template>
20 20
21 <ng-template #ownerTemplate> 21 <ng-template #ownerTemplate>
@@ -23,7 +23,7 @@
23 <div class="section-label" i18n>OWNER ACCOUNT</div> 23 <div class="section-label" i18n>OWNER ACCOUNT</div>
24 24
25 <div class="avatar-row"> 25 <div class="avatar-row">
26 <my-account-avatar [account]="videoChannel.ownerAccount" [internalHref]="getAccountUrl()" size="120"></my-account-avatar> 26 <my-actor-avatar class="account-avatar" [account]="videoChannel.ownerAccount" [internalHref]="getAccountUrl()"></my-actor-avatar>
27 27
28 <div class="actor-info"> 28 <div class="actor-info">
29 <h4> 29 <h4>
@@ -49,7 +49,7 @@
49 </ng-template> 49 </ng-template>
50 50
51 <div class="channel-avatar-row"> 51 <div class="channel-avatar-row">
52 <img class="channel-avatar" [src]="videoChannel.avatarUrl" alt="Avatar" /> 52 <my-actor-avatar class="main-avatar" [channel]="videoChannel"></my-actor-avatar>
53 53
54 <div> 54 <div>
55 <div class="section-label" i18n>VIDEO CHANNEL</div> 55 <div class="section-label" i18n>VIDEO CHANNEL</div>
diff --git a/client/src/app/+video-channels/video-channels.component.scss b/client/src/app/+video-channels/video-channels.component.scss
index e946707ef..470f64878 100644
--- a/client/src/app/+video-channels/video-channels.component.scss
+++ b/client/src/app/+video-channels/video-channels.component.scss
@@ -107,8 +107,8 @@
107 display: flex; 107 display: flex;
108 margin-bottom: 15px; 108 margin-bottom: 15px;
109 109
110 img { 110 .account-avatar {
111 @include avatar(48px); 111 @include actor-avatar-size(48px);
112 } 112 }
113 113
114 .actor-info { 114 .actor-info {
@@ -131,10 +131,10 @@
131 } 131 }
132 132
133 .owner-description { 133 .owner-description {
134 @include fade-text(120px, pvar(--mainBackgroundColor));
135
134 max-height: 140px; 136 max-height: 140px;
135 word-break: break-word; 137 word-break: break-word;
136
137 @include fade-text(120px, pvar(--mainBackgroundColor));
138 } 138 }
139} 139}
140 140
@@ -150,7 +150,7 @@
150} 150}
151 151
152.copy-button { 152.copy-button {
153 border: none; 153 border: 0;
154} 154}
155 155
156@media screen and (max-width: 1400px) { 156@media screen and (max-width: 1400px) {
@@ -178,9 +178,9 @@
178 } 178 }
179 179
180 .channel-description:not(.expanded) { 180 .channel-description:not(.expanded) {
181 max-height: 70px;
182
183 @include fade-text(30px, pvar(--channelBackgroundColor)); 181 @include fade-text(30px, pvar(--channelBackgroundColor));
182
183 max-height: 70px;
184 } 184 }
185 185
186 .show-more { 186 .show-more {
@@ -220,10 +220,10 @@
220 } 220 }
221 221
222 .owner-description { 222 .owner-description {
223 @include fade-text(30px, pvar(--mainBackgroundColor));
224
223 grid-column: 2; 225 grid-column: 2;
224 max-height: 70px; 226 max-height: 70px;
225
226 @include fade-text(30px, pvar(--mainBackgroundColor));
227 } 227 }
228 228
229 .view-account { 229 .view-account {
@@ -289,8 +289,8 @@
289 margin-top: -5px; 289 margin-top: -5px;
290 } 290 }
291 291
292 img { 292 .account-avatar {
293 @include channel-avatar(64px); 293 @include actor-avatar-size(64px);
294 294
295 margin: -30px 0 0 15px; 295 margin: -30px 0 0 15px;
296 } 296 }
diff --git a/client/src/app/+video-channels/video-channels.module.ts b/client/src/app/+video-channels/video-channels.module.ts
index 2e387f401..35c39cc2e 100644
--- a/client/src/app/+video-channels/video-channels.module.ts
+++ b/client/src/app/+video-channels/video-channels.module.ts
@@ -10,7 +10,7 @@ import { VideoChannelPlaylistsComponent } from './video-channel-playlists/video-
10import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component' 10import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component'
11import { VideoChannelsRoutingModule } from './video-channels-routing.module' 11import { VideoChannelsRoutingModule } from './video-channels-routing.module'
12import { VideoChannelsComponent } from './video-channels.component' 12import { VideoChannelsComponent } from './video-channels.component'
13import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/shared-account-avatar.module' 13import { SharedActorImageModule } from '../shared/shared-actor-image/shared-actor-image.module'
14 14
15@NgModule({ 15@NgModule({
16 imports: [ 16 imports: [
@@ -23,7 +23,7 @@ import { SharedAccountAvatarModule } from '../shared/shared-account-avatar/share
23 SharedUserSubscriptionModule, 23 SharedUserSubscriptionModule,
24 SharedGlobalIconModule, 24 SharedGlobalIconModule,
25 SharedSupportModal, 25 SharedSupportModal,
26 SharedAccountAvatarModule 26 SharedActorImageModule
27 ], 27 ],
28 28
29 declarations: [ 29 declarations: [
diff --git a/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.scss b/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.scss
index 0958b5f80..a85cf444c 100644
--- a/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.scss
+++ b/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.scss
@@ -16,6 +16,6 @@ label {
16} 16}
17 17
18.warning-replace-caption { 18.warning-replace-caption {
19 color: red; 19 color: #ff0000;
20 margin-top: 10px; 20 margin-top: 10px;
21} \ No newline at end of file 21}
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.html b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
index 6fe52af67..094b4d3b3 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.html
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
@@ -132,7 +132,7 @@
132 </ng-template> 132 </ng-template>
133 133
134 <ng-template ptTemplate="help"> 134 <ng-template ptTemplate="help">
135 <ng-container i18n>Some instances do not list videos containing mature or explicit content by default.</ng-container> 135 <ng-container i18n>Some instances hide videos containing mature or explicit content by default.</ng-container>
136 </ng-template> 136 </ng-template>
137 </my-peertube-checkbox> 137 </my-peertube-checkbox>
138 138
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.scss b/client/src/app/+videos/+video-edit/shared/video-edit.component.scss
index 0b70b0270..bc32d7964 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.scss
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.scss
@@ -150,7 +150,7 @@ p-calendar {
150 @include media-breakpoint-up(md) { 150 @include media-breakpoint-up(md) {
151 @include make-col(7); 151 @include make-col(7);
152 152
153 & + .col-video-edit { 153 + .col-video-edit {
154 @include make-col(5); 154 @include make-col(5);
155 } 155 }
156 } 156 }
@@ -158,7 +158,7 @@ p-calendar {
158 @include media-breakpoint-up(xl) { 158 @include media-breakpoint-up(xl) {
159 @include make-col(8); 159 @include make-col(8);
160 160
161 & + .col-video-edit { 161 + .col-video-edit {
162 @include make-col(4); 162 @include make-col(4);
163 } 163 }
164 } 164 }
@@ -169,7 +169,7 @@ p-calendar {
169 @include media-breakpoint-up(md) { 169 @include media-breakpoint-up(md) {
170 @include make-col(8); 170 @include make-col(8);
171 171
172 & + .col-video-edit { 172 + .col-video-edit {
173 @include make-col(4); 173 @include make-col(4);
174 } 174 }
175 } 175 }
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-send.scss b/client/src/app/+videos/+video-edit/video-add-components/video-send.scss
index 17c5f63e9..dc9153b2b 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-send.scss
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-send.scss
@@ -6,7 +6,7 @@ $width-size: 190px;
6.alert.alert-danger { 6.alert.alert-danger {
7 text-align: center; 7 text-align: center;
8 8
9 & > div { 9 > div {
10 font-weight: $font-semibold; 10 font-weight: $font-semibold;
11 } 11 }
12} 12}
@@ -17,10 +17,10 @@ $width-size: 190px;
17 align-items: center; 17 align-items: center;
18 18
19 .upload-icon { 19 .upload-icon {
20 @include apply-svg-color(#C6C6C6);
21
20 width: 90px; 22 width: 90px;
21 margin-bottom: 25px; 23 margin-bottom: 25px;
22
23 @include apply-svg-color(#C6C6C6);
24 } 24 }
25 25
26 .peertube-select-container { 26 .peertube-select-container {
diff --git a/client/src/app/+videos/+video-edit/video-add.component.scss b/client/src/app/+videos/+video-edit/video-add.component.scss
index 1ebee946b..35bca24d0 100644
--- a/client/src/app/+videos/+video-edit/video-add.component.scss
+++ b/client/src/app/+videos/+video-edit/video-add.component.scss
@@ -44,7 +44,7 @@ $nav-link-height: 40px;
44 44
45::ng-deep .video-add-nav { 45::ng-deep .video-add-nav {
46 border-bottom: $border-width $border-type $border-color; 46 border-bottom: $border-width $border-type $border-color;
47 margin: 20px 0 0 0 !important; 47 margin: 20px 0 0 !important;
48 48
49 &.hide-nav { 49 &.hide-nav {
50 display: none !important; 50 display: none !important;
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html
index 7bd9b7c90..42adfed8d 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html
+++ b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html
@@ -1,6 +1,6 @@
1<form novalidate [formGroup]="form" (ngSubmit)="formValidated()"> 1<form novalidate [formGroup]="form" (ngSubmit)="formValidated()">
2 <div class="avatar-and-textarea"> 2 <div class="avatar-and-textarea">
3 <my-account-avatar [account]="user?.account" size="25"></my-account-avatar> 3 <my-actor-avatar [account]="user?.account" size="25"></my-actor-avatar>
4 4
5 <div class="form-group"> 5 <div class="form-group">
6 <textarea i18n-placeholder placeholder="Add comment..." myAutoResize 6 <textarea i18n-placeholder placeholder="Add comment..." myAutoResize
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss
index 1aa9255c2..7743bd41d 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss
+++ b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss
@@ -13,8 +13,7 @@ form {
13 display: flex; 13 display: flex;
14 margin-bottom: 10px; 14 margin-bottom: 10px;
15 15
16 my-account-avatar { 16 my-actor-avatar {
17 vertical-align: top;
18 margin-right: 10px; 17 margin-right: 10px;
19 } 18 }
20 19
@@ -32,7 +31,7 @@ form {
32 padding-right: $markdown-icon-width + 15px !important; 31 padding-right: $markdown-icon-width + 15px !important;
33 32
34 @media screen and (max-width: 600px) { 33 @media screen and (max-width: 600px) {
35 padding-right: $markdown-icon-width + 19px !important; 34 padding-right: $markdown-icon-width + 19px !important;
36 } 35 }
37 36
38 &:focus::placeholder { 37 &:focus::placeholder {
@@ -58,7 +57,9 @@ form {
58 } 57 }
59 } 58 }
60 59
61 &:focus, &:active, &:hover { 60 &:focus,
61 &:active,
62 &:hover {
62 my-global-icon svg { 63 my-global-icon svg {
63 background-color: #C6C6C6; 64 background-color: #C6C6C6;
64 color: pvar(--mainBackgroundColor); 65 color: pvar(--mainBackgroundColor);
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.html b/client/src/app/+videos/+video-watch/comment/video-comment.component.html
index 2b0739261..d7ba40ef6 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment.component.html
+++ b/client/src/app/+videos/+video-watch/comment/video-comment.component.html
@@ -1,12 +1,10 @@
1<div *ngIf="isCommentDisplayed()" class="root-comment"> 1<div *ngIf="isCommentDisplayed()" class="root-comment" [ngClass]="{ 'is-child': isChild() }">
2 <div class="left"> 2 <div class="left">
3 <my-account-avatar *ngIf="!comment.isDeleted" [href]="comment.account.url" [account]="comment.account"></my-account-avatar> 3 <my-actor-avatar *ngIf="!comment.isDeleted" [href]="comment.account.url" [account]="comment.account"></my-actor-avatar>
4 <div class="vertical-border"></div> 4 <div class="vertical-border"></div>
5 </div> 5 </div>
6 6
7 <div class="right" [ngClass]="{ 'mb-3': firstInThread }"> 7 <div class="right" [ngClass]="{ 'mb-3': firstInThread }">
8 <span *ngIf="comment.isDeleted" class="comment-avatar"></span>
9
10 <div class="comment"> 8 <div class="comment">
11 <ng-container *ngIf="!comment.isDeleted"> 9 <ng-container *ngIf="!comment.isDeleted">
12 <div *ngIf="highlightedComment === true" class="highlighted-comment" i18n>Highlighted comment</div> 10 <div *ngIf="highlightedComment === true" class="highlighted-comment" i18n>Highlighted comment</div>
@@ -68,7 +66,7 @@
68 [textValue]="redraftValue" 66 [textValue]="redraftValue"
69 ></my-video-comment-add> 67 ></my-video-comment-add>
70 68
71 <div *ngIf="commentTree" class="children"> 69 <div *ngIf="commentTree">
72 <div *ngFor="let commentChild of commentTree.children"> 70 <div *ngFor="let commentChild of commentTree.children">
73 <my-video-comment 71 <my-video-comment
74 [comment]="commentChild.comment" 72 [comment]="commentChild.comment"
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.scss b/client/src/app/+videos/+video-watch/comment/video-comment.component.scss
index cf33a5b0e..a4d2e237c 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment.component.scss
+++ b/client/src/app/+videos/+video-watch/comment/video-comment.component.scss
@@ -24,6 +24,10 @@
24 } 24 }
25} 25}
26 26
27my-actor-avatar {
28 @include actor-avatar-size(36px);
29}
30
27.comment { 31.comment {
28 flex-grow: 1; 32 flex-grow: 1;
29 // Fix word-wrap with flex 33 // Fix word-wrap with flex
@@ -58,7 +62,7 @@
58 display: inline-flex; 62 display: inline-flex;
59 padding-right: 6px; 63 padding-right: 6px;
60 padding-left: 6px; 64 padding-left: 6px;
61 color: white !important; 65 color: #fff !important;
62} 66}
63 67
64.comment-account { 68.comment-account {
@@ -129,7 +133,10 @@
129 cursor: pointer; 133 cursor: pointer;
130 margin-right: 10px; 134 margin-right: 10px;
131 135
132 &:hover, &:active, &:focus, &:focus-visible { 136 &:hover,
137 &:active,
138 &:focus,
139 &:focus-visible {
133 color: pvar(--mainForegroundColor); 140 color: pvar(--mainForegroundColor);
134 } 141 }
135 } 142 }
@@ -148,10 +155,10 @@ my-video-comment-add {
148 } 155 }
149} 156}
150 157
151.children { 158.is-child {
152 // Reduce avatars size for replies 159 // Reduce avatars size for replies
153 .comment-avatar { 160 my-actor-avatar {
154 @include avatar(25px); 161 @include actor-avatar-size(25px);
155 } 162 }
156 163
157 .left { 164 .left {
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.ts b/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
index dd3db0c65..fd379e80e 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
+++ b/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
@@ -138,6 +138,10 @@ export class VideoCommentComponent implements OnInit, OnChanges {
138 (this.commentTree?.hasDisplayedChildren) // Or this is a reply that have other replies 138 (this.commentTree?.hasDisplayedChildren) // Or this is a reply that have other replies
139 } 139 }
140 140
141 isChild () {
142 return this.parentComments.length !== 0
143 }
144
141 private getUserIfNeeded (account: Account) { 145 private getUserIfNeeded (account: Account) {
142 if (!account.userId) return 146 if (!account.userId) return
143 if (!this.authService.isLoggedIn()) return 147 if (!this.authService.isLoggedIn()) return
diff --git a/client/src/app/+videos/+video-watch/comment/video-comments.component.scss b/client/src/app/+videos/+video-watch/comment/video-comments.component.scss
index e6778e1a9..a7e858069 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comments.component.scss
+++ b/client/src/app/+videos/+video-watch/comment/video-comments.component.scss
@@ -11,7 +11,8 @@
11 cursor: pointer; 11 cursor: pointer;
12} 12}
13 13
14.glyphicon, .comment-thread-loading { 14.glyphicon,
15.comment-thread-loading {
15 margin-right: 5px; 16 margin-right: 5px;
16 display: inline-block; 17 display: inline-block;
17 font-size: 13px; 18 font-size: 13px;
@@ -40,7 +41,7 @@
40#dropdown-sort-comments { 41#dropdown-sort-comments {
41 font-weight: 600; 42 font-weight: 600;
42 text-transform: uppercase; 43 text-transform: uppercase;
43 border: none; 44 border: 0;
44 transform: translateY(-7%); 45 transform: translateY(-7%);
45} 46}
46 47
diff --git a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html
index e0e9f92e7..e1040fead 100644
--- a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html
+++ b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html
@@ -15,7 +15,9 @@
15 <ng-container *ngFor="let video of (videos$ | async); let i = index; let length = count"> 15 <ng-container *ngFor="let video of (videos$ | async); let i = index; let length = count">
16 <my-video-miniature 16 <my-video-miniature
17 [displayOptions]="displayOptions" [video]="video" [user]="userMiniature" [displayAsRow]="displayAsRow" 17 [displayOptions]="displayOptions" [video]="video" [user]="userMiniature" [displayAsRow]="displayAsRow"
18 (videoBlocked)="onVideoRemoved()" (videoRemoved)="onVideoRemoved()" (videoAccountMuted)="onVideoRemoved()"> 18 (videoBlocked)="onVideoRemoved()" (videoRemoved)="onVideoRemoved()" (videoAccountMuted)="onVideoRemoved()"
19 actorImageSize="32"
20 >
19 </my-video-miniature> 21 </my-video-miniature>
20 22
21 <hr *ngIf="!playlist && i == 0 && length > 1" /> 23 <hr *ngIf="!playlist && i == 0 && length > 1" />
diff --git a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss
index c9fae6f27..5e0373afc 100644
--- a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss
+++ b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss
@@ -8,7 +8,8 @@
8 margin-bottom: 25px; 8 margin-bottom: 25px;
9 flex-wrap: wrap-reverse; 9 flex-wrap: wrap-reverse;
10 10
11 .title-page.active, .title-page.title-page-single { 11 .title-page.active,
12 .title-page.title-page-single {
12 margin-bottom: unset; 13 margin-bottom: unset;
13 margin-right: .5rem !important; 14 margin-right: .5rem !important;
14 } 15 }
diff --git a/client/src/app/+videos/+video-watch/video-avatar-channel.component.html b/client/src/app/+videos/+video-watch/video-avatar-channel.component.html
index a02373f2d..5f149cbd1 100644
--- a/client/src/app/+videos/+video-watch/video-avatar-channel.component.html
+++ b/client/src/app/+videos/+video-watch/video-avatar-channel.component.html
@@ -1,21 +1,11 @@
1<div class="wrapper" [ngClass]="'avatar-' + size"> 1<div class="wrapper" [ngClass]="{ 'generic-channel': genericChannel }">
2 <ng-container *ngIf="!isChannelAvatarNull() && !genericChannel"> 2 <my-actor-avatar
3 <a [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle"> 3 class="channel" [channel]="video.channel"
4 <img [src]="video.videoChannelAvatarUrl" i18n-alt alt="Channel avatar" class="channel-avatar" /> 4 [internalHref]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle"
5 </a> 5 ></my-actor-avatar>
6 6
7 <my-account-avatar [account]="video.account" [title]="accountLinkTitle" [internalHref]="[ '/accounts', video.byAccount ]"></my-account-avatar> 7 <my-actor-avatar
8</ng-container> 8 class="account" [account]="video.account"
9 9 [internalHref]="[ '/accounts', video.byAccount ]" [title]="accountLinkTitle">
10 <ng-container *ngIf="!isChannelAvatarNull() && genericChannel"> 10 </my-actor-avatar>
11 <my-account-avatar [account]="video.account" [title]="accountLinkTitle" [internalHref]="[ '/accounts', video.byAccount ]"></my-account-avatar>
12
13 <a [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle">
14 <img [src]="video.videoChannelAvatarUrl" i18n-alt alt="Channel avatar" class="channel-avatar" />
15 </a>
16 </ng-container>
17
18 <ng-container *ngIf="isChannelAvatarNull()">
19 <my-account-avatar [account]="video.account" [title]="accountLinkTitle" [internalHref]="[ '/accounts', video.byAccount ]"></my-account-avatar>
20 </ng-container>
21</div> 11</div>
diff --git a/client/src/app/+videos/+video-watch/video-avatar-channel.component.scss b/client/src/app/+videos/+video-watch/video-avatar-channel.component.scss
index 4998e85fa..20e32240c 100644
--- a/client/src/app/+videos/+video-watch/video-avatar-channel.component.scss
+++ b/client/src/app/+videos/+video-watch/video-avatar-channel.component.scss
@@ -1,44 +1,42 @@
1@import '_mixins'; 1@import '_mixins';
2 2
3@mixin main {
4 @include actor-avatar-size(35px);
5}
6
7@mixin secondary {
8 height: 60%;
9 width: 60%;
10 position: absolute;
11 bottom: -5px;
12 right: -5px;
13 background-color: rgba(0, 0, 0, 0);
14}
15
3.wrapper { 16.wrapper {
4 $avatar-size: 35px; 17 @include actor-avatar-size(35px);
5 18
6 width: $avatar-size;
7 height: $avatar-size;
8 position: relative; 19 position: relative;
9 margin-right: 5px; 20 margin-right: 5px;
10 margin-bottom: 5px; 21 margin-bottom: 5px;
11 22
12 &.avatar-sm { 23 &.generic-channel {
13 width: 28px; 24 .account {
14 height: 28px; 25 @include main();
15 margin-bottom: 3px; 26 }
16 }
17 27
18 a { 28 .channel {
19 @include disable-outline; 29 display: none !important;
30 }
20 } 31 }
21 32
22 a img { 33 &:not(.generic-channel) {
23 height: 100%; 34 .account {
24 object-fit: cover; 35 @include secondary();
25 position: absolute;
26 top:50%;
27 left:50%;
28 transform: translate(-50%,-50%);
29 border-radius: 5px;
30
31 &:not(.channel-avatar) {
32 border-radius: 50%;
33 } 36 }
34 }
35 37
36 a:nth-of-type(2) img { 38 .channel {
37 height: 60%; 39 @include main();
38 width: 60%; 40 }
39 border: 2px solid pvar(--mainBackgroundColor);
40 transform: translateX(15%);
41 position: relative;
42 background-color: pvar(--mainBackgroundColor);
43 } 41 }
44} 42}
diff --git a/client/src/app/+videos/+video-watch/video-avatar-channel.component.ts b/client/src/app/+videos/+video-watch/video-avatar-channel.component.ts
index 0b6e796df..63edd7bad 100644
--- a/client/src/app/+videos/+video-watch/video-avatar-channel.component.ts
+++ b/client/src/app/+videos/+video-watch/video-avatar-channel.component.ts
@@ -10,7 +10,6 @@ export class VideoAvatarChannelComponent implements OnInit {
10 @Input() video: Video 10 @Input() video: Video
11 @Input() byAccount: string 11 @Input() byAccount: string
12 12
13 @Input() size: 'md' | 'sm' = 'md'
14 @Input() genericChannel: boolean 13 @Input() genericChannel: boolean
15 14
16 channelLinkTitle = '' 15 channelLinkTitle = ''
diff --git a/client/src/app/+videos/+video-watch/video-watch-playlist.component.scss b/client/src/app/+videos/+video-watch/video-watch-playlist.component.scss
index 0b0a2a899..b3f93b83c 100644
--- a/client/src/app/+videos/+video-watch/video-watch-playlist.component.scss
+++ b/client/src/app/+videos/+video-watch/video-watch-playlist.component.scss
@@ -45,7 +45,7 @@
45 45
46 my-global-icon { 46 my-global-icon {
47 &:not(.active) { 47 &:not(.active) {
48 opacity: .5 48 opacity: .5;
49 } 49 }
50 50
51 ::ng-deep { 51 ::ng-deep {
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.html b/client/src/app/+videos/+video-watch/video-watch.component.html
index eadb2148a..4779602d2 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.html
+++ b/client/src/app/+videos/+video-watch/video-watch.component.html
@@ -79,7 +79,7 @@
79 <span [innerHTML]="getRatePopoverText()"></span> 79 <span [innerHTML]="getRatePopoverText()"></span>
80 </ng-template> 80 </ng-template>
81 81
82 <div class="video-actions fullWidth justify-content-end"> 82 <div class="video-actions full-width justify-content-end">
83 <button 83 <button
84 [ngbPopover]="getRatePopoverText() && ratePopoverText" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()" (keyup.enter)="setLike()" 84 [ngbPopover]="getRatePopoverText() && ratePopoverText" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()" (keyup.enter)="setLike()"
85 class="action-button action-button-like" [attr.aria-pressed]="userRating === 'like'" [attr.aria-label]="tooltipLike" 85 class="action-button action-button-like" [attr.aria-pressed]="userRating === 'like'" [attr.aria-label]="tooltipLike"
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.scss b/client/src/app/+videos/+video-watch/video-watch.component.scss
index e8ad10a11..301762695 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.scss
+++ b/client/src/app/+videos/+video-watch/video-watch.component.scss
@@ -6,12 +6,12 @@
6$player-factor: 16/9; 6$player-factor: 16/9;
7$video-info-margin-left: 44px; 7$video-info-margin-left: 44px;
8 8
9@function getPlayerHeight($width){ 9@function getPlayerHeight ($width) {
10 @return calc(#{$width} / #{$player-factor}) 10 @return calc(#{$width} / #{$player-factor});
11} 11}
12 12
13@function getPlayerWidth($height){ 13@function getPlayerWidth ($height) {
14 @return calc(#{$height} * #{$player-factor}) 14 @return calc(#{$height} * #{$player-factor});
15} 15}
16 16
17@mixin playlist-below-player { 17@mixin playlist-below-player {
@@ -24,11 +24,11 @@ $video-info-margin-left: 44px;
24 24
25.root { 25.root {
26 &.theater-enabled #video-wrapper { 26 &.theater-enabled #video-wrapper {
27 $height: calc(100vh - #{$header-height} - #{$theater-bottom-space});
28
27 flex-direction: column; 29 flex-direction: column;
28 justify-content: center; 30 justify-content: center;
29 31
30 $height: calc(100vh - #{$header-height} - #{$theater-bottom-space});
31
32 #videojs-wrapper { 32 #videojs-wrapper {
33 width: 100%; 33 width: 100%;
34 height: $height; 34 height: $height;
@@ -141,7 +141,7 @@ $video-info-margin-left: 44px;
141 .video-info-first-row { 141 .video-info-first-row {
142 display: flex; 142 display: flex;
143 143
144 & > div:first-child { 144 > div:first-child {
145 flex-grow: 1; 145 flex-grow: 1;
146 } 146 }
147 147
@@ -207,7 +207,7 @@ $video-info-margin-left: 44px;
207 } 207 }
208 208
209 .video-actions-rates { 209 .video-actions-rates {
210 margin: 0 0 10px 0; 210 margin: 0 0 10px;
211 align-items: start; 211 align-items: start;
212 width: max-content; 212 width: max-content;
213 margin-left: auto; 213 margin-left: auto;
@@ -231,7 +231,7 @@ $video-info-margin-left: 44px;
231 font-size: 100%; 231 font-size: 100%;
232 font-weight: $font-semibold; 232 font-weight: $font-semibold;
233 display: inline-block; 233 display: inline-block;
234 padding: 0 10px 0 10px; 234 padding: 0 10px;
235 white-space: nowrap; 235 white-space: nowrap;
236 background-color: transparent !important; 236 background-color: transparent !important;
237 color: pvar(--actionButtonColor); 237 color: pvar(--actionButtonColor);
@@ -346,7 +346,8 @@ $video-info-margin-left: 44px;
346 } 346 }
347 } 347 }
348 348
349 .glyphicon, .description-loading { 349 .glyphicon,
350 .description-loading {
350 margin-left: 3px; 351 margin-left: 3px;
351 } 352 }
352 353
@@ -396,7 +397,7 @@ $video-info-margin-left: 44px;
396 &.video-attribute-tags { 397 &.video-attribute-tags {
397 .video-attribute-value:not(:nth-child(2)) { 398 .video-attribute-value:not(:nth-child(2)) {
398 &::before { 399 &::before {
399 content: ', ' 400 content: ', ';
400 } 401 }
401 } 402 }
402 } 403 }
diff --git a/client/src/app/+videos/+video-watch/video-watch.module.ts b/client/src/app/+videos/+video-watch/video-watch.module.ts
index cf6afd852..62ce7be2d 100644
--- a/client/src/app/+videos/+video-watch/video-watch.module.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.module.ts
@@ -20,7 +20,7 @@ import { TimestampRouteTransformerDirective } from './timestamp-route-transforme
20import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' 20import { VideoWatchPlaylistComponent } from './video-watch-playlist.component'
21import { VideoWatchRoutingModule } from './video-watch-routing.module' 21import { VideoWatchRoutingModule } from './video-watch-routing.module'
22import { VideoWatchComponent } from './video-watch.component' 22import { VideoWatchComponent } from './video-watch.component'
23import { SharedAccountAvatarModule } from '../../shared/shared-account-avatar/shared-account-avatar.module' 23import { SharedActorImageModule } from '../../shared/shared-actor-image/shared-actor-image.module'
24import { VideoAvatarChannelComponent } from './video-avatar-channel.component' 24import { VideoAvatarChannelComponent } from './video-avatar-channel.component'
25 25
26@NgModule({ 26@NgModule({
@@ -39,7 +39,7 @@ import { VideoAvatarChannelComponent } from './video-avatar-channel.component'
39 SharedShareModal, 39 SharedShareModal,
40 SharedVideoModule, 40 SharedVideoModule,
41 SharedSupportModal, 41 SharedSupportModal,
42 SharedAccountAvatarModule 42 SharedActorImageModule
43 ], 43 ],
44 44
45 declarations: [ 45 declarations: [
diff --git a/client/src/app/+videos/video-list/overview/video-overview.component.html b/client/src/app/+videos/video-list/overview/video-overview.component.html
index 639a96c43..e21bffb6c 100644
--- a/client/src/app/+videos/video-list/overview/video-overview.component.html
+++ b/client/src/app/+videos/video-list/overview/video-overview.component.html
@@ -33,7 +33,7 @@
33 <div class="section channel videos" *ngFor="let object of overview.channels"> 33 <div class="section channel videos" *ngFor="let object of overview.channels">
34 <div class="section-title"> 34 <div class="section-title">
35 <a [routerLink]="[ '/video-channels', buildVideoChannelBy(object) ]"> 35 <a [routerLink]="[ '/video-channels', buildVideoChannelBy(object) ]">
36 <img [src]="buildVideoChannelAvatarUrl(object)" alt="Avatar" /> 36 <my-actor-avatar [channel]="buildVideoChannel(object)"></my-actor-avatar>
37 37
38 <h2 class="section-title">{{ object.channel.displayName }}</h2> 38 <h2 class="section-title">{{ object.channel.displayName }}</h2>
39 </a> 39 </a>
diff --git a/client/src/app/+videos/video-list/overview/video-overview.component.scss b/client/src/app/+videos/video-list/overview/video-overview.component.scss
index ec73c628c..8fbac1b46 100644
--- a/client/src/app/+videos/video-list/overview/video-overview.component.scss
+++ b/client/src/app/+videos/video-list/overview/video-overview.component.scss
@@ -16,7 +16,7 @@
16 padding-top: 30px; 16 padding-top: 30px;
17 17
18 .section-title { 18 .section-title {
19 border-top: none !important; 19 border-top: 0 !important;
20 } 20 }
21 } 21 }
22 22
@@ -33,12 +33,14 @@
33 } 33 }
34 34
35 a { 35 a {
36 &:hover, &:focus:not(.focus-visible), &:active { 36 color: pvar(--mainForegroundColor);
37
38 &:hover,
39 &:focus:not(.focus-visible),
40 &:active {
37 text-decoration: none; 41 text-decoration: none;
38 outline: none; 42 outline: none;
39 } 43 }
40
41 color: pvar(--mainForegroundColor);
42 } 44 }
43 } 45 }
44 46
@@ -49,9 +51,10 @@
49 width: fit-content; 51 width: fit-content;
50 align-items: center; 52 align-items: center;
51 53
52 img { 54 my-actor-avatar {
53 @include channel-avatar(28px); 55 @include actor-avatar-size(28px);
54 56
57 font-size: initial;
55 margin-right: 8px; 58 margin-right: 8px;
56 } 59 }
57 } 60 }
diff --git a/client/src/app/+videos/video-list/overview/video-overview.component.ts b/client/src/app/+videos/video-list/overview/video-overview.component.ts
index b3be1d7b5..14532ca1e 100644
--- a/client/src/app/+videos/video-list/overview/video-overview.component.ts
+++ b/client/src/app/+videos/video-list/overview/video-overview.component.ts
@@ -45,8 +45,8 @@ export class VideoOverviewComponent implements OnInit {
45 return object.videos[0].byVideoChannel 45 return object.videos[0].byVideoChannel
46 } 46 }
47 47
48 buildVideoChannelAvatarUrl (object: { videos: Video[] }) { 48 buildVideoChannel (object: { videos: Video[] }) {
49 return object.videos[0].videoChannelAvatarUrl 49 return object.videos[0].channel
50 } 50 }
51 51
52 buildVideos (videos: Video[]) { 52 buildVideos (videos: Video[]) {
diff --git a/client/src/app/+videos/video-list/trending/video-trending-header.component.scss b/client/src/app/+videos/video-list/trending/video-trending-header.component.scss
index 923a1d67a..6daacc78e 100644
--- a/client/src/app/+videos/video-list/trending/video-trending-header.component.scss
+++ b/client/src/app/+videos/video-list/trending/video-trending-header.component.scss
@@ -14,4 +14,4 @@
14 height: 1rem; 14 height: 1rem;
15 margin-right: .1rem; 15 margin-right: .1rem;
16 } 16 }
17} \ No newline at end of file 17}
diff --git a/client/src/app/+videos/videos.module.ts b/client/src/app/+videos/videos.module.ts
index 61d012d63..8a35015d6 100644
--- a/client/src/app/+videos/videos.module.ts
+++ b/client/src/app/+videos/videos.module.ts
@@ -1,4 +1,5 @@
1import { NgModule } from '@angular/core' 1import { NgModule } from '@angular/core'
2import { SharedActorImageModule } from '@app/shared/shared-actor-image/shared-actor-image.module'
2import { SharedFormModule } from '@app/shared/shared-forms' 3import { SharedFormModule } from '@app/shared/shared-forms'
3import { SharedGlobalIconModule } from '@app/shared/shared-icons' 4import { SharedGlobalIconModule } from '@app/shared/shared-icons'
4import { SharedMainModule } from '@app/shared/shared-main' 5import { SharedMainModule } from '@app/shared/shared-main'
@@ -21,7 +22,8 @@ import { VideosComponent } from './videos.component'
21 SharedFormModule, 22 SharedFormModule,
22 SharedVideoMiniatureModule, 23 SharedVideoMiniatureModule,
23 SharedUserSubscriptionModule, 24 SharedUserSubscriptionModule,
24 SharedGlobalIconModule 25 SharedGlobalIconModule,
26 SharedActorImageModule
25 ], 27 ],
26 28
27 declarations: [ 29 declarations: [
diff --git a/client/src/app/app.component.scss b/client/src/app/app.component.scss
index e7d05369b..e21ada0f1 100644
--- a/client/src/app/app.component.scss
+++ b/client/src/app/app.component.scss
@@ -79,7 +79,7 @@
79 display: inline-block; 79 display: inline-block;
80 width: 23px; 80 width: 23px;
81 height: 24px; 81 height: 24px;
82 margin-right: .5rem; 82 margin-right: 0.5rem;
83 } 83 }
84 84
85 @media screen and (max-width: $mobile-view) { 85 @media screen and (max-width: $mobile-view) {
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts
index 41c59cc86..3cec6d739 100644
--- a/client/src/app/app.module.ts
+++ b/client/src/app/app.module.ts
@@ -24,7 +24,7 @@ import { SharedGlobalIconModule } from './shared/shared-icons'
24import { SharedInstanceModule } from './shared/shared-instance' 24import { SharedInstanceModule } from './shared/shared-instance'
25import { SharedMainModule } from './shared/shared-main' 25import { SharedMainModule } from './shared/shared-main'
26import { SharedUserInterfaceSettingsModule } from './shared/shared-user-settings' 26import { SharedUserInterfaceSettingsModule } from './shared/shared-user-settings'
27import { SharedAccountAvatarModule } from './shared/shared-account-avatar/shared-account-avatar.module' 27import { SharedActorImageModule } from './shared/shared-actor-image/shared-actor-image.module'
28 28
29registerLocaleData(localeOc, 'oc') 29registerLocaleData(localeOc, 'oc')
30 30
@@ -60,7 +60,7 @@ registerLocaleData(localeOc, 'oc')
60 SharedUserInterfaceSettingsModule, 60 SharedUserInterfaceSettingsModule,
61 SharedGlobalIconModule, 61 SharedGlobalIconModule,
62 SharedInstanceModule, 62 SharedInstanceModule,
63 SharedAccountAvatarModule, 63 SharedActorImageModule,
64 64
65 MetaModule.forRoot({ 65 MetaModule.forRoot({
66 provide: MetaLoader, 66 provide: MetaLoader,
diff --git a/client/src/app/core/hotkeys/hotkeys.component.scss b/client/src/app/core/hotkeys/hotkeys.component.scss
index a970260c9..b39ffa98d 100644
--- a/client/src/app/core/hotkeys/hotkeys.component.scss
+++ b/client/src/app/core/hotkeys/hotkeys.component.scss
@@ -12,16 +12,13 @@
12 left: 0; 12 left: 0;
13 color: #333; 13 color: #333;
14 font-size: 1em; 14 font-size: 1em;
15 background-color: rgba(255,255,255,0.9); 15 background-color: rgba(255, 255, 255, 0.9);
16} 16}
17 17
18.cfp-hotkeys-container.fade { 18.cfp-hotkeys-container.fade {
19 z-index: -1024; 19 z-index: -1024;
20 visibility: hidden; 20 visibility: hidden;
21 opacity: 0; 21 opacity: 0;
22 -webkit-transition: opacity 0.15s linear;
23 -moz-transition: opacity 0.15s linear;
24 -o-transition: opacity 0.15s linear;
25 transition: opacity 0.15s linear; 22 transition: opacity 0.15s linear;
26} 23}
27 24
diff --git a/client/src/app/core/rest/rest-table.ts b/client/src/app/core/rest/rest-table.ts
index 32c1db446..a5b48f10c 100644
--- a/client/src/app/core/rest/rest-table.ts
+++ b/client/src/app/core/rest/rest-table.ts
@@ -1,8 +1,6 @@
1import * as debug from 'debug' 1import * as debug from 'debug'
2import { LazyLoadEvent, SortMeta } from 'primeng/api' 2import { LazyLoadEvent, SortMeta } from 'primeng/api'
3import { Subject } from 'rxjs' 3import { ActivatedRoute, Router } from '@angular/router'
4import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
5import { ActivatedRoute, Params, Router } from '@angular/router'
6import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 4import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
7import { RestPagination } from './rest-pagination' 5import { RestPagination } from './rest-pagination'
8 6
@@ -14,14 +12,11 @@ export abstract class RestTable {
14 abstract sort: SortMeta 12 abstract sort: SortMeta
15 abstract pagination: RestPagination 13 abstract pagination: RestPagination
16 14
17 search: string
18 rowsPerPageOptions = [ 10, 20, 50, 100 ] 15 rowsPerPageOptions = [ 10, 20, 50, 100 ]
19 rowsPerPage = this.rowsPerPageOptions[0] 16 rowsPerPage = this.rowsPerPageOptions[0]
20 expandedRows = {} 17 expandedRows = {}
21 18
22 baseRoute: string 19 search: string
23
24 protected searchStream: Subject<string>
25 20
26 protected route: ActivatedRoute 21 protected route: ActivatedRoute
27 protected router: Router 22 protected router: Router
@@ -30,7 +25,6 @@ export abstract class RestTable {
30 25
31 initialize () { 26 initialize () {
32 this.loadSort() 27 this.loadSort()
33 this.initSearch()
34 } 28 }
35 29
36 loadSort () { 30 loadSort () {
@@ -58,7 +52,7 @@ export abstract class RestTable {
58 count: this.rowsPerPage 52 count: this.rowsPerPage
59 } 53 }
60 54
61 this.loadData() 55 this.reloadData()
62 this.saveSort() 56 this.saveSort()
63 } 57 }
64 58
@@ -66,55 +60,6 @@ export abstract class RestTable {
66 peertubeLocalStorage.setItem(this.getSortLocalStorageKey(), JSON.stringify(this.sort)) 60 peertubeLocalStorage.setItem(this.getSortLocalStorageKey(), JSON.stringify(this.sort))
67 } 61 }
68 62
69 initSearch () {
70 this.searchStream = new Subject()
71
72 this.searchStream
73 .pipe(
74 debounceTime(400),
75 distinctUntilChanged()
76 )
77 .subscribe(search => {
78 this.search = search
79
80 logger('On search %s.', this.search)
81
82 this.loadData()
83 })
84 }
85
86 onSearch (event: Event) {
87 const target = event.target as HTMLInputElement
88 this.searchStream.next(target.value)
89
90 this.setQueryParams((event.target as HTMLInputElement).value)
91 }
92
93 setQueryParams (search: string) {
94 if (!this.baseRoute) return
95
96 const queryParams: Params = {}
97
98 if (search) Object.assign(queryParams, { search })
99 this.router.navigate([ this.baseRoute ], { queryParams })
100 }
101
102 resetTableFilter () {
103 this.setTableFilter('')
104 this.setQueryParams('')
105 this.resetSearch()
106 }
107
108 listenToSearchChange () {
109 this.route.queryParams
110 .subscribe(params => {
111 this.search = params.search || ''
112
113 // Primeng table will run an event to load data
114 this.setTableFilter(this.search)
115 })
116 }
117
118 onPage (event: { first: number, rows: number }) { 63 onPage (event: { first: number, rows: number }) {
119 logger('On page %o.', event) 64 logger('On page %o.', event)
120 65
@@ -125,28 +70,18 @@ export abstract class RestTable {
125 count: this.rowsPerPage 70 count: this.rowsPerPage
126 } 71 }
127 72
128 this.loadData() 73 this.reloadData()
129 } 74 }
130 75
131 this.expandedRows = {} 76 this.expandedRows = {}
132 } 77 }
133 78
134 setTableFilter (filter: string, triggerEvent = true) { 79 onSearch (search: string) {
135 // FIXME: cannot use ViewChild, so create a component for the filter input 80 this.search = search
136 const filterInput = document.getElementById('table-filter') as HTMLInputElement 81 this.reloadData()
137 if (!filterInput) return
138
139 filterInput.value = filter
140
141 if (triggerEvent) filterInput.dispatchEvent(new Event('keyup'))
142 }
143
144 resetSearch () {
145 this.searchStream.next('')
146 this.setTableFilter('')
147 } 82 }
148 83
149 protected abstract loadData (): void 84 protected abstract reloadData (): void
150 85
151 private getSortLocalStorageKey () { 86 private getSortLocalStorageKey () {
152 return 'rest-table-sort-' + this.getIdentifier() 87 return 'rest-table-sort-' + this.getIdentifier()
diff --git a/client/src/app/core/rest/rest.service.ts b/client/src/app/core/rest/rest.service.ts
index 4f1fc8848..1696e6709 100644
--- a/client/src/app/core/rest/rest.service.ts
+++ b/client/src/app/core/rest/rest.service.ts
@@ -90,14 +90,20 @@ export class RestService {
90 90
91 const matchedTokens = tokens.filter(t => t.startsWith(prefix)) 91 const matchedTokens = tokens.filter(t => t.startsWith(prefix))
92 .map(t => t.slice(prefix.length)) // Keep the value filter 92 .map(t => t.slice(prefix.length)) // Keep the value filter
93 .map(t => t.replace(/^"|"$/g, '')) 93 .map(t => t.replace(/^"|"$/g, '')) // Remove ""
94 .map(t => { 94 .map(t => {
95 if (prefixObj.handler) return prefixObj.handler(t) 95 if (prefixObj.handler) return prefixObj.handler(t)
96 96
97 if (prefixObj.isBoolean) {
98 if (t === 'true') return true
99 if (t === 'false') return false
100
101 return undefined
102 }
103
97 return t 104 return t
98 }) 105 })
99 .filter(t => !!t || t === 0) 106 .filter(t => t !== null && t !== undefined)
100 .map(t => prefixObj.isBoolean ? t === 'true' : t)
101 107
102 if (matchedTokens.length === 0) continue 108 if (matchedTokens.length === 0) continue
103 109
diff --git a/client/src/app/core/users/user.service.ts b/client/src/app/core/users/user.service.ts
index 3de83152c..47db985e1 100644
--- a/client/src/app/core/users/user.service.ts
+++ b/client/src/app/core/users/user.service.ts
@@ -320,13 +320,7 @@ export class UserService {
320 const filters = this.restService.parseQueryStringFilter(search, { 320 const filters = this.restService.parseQueryStringFilter(search, {
321 blocked: { 321 blocked: {
322 prefix: 'banned:', 322 prefix: 'banned:',
323 isBoolean: true, 323 isBoolean: true
324 handler: v => {
325 if (v === 'true') return v
326 if (v === 'false') return v
327
328 return undefined
329 }
330 } 324 }
331 }) 325 })
332 326
diff --git a/client/src/app/core/wrappers/screen.service.ts b/client/src/app/core/wrappers/screen.service.ts
index c133b5fe9..fd8268b35 100644
--- a/client/src/app/core/wrappers/screen.service.ts
+++ b/client/src/app/core/wrappers/screen.service.ts
@@ -39,9 +39,9 @@ export class ScreenService {
39 let numberOfVideos = 1 39 let numberOfVideos = 1
40 40
41 if (screenWidth > 1850) numberOfVideos = 5 41 if (screenWidth > 1850) numberOfVideos = 5
42 else if (screenWidth > 1600) numberOfVideos = 4 42 else if (screenWidth > 1410) numberOfVideos = 4
43 else if (screenWidth > 1370) numberOfVideos = 3 43 else if (screenWidth > 1120) numberOfVideos = 3
44 else if (screenWidth > 1100) numberOfVideos = 2 44 else if (screenWidth > 890) numberOfVideos = 2
45 45
46 return numberOfVideos 46 return numberOfVideos
47 } 47 }
diff --git a/client/src/app/header/search-typeahead.component.scss b/client/src/app/header/search-typeahead.component.scss
index c754a99d1..3e0350ba0 100644
--- a/client/src/app/header/search-typeahead.component.scss
+++ b/client/src/app/header/search-typeahead.component.scss
@@ -44,7 +44,8 @@ li.suggestion {
44 44
45 // soft border-radius for the last suggestion and the link inside 45 // soft border-radius for the last suggestion and the link inside
46 &:last-of-type { 46 &:last-of-type {
47 &, & ::ng-deep a { 47 &,
48 ::ng-deep a {
48 border-bottom-right-radius: 3px; 49 border-bottom-right-radius: 3px;
49 border-bottom-left-radius: 3px; 50 border-bottom-left-radius: 3px;
50 } 51 }
@@ -74,7 +75,7 @@ li.suggestion {
74#typeahead-container { 75#typeahead-container {
75 input { 76 input {
76 border: 1px solid pvar(--mainBackgroundColor) !important; 77 border: 1px solid pvar(--mainBackgroundColor) !important;
77 box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 20px 0px; 78 box-shadow: rgba(0, 0, 0, 0.1) 0 1px 20px 0;
78 flex-grow: 1; 79 flex-grow: 1;
79 transition: box-shadow .3s ease, width .2s ease; 80 transition: box-shadow .3s ease, width .2s ease;
80 } 81 }
@@ -95,7 +96,7 @@ li.suggestion {
95 right: 10px; 96 right: 10px;
96 } 97 }
97 98
98 & > div:last-child { 99 > div:last-child {
99 // we have to switch the display and not the opacity, 100 // we have to switch the display and not the opacity,
100 // to avoid clashing with the rest of the interface. 101 // to avoid clashing with the rest of the interface.
101 display: none; 102 display: none;
@@ -103,7 +104,7 @@ li.suggestion {
103 104
104 &:focus, 105 &:focus,
105 ::ng-deep &:focus-within { 106 ::ng-deep &:focus-within {
106 & > div:last-child { 107 > div:last-child {
107 @media screen and (min-width: $mobile-view) { 108 @media screen and (min-width: $mobile-view) {
108 display: initial !important; 109 display: initial !important;
109 } 110 }
@@ -111,12 +112,12 @@ li.suggestion {
111 #typeahead-help, 112 #typeahead-help,
112 #typeahead-instructions, 113 #typeahead-instructions,
113 li.suggestion { 114 li.suggestion {
114 box-shadow: rgba(0, 0, 0, 0.2) 0px 10px 20px -5px; 115 box-shadow: rgba(0, 0, 0, 0.2) 0 10px 20px -5px;
115 } 116 }
116 } 117 }
117 118
118 ::ng-deep input { 119 ::ng-deep input {
119 box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 20px 0px; 120 box-shadow: rgba(0, 0, 0, 0.2) 0 1px 20px 0;
120 border-end-start-radius: 0; 121 border-end-start-radius: 0;
121 border-end-end-radius: 0; 122 border-end-end-radius: 0;
122 123
diff --git a/client/src/app/header/suggestion.component.scss b/client/src/app/header/suggestion.component.scss
index 692a81daa..9163de0b1 100644
--- a/client/src/app/header/suggestion.component.scss
+++ b/client/src/app/header/suggestion.component.scss
@@ -2,9 +2,11 @@
2 2
3a { 3a {
4 @include disable-default-a-behaviour; 4 @include disable-default-a-behaviour;
5
5 width: 100%; 6 width: 100%;
6 7
7 &, &:hover { 8 &,
9 &:hover {
8 color: pvar(--mainForegroundColor); 10 color: pvar(--mainForegroundColor);
9 11
10 &.focus-visible { 12 &.focus-visible {
@@ -23,10 +25,10 @@ a {
23} 25}
24 26
25my-global-icon { 27my-global-icon {
28 @include apply-svg-color(pvar(--mainForegroundColor));
29
26 width: 17px; 30 width: 17px;
27 position: relative; 31 position: relative;
28 top: -2px; 32 top: -2px;
29 margin: 5px; 33 margin: 5px;
30
31 @include apply-svg-color(pvar(--mainForegroundColor));
32} 34}
diff --git a/client/src/app/menu/language-chooser.component.scss b/client/src/app/menu/language-chooser.component.scss
index 6226a85cb..800b1ebef 100644
--- a/client/src/app/menu/language-chooser.component.scss
+++ b/client/src/app/menu/language-chooser.component.scss
@@ -5,12 +5,12 @@
5 @include peertube-button-link; 5 @include peertube-button-link;
6 @include orange-button; 6 @include orange-button;
7 7
8 border-radius: 0;
9
8 &.focus-visible, 10 &.focus-visible,
9 &:focus { 11 &:focus {
10 box-shadow: none; 12 box-shadow: none;
11 } 13 }
12
13 border-radius: 0;
14} 14}
15 15
16.modal-body { 16.modal-body {
diff --git a/client/src/app/menu/menu.component.html b/client/src/app/menu/menu.component.html
index df5c7971d..2e07deca2 100644
--- a/client/src/app/menu/menu.component.html
+++ b/client/src/app/menu/menu.component.html
@@ -5,7 +5,7 @@
5 <div> 5 <div>
6 <div class="logged-in-more" ngbDropdown #dropdown="ngbDropdown" placement="bottom-left" [container]="dropdownContainer" (openChange)="onDropdownOpenChange($event)" autoClose="outside"> 6 <div class="logged-in-more" ngbDropdown #dropdown="ngbDropdown" placement="bottom-left" [container]="dropdownContainer" (openChange)="onDropdownOpenChange($event)" autoClose="outside">
7 <div ngbDropdownToggle> 7 <div ngbDropdownToggle>
8 <my-account-avatar [account]="user.account" size="34"></my-account-avatar> 8 <my-actor-avatar [account]="user.account" size="34"></my-actor-avatar>
9 <div class="logged-in-info"> 9 <div class="logged-in-info">
10 <div class="logged-in-display-name">{{ user.account?.displayName }}</div> 10 <div class="logged-in-display-name">{{ user.account?.displayName }}</div>
11 11
diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss
index 00d1a1f69..d0edd820e 100644
--- a/client/src/app/menu/menu.component.scss
+++ b/client/src/app/menu/menu.component.scss
@@ -24,8 +24,9 @@ $footer-links-base-opacity: .8;
24 background-color: rgba(255, 255, 255, 0.15); 24 background-color: rgba(255, 255, 255, 0.15);
25 } 25 }
26 26
27 &:hover, &.focus-visible { 27 &:hover,
28 background-color: rgba(255, 255, 255, 0.10); 28 &.focus-visible {
29 background-color: rgba(255, 255, 255, 0.1);
29 } 30 }
30 31
31 my-global-icon { 32 my-global-icon {
@@ -60,7 +61,8 @@ menu {
60 margin: 0; 61 margin: 0;
61 padding: 0; 62 padding: 0;
62 63
63 &:focus, &:hover { 64 &:focus,
65 &:hover {
64 overflow-y: auto; 66 overflow-y: auto;
65 } 67 }
66 68
@@ -125,7 +127,7 @@ my-notification {
125 line-height: 1; 127 line-height: 1;
126 128
127 &.show { 129 &.show {
128 background-color: rgba(255, 255, 255, 0.20); 130 background-color: rgba(255, 255, 255, 0.2);
129 box-shadow: inset 0 3px 5px rgba(0, 0, 0, .325); 131 box-shadow: inset 0 3px 5px rgba(0, 0, 0, .325);
130 } 132 }
131 133
@@ -158,14 +160,14 @@ my-notification {
158 position: absolute; 160 position: absolute;
159 right: -35px; 161 right: -35px;
160 top: -8px; 162 top: -8px;
161 color: grey; 163 color: #808080;
162 width: $main-radius; 164 width: $main-radius;
163 } 165 }
164 } 166 }
165 167
166 .dropdown-toggle { 168 .dropdown-toggle {
167 &::after { 169 &::after {
168 border: none; 170 border: 0;
169 } 171 }
170 } 172 }
171 173
@@ -177,7 +179,7 @@ my-notification {
177 } 179 }
178} 180}
179 181
180my-account-avatar { 182my-actor-avatar {
181 margin-right: 10px; 183 margin-right: 10px;
182} 184}
183 185
@@ -193,11 +195,11 @@ my-account-avatar {
193} 195}
194 196
195.logged-in-display-name { 197.logged-in-display-name {
198 @include disable-default-a-behaviour;
199
196 font-size: 16px; 200 font-size: 16px;
197 font-weight: $font-semibold; 201 font-weight: $font-semibold;
198 color: pvar(--menuForegroundColor); 202 color: pvar(--menuForegroundColor);
199
200 @include disable-default-a-behaviour;
201} 203}
202 204
203.logged-in-username { 205.logged-in-username {
@@ -251,7 +253,7 @@ my-account-avatar {
251} 253}
252 254
253.login-buttons-block { 255.login-buttons-block {
254 margin: 30px 25px 35px 25px; 256 margin: 30px 25px 35px;
255 257
256 > a { 258 > a {
257 display: block; 259 display: block;
@@ -305,7 +307,8 @@ my-account-avatar {
305} 307}
306 308
307.footer-links { 309.footer-links {
308 &, > div { 310 &,
311 > div {
309 display: flex; 312 display: flex;
310 flex-wrap: wrap; 313 flex-wrap: wrap;
311 } 314 }
@@ -388,29 +391,29 @@ my-account-avatar {
388 .dropdown-item:hover, 391 .dropdown-item:hover,
389 .dropdown-item:active { 392 .dropdown-item:active {
390 &.settings-sensitive my-global-icon ::ng-deep svg { 393 &.settings-sensitive my-global-icon ::ng-deep svg {
391 margin-top: 0px !important; 394 margin-top: 0 !important;
392 } 395 }
393 } 396 }
394} 397}
395 398
396my-global-icon { 399my-global-icon {
397 &[iconName="playlists"] { 400 &[iconName=playlists] {
398 height: 24px; 401 height: 24px;
399 width: 24px; 402 width: 24px;
400 403
401 margin-right: 16px; 404 margin-right: 16px;
402 } 405 }
403 406
404 &[iconName="videos"] { 407 &[iconName=videos] {
405 position: relative; 408 position: relative;
406 right: -1px; 409 right: -1px;
407 } 410 }
408 411
409 &[iconName="channel"] { 412 &[iconName=channel] {
410 margin-top: -2px; 413 margin-top: -2px;
411 } 414 }
412 415
413 &[iconName="sign-out"] { 416 &[iconName='sign-out'] {
414 position: relative; 417 position: relative;
415 right: -2px; 418 right: -2px;
416 height: 20px; 419 height: 20px;
diff --git a/client/src/app/menu/notification.component.scss b/client/src/app/menu/notification.component.scss
index c65787779..554c20ca9 100644
--- a/client/src/app/menu/notification.component.scss
+++ b/client/src/app/menu/notification.component.scss
@@ -16,19 +16,20 @@
16.notification-inbox-popover, 16.notification-inbox-popover,
17.notification-inbox-link a { 17.notification-inbox-link a {
18 @include apply-svg-color(#808080); 18 @include apply-svg-color(#808080);
19 ::ng-deep {
20 svg {
21 transition: color .1s ease-in-out;
22 }
23 }
24 19
25 transition: all .1s ease-in-out; 20 transition: all .1s ease-in-out;
26 border-radius: 25px; 21 border-radius: 25px;
27 cursor: pointer; 22 cursor: pointer;
28 23
29 &:hover, &:active { 24 ::ng-deep svg {
30 background-color: rgba(255, 255, 255, 0.15); 25 transition: color .1s ease-in-out;
26 }
27
28 &:hover,
29 &:active {
31 @include apply-svg-color(#fff); 30 @include apply-svg-color(#fff);
31
32 background-color: rgba(255, 255, 255, 0.15);
32 } 33 }
33} 34}
34 35
@@ -59,7 +60,7 @@
59 font-size: 14px; 60 font-size: 14px;
60 font-family: $main-fonts; 61 font-family: $main-fonts;
61 width: 400px; 62 width: 400px;
62 box-shadow: 0 2px 6px rgba(0, 0, 0, 0.30); 63 box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
63 64
64 .loader { 65 .loader {
65 display: flex; 66 display: flex;
@@ -80,7 +81,7 @@
80 max-height: 500px; 81 max-height: 500px;
81 } 82 }
82 83
83 & > my-user-notifications:nth-child(2) { 84 > my-user-notifications:nth-child(2) {
84 overflow-y: auto; 85 overflow-y: auto;
85 flex-grow: 1; 86 flex-grow: 1;
86 } 87 }
@@ -110,7 +111,8 @@
110 background: transparent; 111 background: transparent;
111 } 112 }
112 113
113 a, button { 114 a,
115 button {
114 color: rgba(20, 20, 20, 0.5); 116 color: rgba(20, 20, 20, 0.5);
115 117
116 &:hover:not(:disabled) { 118 &:hover:not(:disabled) {
@@ -133,7 +135,8 @@
133 } 135 }
134} 136}
135 137
136.notification-inbox-popover, .notification-inbox-link { 138.notification-inbox-popover,
139.notification-inbox-link {
137 cursor: pointer; 140 cursor: pointer;
138 position: relative; 141 position: relative;
139 142
diff --git a/client/src/app/modal/welcome-modal.component.scss b/client/src/app/modal/welcome-modal.component.scss
index 28d5dc49c..5e9e3dc51 100644
--- a/client/src/app/modal/welcome-modal.component.scss
+++ b/client/src/app/modal/welcome-modal.component.scss
@@ -42,7 +42,7 @@ li {
42 text-align: center; 42 text-align: center;
43 font-weight: 600; 43 font-weight: 600;
44 font-size: 18px; 44 font-size: 18px;
45 margin: 20px 0 40px 0; 45 margin: 20px 0 40px;
46} 46}
47 47
48.columns { 48.columns {
diff --git a/client/src/app/shared/shared-abuse-list/abuse-details.component.html b/client/src/app/shared/shared-abuse-list/abuse-details.component.html
index 658d42537..ca68de4b1 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-details.component.html
+++ b/client/src/app/shared/shared-abuse-list/abuse-details.component.html
@@ -7,16 +7,16 @@
7 <span class="col-3 moderation-expanded-label" i18n>Reporter</span> 7 <span class="col-3 moderation-expanded-label" i18n>Reporter</span>
8 8
9 <span class="col-9 moderation-expanded-text"> 9 <span class="col-9 moderation-expanded-text">
10 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reporter:&quot;' + abuse.reporterAccount.displayName + '&quot;' }" 10 <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reporter:&quot;' + abuse.reporterAccount.displayName + '&quot;' }"
11 class="chip" 11 class="chip"
12 > 12 >
13 <my-account-avatar [account]="abuse.reporterAccount"></my-account-avatar> 13 <my-actor-avatar size="18" [account]="abuse.reporterAccount"></my-actor-avatar>
14 <div> 14 <div>
15 <span class="text-muted">{{ abuse.reporterAccount.nameWithHost }}</span> 15 <span class="text-muted">{{ abuse.reporterAccount.nameWithHost }}</span>
16 </div> 16 </div>
17 </a> 17 </a>
18 18
19 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reporter:&quot;' + abuse.reporterAccount.displayName + '&quot;' }" 19 <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reporter:&quot;' + abuse.reporterAccount.displayName + '&quot;' }"
20 class="ml-auto text-muted abuse-details-links" i18n 20 class="ml-auto text-muted abuse-details-links" i18n
21 > 21 >
22 {abuse.countReportsForReporter, plural, =1 {1 report} other {{{ abuse.countReportsForReporter }} reports}}<span class="ml-1 glyphicon glyphicon-flag"></span> 22 {abuse.countReportsForReporter, plural, =1 {1 report} other {{{ abuse.countReportsForReporter }} reports}}<span class="ml-1 glyphicon glyphicon-flag"></span>
@@ -27,16 +27,16 @@
27 <div class="d-flex" *ngIf="abuse.flaggedAccount"> 27 <div class="d-flex" *ngIf="abuse.flaggedAccount">
28 <span class="col-3 moderation-expanded-label" i18n>Reportee</span> 28 <span class="col-3 moderation-expanded-label" i18n>Reportee</span>
29 <span class="col-9 moderation-expanded-text"> 29 <span class="col-9 moderation-expanded-text">
30 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reportee:&quot;' +abuse.flaggedAccount.displayName + '&quot;' }" 30 <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reportee:&quot;' +abuse.flaggedAccount.displayName + '&quot;' }"
31 class="chip" 31 class="chip"
32 > 32 >
33 <my-account-avatar [account]="abuse.flaggedAccount"></my-account-avatar> 33 <my-actor-avatar size="18" [account]="abuse.flaggedAccount"></my-actor-avatar>
34 <div> 34 <div>
35 <span class="text-muted">{{ abuse.flaggedAccount ? abuse.flaggedAccount.nameWithHost : '' }}</span> 35 <span class="text-muted">{{ abuse.flaggedAccount ? abuse.flaggedAccount.nameWithHost : '' }}</span>
36 </div> 36 </div>
37 </a> 37 </a>
38 38
39 <a *ngIf="isAdminView" [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reportee:&quot;' +abuse.flaggedAccount.displayName + '&quot;' }" 39 <a *ngIf="isAdminView" [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reportee:&quot;' +abuse.flaggedAccount.displayName + '&quot;' }"
40 class="ml-auto text-muted abuse-details-links" i18n 40 class="ml-auto text-muted abuse-details-links" i18n
41 > 41 >
42 {abuse.countReportsForReportee, plural, =1 {1 report} other {{{ abuse.countReportsForReportee }} reports}}<span class="ml-1 glyphicon glyphicon-flag"></span> 42 {abuse.countReportsForReportee, plural, =1 {1 report} other {{{ abuse.countReportsForReportee }} reports}}<span class="ml-1 glyphicon glyphicon-flag"></span>
@@ -53,7 +53,7 @@
53 <div class="mt-3 d-flex"> 53 <div class="mt-3 d-flex">
54 <span class="col-3 moderation-expanded-label"> 54 <span class="col-3 moderation-expanded-label">
55 <ng-container i18n>Report</ng-container> 55 <ng-container i18n>Report</ng-container>
56 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': '#' + abuse.id }" class="ml-1 text-muted">#{{ abuse.id }}</a> 56 <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': '#' + abuse.id }" class="ml-1 text-muted">#{{ abuse.id }}</a>
57 </span> 57 </span>
58 <span class="col-9 moderation-expanded-text" [innerHTML]="abuse.reasonHtml"></span> 58 <span class="col-9 moderation-expanded-text" [innerHTML]="abuse.reasonHtml"></span>
59 </div> 59 </div>
@@ -61,7 +61,7 @@
61 <div *ngIf="getPredefinedReasons()" class="mt-2 d-flex"> 61 <div *ngIf="getPredefinedReasons()" class="mt-2 d-flex">
62 <span class="col-3"></span> 62 <span class="col-3"></span>
63 <span class="col-9"> 63 <span class="col-9">
64 <a *ngFor="let reason of getPredefinedReasons()" [routerLink]="[ baseRoute ]" 64 <a *ngFor="let reason of getPredefinedReasons()" [routerLink]="[ '.' ]"
65 [queryParams]="{ 'search': 'tag:' + reason.id }" class="chip rectangular bg-secondary text-light" 65 [queryParams]="{ 'search': 'tag:' + reason.id }" class="chip rectangular bg-secondary text-light"
66 > 66 >
67 <div>{{ reason.label }}</div> 67 <div>{{ reason.label }}</div>
diff --git a/client/src/app/shared/shared-abuse-list/abuse-details.component.ts b/client/src/app/shared/shared-abuse-list/abuse-details.component.ts
index e8ce7e678..14674c5f0 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-details.component.ts
+++ b/client/src/app/shared/shared-abuse-list/abuse-details.component.ts
@@ -1,6 +1,5 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input } from '@angular/core'
2import { durationToString } from '@app/helpers' 2import { durationToString } from '@app/helpers'
3import { Account } from '@app/shared/shared-main'
4import { AbusePredefinedReasonsString } from '@shared/models' 3import { AbusePredefinedReasonsString } from '@shared/models'
5import { ProcessedAbuse } from './processed-abuse.model' 4import { ProcessedAbuse } from './processed-abuse.model'
6 5
@@ -12,7 +11,6 @@ import { ProcessedAbuse } from './processed-abuse.model'
12export class AbuseDetailsComponent { 11export class AbuseDetailsComponent {
13 @Input() abuse: ProcessedAbuse 12 @Input() abuse: ProcessedAbuse
14 @Input() isAdminView: boolean 13 @Input() isAdminView: boolean
15 @Input() baseRoute: string
16 14
17 private predefinedReasonsTranslations: { [key in AbusePredefinedReasonsString]: string } 15 private predefinedReasonsTranslations: { [key in AbusePredefinedReasonsString]: string }
18 16
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html
index 29b51f09c..b1c065c7a 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html
+++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html
@@ -1,6 +1,7 @@
1<p-table 1<p-table
2 [value]="abuses" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 2 [value]="abuses" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
3 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" [resizableColumns]="true" 3 [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true"
4 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
4 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 5 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
5 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} reports" 6 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} reports"
6 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" 7 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
@@ -8,28 +9,7 @@
8 <ng-template pTemplate="caption"> 9 <ng-template pTemplate="caption">
9 <div class="caption"> 10 <div class="caption">
10 <div class="ml-auto"> 11 <div class="ml-auto">
11 <div class="input-group has-feedback has-clear"> 12 <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter>
12 <div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
13 <div class="input-group-text" ngbDropdownToggle>
14 <span class="caret" aria-haspopup="menu" role="button"></span>
15 </div>
16
17 <div role="menu" ngbDropdownMenu>
18 <h6 class="dropdown-header" i18n>Advanced report filters</h6>
19 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'state:pending' }" class="dropdown-item" i18n>Unsolved reports</a>
20 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'state:accepted' }" class="dropdown-item" i18n>Accepted reports</a>
21 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'state:rejected' }" class="dropdown-item" i18n>Refused reports</a>
22 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'videoIs:blacklisted' }" class="dropdown-item" i18n>Reports with blocked videos</a>
23 <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'videoIs:deleted' }" class="dropdown-item" i18n>Reports with deleted videos</a>
24 </div>
25 </div>
26 <input
27 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
28 (keyup)="onSearch($event)"
29 >
30 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetTableFilter()"></a>
31 <span class="sr-only" i18n>Clear filters</span>
32 </div>
33 </div> 13 </div>
34 </div> 14 </div>
35 </ng-template> 15 </ng-template>
@@ -65,7 +45,7 @@
65 <td *ngIf="isAdminView()"> 45 <td *ngIf="isAdminView()">
66 <a *ngIf="abuse.reporterAccount" [href]="abuse.reporterAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> 46 <a *ngIf="abuse.reporterAccount" [href]="abuse.reporterAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
67 <div class="chip two-lines"> 47 <div class="chip two-lines">
68 <my-account-avatar [account]="abuse.reporterAccount"></my-account-avatar> 48 <my-actor-avatar [account]="abuse.reporterAccount"></my-actor-avatar>
69 <div> 49 <div>
70 {{ abuse.reporterAccount.displayName }} 50 {{ abuse.reporterAccount.displayName }}
71 <span>{{ abuse.reporterAccount.nameWithHost }}</span> 51 <span>{{ abuse.reporterAccount.nameWithHost }}</span>
@@ -171,7 +151,7 @@
171 <ng-template pTemplate="rowexpansion" let-abuse> 151 <ng-template pTemplate="rowexpansion" let-abuse>
172 <tr> 152 <tr>
173 <td class="expand-cell" colspan="8"> 153 <td class="expand-cell" colspan="8">
174 <my-abuse-details [abuse]="abuse" [baseRoute]="baseRoute" [isAdminView]="isAdminView()"></my-abuse-details> 154 <my-abuse-details [abuse]="abuse" [isAdminView]="isAdminView()"></my-abuse-details>
175 </td> 155 </td>
176 </tr> 156 </tr>
177 </ng-template> 157 </ng-template>
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
index 8b5771237..4dc2b4f10 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
+++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
@@ -3,7 +3,7 @@ import truncate from 'lodash-es/truncate'
3import { SortMeta } from 'primeng/api' 3import { SortMeta } from 'primeng/api'
4import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' 4import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
5import { environment } from 'src/environments/environment' 5import { environment } from 'src/environments/environment'
6import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core' 6import { Component, Input, OnInit, ViewChild } from '@angular/core'
7import { DomSanitizer } from '@angular/platform-browser' 7import { DomSanitizer } from '@angular/platform-browser'
8import { ActivatedRoute, Router } from '@angular/router' 8import { ActivatedRoute, Router } from '@angular/router'
9import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' 9import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core'
@@ -11,6 +11,7 @@ import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared
11import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation' 11import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
12import { VideoCommentService } from '@app/shared/shared-video-comment' 12import { VideoCommentService } from '@app/shared/shared-video-comment'
13import { AbuseState, AdminAbuse } from '@shared/models' 13import { AbuseState, AdminAbuse } from '@shared/models'
14import { AdvancedInputFilter } from '../shared-forms'
14import { AbuseMessageModalComponent } from './abuse-message-modal.component' 15import { AbuseMessageModalComponent } from './abuse-message-modal.component'
15import { ModerationCommentModalComponent } from './moderation-comment-modal.component' 16import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
16import { ProcessedAbuse } from './processed-abuse.model' 17import { ProcessedAbuse } from './processed-abuse.model'
@@ -22,9 +23,8 @@ const logger = debug('peertube:moderation:AbuseListTableComponent')
22 templateUrl: './abuse-list-table.component.html', 23 templateUrl: './abuse-list-table.component.html',
23 styleUrls: [ '../shared-moderation/moderation.scss', './abuse-list-table.component.scss' ] 24 styleUrls: [ '../shared-moderation/moderation.scss', './abuse-list-table.component.scss' ]
24}) 25})
25export class AbuseListTableComponent extends RestTable implements OnInit, AfterViewInit { 26export class AbuseListTableComponent extends RestTable implements OnInit {
26 @Input() viewType: 'admin' | 'user' 27 @Input() viewType: 'admin' | 'user'
27 @Input() baseRoute: string
28 28
29 @ViewChild('abuseMessagesModal', { static: true }) abuseMessagesModal: AbuseMessageModalComponent 29 @ViewChild('abuseMessagesModal', { static: true }) abuseMessagesModal: AbuseMessageModalComponent
30 @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent 30 @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent
@@ -36,6 +36,29 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
36 36
37 abuseActions: DropdownAction<ProcessedAbuse>[][] = [] 37 abuseActions: DropdownAction<ProcessedAbuse>[][] = []
38 38
39 inputFilters: AdvancedInputFilter[] = [
40 {
41 queryParams: { 'search': 'state:pending' },
42 label: $localize`Unsolved reports`
43 },
44 {
45 queryParams: { 'search': 'state:accepted' },
46 label: $localize`Accepted reports`
47 },
48 {
49 queryParams: { 'search': 'state:rejected' },
50 label: $localize`Refused reports`
51 },
52 {
53 queryParams: { 'search': 'videoIs:blacklisted' },
54 label: $localize`Reports with blocked videos`
55 },
56 {
57 queryParams: { 'search': 'videoIs:deleted' },
58 label: $localize`Reports with deleted videos`
59 }
60 ]
61
39 constructor ( 62 constructor (
40 protected route: ActivatedRoute, 63 protected route: ActivatedRoute,
41 protected router: Router, 64 protected router: Router,
@@ -66,11 +89,6 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
66 ] 89 ]
67 90
68 this.initialize() 91 this.initialize()
69 this.listenToSearchChange()
70 }
71
72 ngAfterViewInit () {
73 if (this.search) this.setTableFilter(this.search, false)
74 } 92 }
75 93
76 isAdminView () { 94 isAdminView () {
@@ -86,7 +104,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
86 } 104 }
87 105
88 onModerationCommentUpdated () { 106 onModerationCommentUpdated () {
89 this.loadData() 107 this.reloadData()
90 } 108 }
91 109
92 isAbuseAccepted (abuse: AdminAbuse) { 110 isAbuseAccepted (abuse: AdminAbuse) {
@@ -129,7 +147,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
129 this.abuseService.removeAbuse(abuse).subscribe( 147 this.abuseService.removeAbuse(abuse).subscribe(
130 () => { 148 () => {
131 this.notifier.success($localize`Abuse deleted.`) 149 this.notifier.success($localize`Abuse deleted.`)
132 this.loadData() 150 this.reloadData()
133 }, 151 },
134 152
135 err => this.notifier.error(err.message) 153 err => this.notifier.error(err.message)
@@ -139,7 +157,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
139 updateAbuseState (abuse: AdminAbuse, state: AbuseState) { 157 updateAbuseState (abuse: AdminAbuse, state: AbuseState) {
140 this.abuseService.updateAbuse(abuse, { state }) 158 this.abuseService.updateAbuse(abuse, { state })
141 .subscribe( 159 .subscribe(
142 () => this.loadData(), 160 () => this.reloadData(),
143 161
144 err => this.notifier.error(err.message) 162 err => this.notifier.error(err.message)
145 ) 163 )
@@ -166,7 +184,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
166 return Actor.IS_LOCAL(abuse.reporterAccount.host) 184 return Actor.IS_LOCAL(abuse.reporterAccount.host)
167 } 185 }
168 186
169 protected loadData () { 187 protected reloadData () {
170 logger('Loading data.') 188 logger('Loading data.')
171 189
172 const options = { 190 const options = {
diff --git a/client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts b/client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts
index 19b6d456d..8f3830a17 100644
--- a/client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts
+++ b/client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts
@@ -10,7 +10,7 @@ import { AbuseDetailsComponent } from './abuse-details.component'
10import { AbuseListTableComponent } from './abuse-list-table.component' 10import { AbuseListTableComponent } from './abuse-list-table.component'
11import { AbuseMessageModalComponent } from './abuse-message-modal.component' 11import { AbuseMessageModalComponent } from './abuse-message-modal.component'
12import { ModerationCommentModalComponent } from './moderation-comment-modal.component' 12import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
13import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-account-avatar.module' 13import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module'
14 14
15@NgModule({ 15@NgModule({
16 imports: [ 16 imports: [
@@ -21,7 +21,7 @@ import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-accou
21 SharedModerationModule, 21 SharedModerationModule,
22 SharedGlobalIconModule, 22 SharedGlobalIconModule,
23 SharedVideoCommentModule, 23 SharedVideoCommentModule,
24 SharedAccountAvatarModule 24 SharedActorImageModule
25 ], 25 ],
26 26
27 declarations: [ 27 declarations: [
diff --git a/client/src/app/shared/shared-account-avatar/account-avatar.component.html b/client/src/app/shared/shared-account-avatar/account-avatar.component.html
deleted file mode 100644
index ca4ceb12f..000000000
--- a/client/src/app/shared/shared-account-avatar/account-avatar.component.html
+++ /dev/null
@@ -1,15 +0,0 @@
1<ng-template #img>
2 <img [class]="class" [src]="avatarUrl" i18n-alt alt="Account avatar" />
3</ng-template>
4
5<a *ngIf="account && href" [href]="href" target="_blank" rel="noopener noreferrer" [title]="title">
6 <ng-template *ngTemplateOutlet="img"></ng-template>
7</a>
8
9<a *ngIf="account && internalHref" [routerLink]="internalHref" [title]="title">
10 <ng-template *ngTemplateOutlet="img"></ng-template>
11</a>
12
13<ng-container *ngIf="!account || (!href && !internalHref)">
14 <ng-template *ngTemplateOutlet="img"></ng-template>
15</ng-container>
diff --git a/client/src/app/shared/shared-account-avatar/account-avatar.component.scss b/client/src/app/shared/shared-account-avatar/account-avatar.component.scss
deleted file mode 100644
index bb941d712..000000000
--- a/client/src/app/shared/shared-account-avatar/account-avatar.component.scss
+++ /dev/null
@@ -1,22 +0,0 @@
1@import '_variables';
2@import '_mixins';
3
4.avatar-25 {
5 @include avatar(25px);
6}
7
8.avatar-34 {
9 @include avatar(34px);
10}
11
12.avatar-36 {
13 @include avatar(36px);
14}
15
16.avatar-40 {
17 @include avatar(40px);
18}
19
20.avatar-120 {
21 @include avatar(120px);
22} \ No newline at end of file
diff --git a/client/src/app/shared/shared-account-avatar/account-avatar.component.ts b/client/src/app/shared/shared-account-avatar/account-avatar.component.ts
deleted file mode 100644
index 02a0a18bf..000000000
--- a/client/src/app/shared/shared-account-avatar/account-avatar.component.ts
+++ /dev/null
@@ -1,39 +0,0 @@
1import { Component, Input } from '@angular/core'
2import { Account } from '../shared-main/account/account.model'
3
4@Component({
5 selector: 'my-account-avatar',
6 styleUrls: [ './account-avatar.component.scss' ],
7 templateUrl: './account-avatar.component.html'
8})
9export class AccountAvatarComponent {
10 @Input() account: {
11 name: string
12 avatar?: { url?: string, path: string }
13 url: string
14 }
15 @Input() size: '25' | '34' | '36' | '40' | '120' = '36'
16
17 // Use an external link
18 @Input() href: string
19 // Use routerLink
20 @Input() internalHref: string | string[]
21
22 @Input() set title (value) {
23 this._title = value
24 }
25
26 private _title: string
27
28 get title () {
29 return this._title || $localize`${this.account.name} (account page)`
30 }
31
32 get class () {
33 return `avatar avatar-${this.size}`
34 }
35
36 get avatarUrl () {
37 return Account.GET_ACTOR_AVATAR_URL(this.account)
38 }
39}
diff --git a/client/src/app/shared/shared-account-avatar/index.ts b/client/src/app/shared/shared-account-avatar/index.ts
deleted file mode 100644
index 40c742ba5..000000000
--- a/client/src/app/shared/shared-account-avatar/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
1export * from './account-avatar.component'
2export * from './shared-account-avatar.module' \ No newline at end of file
diff --git a/client/src/app/shared/shared-account-avatar/shared-account-avatar.module.ts b/client/src/app/shared/shared-account-avatar/shared-account-avatar.module.ts
deleted file mode 100644
index 17b27589f..000000000
--- a/client/src/app/shared/shared-account-avatar/shared-account-avatar.module.ts
+++ /dev/null
@@ -1,23 +0,0 @@
1
2import { NgModule } from '@angular/core'
3import { SharedGlobalIconModule } from '../shared-icons'
4import { SharedMainModule } from '../shared-main/shared-main.module'
5import { AccountAvatarComponent } from './account-avatar.component'
6
7@NgModule({
8 imports: [
9 SharedMainModule,
10 SharedGlobalIconModule
11 ],
12
13 declarations: [
14 AccountAvatarComponent
15 ],
16
17 exports: [
18 AccountAvatarComponent
19 ],
20
21 providers: [ ]
22})
23export class SharedAccountAvatarModule { }
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html
index 0829263f4..e9c5fadcf 100644
--- a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html
+++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html
@@ -1,6 +1,6 @@
1<div class="actor" *ngIf="actor"> 1<div class="actor" *ngIf="actor">
2 <div class="d-flex"> 2 <div class="d-flex">
3 <img [ngClass]="{ channel: isChannel() }" [src]="preview || actor.avatarUrl" alt="Avatar" /> 3 <my-actor-avatar [channel]="getChannel()" [account]="getAccount()" [previewImage]="preview" size="100"></my-actor-avatar>
4 4
5 <div class="actor-img-edit-container"> 5 <div class="actor-img-edit-container">
6 6
@@ -34,6 +34,7 @@
34 <span for="avatarfile" i18n>Upload a new avatar</span> 34 <span for="avatarfile" i18n>Upload a new avatar</span>
35 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/> 35 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
36 </div> 36 </div>
37
37 <div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()"> 38 <div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()">
38 <my-global-icon iconName="delete"></my-global-icon> 39 <my-global-icon iconName="delete"></my-global-icon>
39 <span i18n>Remove avatar</span> 40 <span i18n>Remove avatar</span>
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss
index 8b0172315..08e80c3b4 100644
--- a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss
+++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss
@@ -4,16 +4,8 @@
4.actor { 4.actor {
5 display: flex; 5 display: flex;
6 6
7 img { 7 my-actor-avatar {
8 margin-right: 15px; 8 margin-right: 15px;
9
10 &:not(.channel) {
11 @include avatar(100px);
12 }
13
14 &.channel {
15 @include channel-avatar(100px);
16 }
17 } 9 }
18 10
19 .actor-info { 11 .actor-info {
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.ts b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts
index d0d269489..840946690 100644
--- a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.ts
+++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts
@@ -80,4 +80,16 @@ export class ActorAvatarEditComponent implements OnInit {
80 isChannel () { 80 isChannel () {
81 return !!(this.actor as VideoChannel).ownerAccount 81 return !!(this.actor as VideoChannel).ownerAccount
82 } 82 }
83
84 getChannel (): VideoChannel {
85 if (this.isChannel()) return this.actor as VideoChannel
86
87 return undefined
88 }
89
90 getAccount (): Account {
91 if (this.isChannel()) return undefined
92
93 return this.actor as Account
94 }
83} 95}
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.html b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html
index 266fc26c5..266fc26c5 100644
--- a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.html
+++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss
index 23606f871..23606f871 100644
--- a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss
+++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts
index 8c12d3c4c..8c12d3c4c 100644
--- a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts
+++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts
diff --git a/client/src/app/shared/shared-actor-image/actor-image-edit.scss b/client/src/app/shared/shared-actor-image-edit/actor-image-edit.scss
index 918955a89..918955a89 100644
--- a/client/src/app/shared/shared-actor-image/actor-image-edit.scss
+++ b/client/src/app/shared/shared-actor-image-edit/actor-image-edit.scss
diff --git a/client/src/app/shared/shared-actor-image-edit/index.ts b/client/src/app/shared/shared-actor-image-edit/index.ts
new file mode 100644
index 000000000..276b2e2fb
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image-edit/index.ts
@@ -0,0 +1 @@
export * from './shared-actor-image-edit.module'
diff --git a/client/src/app/shared/shared-actor-image-edit/shared-actor-image-edit.module.ts b/client/src/app/shared/shared-actor-image-edit/shared-actor-image-edit.module.ts
new file mode 100644
index 000000000..f6a397d5c
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image-edit/shared-actor-image-edit.module.ts
@@ -0,0 +1,31 @@
1
2import { CommonModule } from '@angular/common'
3import { NgModule } from '@angular/core'
4import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module'
5import { SharedGlobalIconModule } from '../shared-icons'
6import { SharedMainModule } from '../shared-main'
7import { ActorAvatarEditComponent } from './actor-avatar-edit.component'
8import { ActorBannerEditComponent } from './actor-banner-edit.component'
9
10@NgModule({
11 imports: [
12 CommonModule,
13
14 SharedMainModule,
15 SharedActorImageModule,
16 SharedGlobalIconModule
17 ],
18
19 declarations: [
20 ActorAvatarEditComponent,
21 ActorBannerEditComponent
22 ],
23
24 exports: [
25 ActorAvatarEditComponent,
26 ActorBannerEditComponent
27 ],
28
29 providers: [ ]
30})
31export class SharedActorImageEditModule { }
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.html b/client/src/app/shared/shared-actor-image/actor-avatar.component.html
new file mode 100644
index 000000000..13a5385a8
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.html
@@ -0,0 +1,19 @@
1<ng-template #img>
2 <img *ngIf="previewImage || avatarUrl || !initial" [class]="getClass('avatar')" [src]="previewImage || avatarUrl || defaultAvatarUrl" [alt]="alt" />
3
4 <div *ngIf="!avatarUrl && initial" [class]="getClass('initial')">
5 <span>{{ initial }}</span>
6 </div>
7</ng-template>
8
9<a *ngIf="hasActor() && href" [href]="href" target="_blank" rel="noopener noreferrer" [title]="title">
10 <ng-template *ngTemplateOutlet="img"></ng-template>
11</a>
12
13<a *ngIf="hasActor() && internalHref" [routerLink]="internalHref" [title]="title">
14 <ng-template *ngTemplateOutlet="img"></ng-template>
15</a>
16
17<ng-container *ngIf="!hasActor() || (!href && !internalHref)">
18 <ng-template *ngTemplateOutlet="img"></ng-template>
19</ng-container>
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.scss b/client/src/app/shared/shared-actor-image/actor-avatar.component.scss
new file mode 100644
index 000000000..bf50de4e9
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.scss
@@ -0,0 +1,101 @@
1@import '_variables';
2@import '_mixins';
3
4.avatar {
5 --avatarSize: 100%;
6 --initialFontSize: 22px;
7
8 width: var(--avatarSize);
9 height: var(--avatarSize);
10 min-width: var(--avatarSize);
11 min-height: var(--avatarSize);
12
13 &.account {
14 object-fit: cover;
15 border-radius: 50%;
16 }
17
18 &.channel {
19 border-radius: 5px;
20 }
21}
22
23.avatar-18 {
24 --avatarSize: 18px;
25 --initialFontSize: 13px;
26}
27
28.avatar-25 {
29 --avatarSize: 25px;
30}
31
32.avatar-32 {
33 --avatarSize: 32px;
34}
35
36.avatar-34 {
37 --avatarSize: 34px;
38}
39
40.avatar-36 {
41 --avatarSize: 36px;
42}
43
44.avatar-40 {
45 --avatarSize: 40px;
46}
47
48.avatar-100 {
49 --avatarSize: 100px;
50 --initialFontSize: 40px;
51}
52
53.avatar-120 {
54 --avatarSize: 120px;
55 --initialFontSize: 46px;
56}
57
58a:hover {
59 text-decoration: none;
60}
61
62.initial {
63 background-color: #3C2109;
64 color: #fff;
65 display: flex;
66 align-items: center;
67 justify-content: center;
68 font-size: var(--initialFontSize);
69
70 &.blue {
71 background-color: #009FD4;
72 }
73
74 &.green {
75 background-color: #00AA55;
76 }
77
78 &.purple {
79 background-color: #B381B3;
80 }
81
82 &.gray {
83 background-color: #939393;
84 }
85
86 &.yellow {
87 background-color: #AA8F00;
88 }
89
90 &.orange {
91 background-color: #D47500;
92 }
93
94 &.red {
95 background-color: #E76E3C;
96 }
97
98 &.dark-blue {
99 background-color: #0A3055;
100 }
101}
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.ts b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
new file mode 100644
index 000000000..b06c2bae6
--- /dev/null
+++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
@@ -0,0 +1,111 @@
1import { Component, Input } from '@angular/core'
2import { SafeResourceUrl } from '@angular/platform-browser'
3import { VideoChannel } from '../shared-main'
4import { Account } from '../shared-main/account/account.model'
5
6type ActorInput = {
7 name: string
8 avatar?: { url?: string, path: string }
9 url: string
10}
11
12export type ActorAvatarSize = '18' | '25' | '32' | '34' | '36' | '40' | '100' | '120'
13
14@Component({
15 selector: 'my-actor-avatar',
16 styleUrls: [ './actor-avatar.component.scss' ],
17 templateUrl: './actor-avatar.component.html'
18})
19export class ActorAvatarComponent {
20 @Input() account: ActorInput
21 @Input() channel: ActorInput
22
23 @Input() previewImage: SafeResourceUrl
24
25 @Input() size: ActorAvatarSize
26
27 // Use an external link
28 @Input() href: string
29 // Use routerLink
30 @Input() internalHref: string | any[]
31
32 @Input() set title (value) {
33 this._title = value
34 }
35
36 private _title: string
37
38 get title () {
39 if (this._title) return this._title
40 if (this.account) return $localize`${this.account.name} (account page)`
41 if (this.channel) return $localize`${this.channel.name} (channel page)`
42
43 return ''
44 }
45
46 get alt () {
47 if (this.account) return $localize`Account avatar`
48 if (this.channel) return $localize`Channel avatar`
49
50 return ''
51 }
52
53 getClass (type: 'avatar' | 'initial') {
54 const base = [ 'avatar' ]
55
56 if (this.size) base.push(`avatar-${this.size}`)
57
58 if (this.channel) base.push('channel')
59 else base.push('account')
60
61 if (type === 'initial' && this.initial) {
62 base.push('initial')
63 base.push(this.getColorTheme())
64 }
65
66 return base
67 }
68
69 get defaultAvatarUrl () {
70 if (this.channel) return VideoChannel.GET_DEFAULT_AVATAR_URL()
71
72 return Account.GET_DEFAULT_AVATAR_URL()
73 }
74
75 get avatarUrl () {
76 if (this.account) return Account.GET_ACTOR_AVATAR_URL(this.account)
77 if (this.channel) return VideoChannel.GET_ACTOR_AVATAR_URL(this.channel)
78
79 return ''
80 }
81
82 get initial () {
83 const name = this.account?.name
84 if (!name) return ''
85
86 return name.slice(0, 1)
87 }
88
89 hasActor () {
90 return !!this.account || !!this.channel
91 }
92
93 private getColorTheme () {
94 // Keep consistency with CSS
95 const themes = {
96 abc: 'blue',
97 def: 'green',
98 ghi: 'purple',
99 jkl: 'gray',
100 mno: 'yellow',
101 pqr: 'orange',
102 stvu: 'red',
103 wxyz: 'dark-blue'
104 }
105
106 const theme = Object.keys(themes)
107 .find(chars => chars.includes(this.initial))
108
109 return themes[theme]
110 }
111}
diff --git a/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts b/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts
index 6044f9925..8ea4bb2bf 100644
--- a/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts
+++ b/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts
@@ -1,27 +1,21 @@
1 1
2import { CommonModule } from '@angular/common'
3import { NgModule } from '@angular/core' 2import { NgModule } from '@angular/core'
4import { SharedGlobalIconModule } from '../shared-icons' 3import { SharedGlobalIconModule } from '../shared-icons'
5import { SharedMainModule } from '../shared-main' 4import { SharedMainModule } from '../shared-main/shared-main.module'
6import { ActorAvatarEditComponent } from './actor-avatar-edit.component' 5import { ActorAvatarComponent } from './actor-avatar.component'
7import { ActorBannerEditComponent } from './actor-banner-edit.component'
8 6
9@NgModule({ 7@NgModule({
10 imports: [ 8 imports: [
11 CommonModule,
12
13 SharedMainModule, 9 SharedMainModule,
14 SharedGlobalIconModule 10 SharedGlobalIconModule
15 ], 11 ],
16 12
17 declarations: [ 13 declarations: [
18 ActorAvatarEditComponent, 14 ActorAvatarComponent
19 ActorBannerEditComponent
20 ], 15 ],
21 16
22 exports: [ 17 exports: [
23 ActorAvatarEditComponent, 18 ActorAvatarComponent
24 ActorBannerEditComponent
25 ], 19 ],
26 20
27 providers: [ ] 21 providers: [ ]
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.html b/client/src/app/shared/shared-forms/advanced-input-filter.component.html
new file mode 100644
index 000000000..10d1296cf
--- /dev/null
+++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.html
@@ -0,0 +1,24 @@
1<div class="input-group has-feedback has-clear">
2 <div *ngIf="hasFilters()" class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
3 <div class="input-group-text" ngbDropdownToggle>
4 <span class="caret" aria-haspopup="menu" role="button"></span>
5 </div>
6
7 <div role="menu" ngbDropdownMenu>
8 <h6 class="dropdown-header" i18n>Advanced filters</h6>
9
10 <a *ngFor="let filter of filters" [routerLink]="[ '.' ]" [queryParams]="filter.queryParams" class="dropdown-item">
11 {{ filter.label }}
12 </a>
13 </div>
14 </div>
15
16 <input
17 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
18 [(ngModel)]="searchValue"
19 (keyup)="onInputSearch($event)"
20 >
21
22 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="onResetTableFilter()"></a>
23 <span class="sr-only" i18n>Clear filters</span>
24</div>
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.scss b/client/src/app/shared/shared-forms/advanced-input-filter.component.scss
new file mode 100644
index 000000000..7c2198927
--- /dev/null
+++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.scss
@@ -0,0 +1,10 @@
1@import '_variables';
2@import '_mixins';
3
4input {
5 @include peertube-input-text(250px);
6}
7
8.input-group-text {
9 background-color: transparent;
10}
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
new file mode 100644
index 000000000..c11f1ad1d
--- /dev/null
+++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts
@@ -0,0 +1,116 @@
1import * as debug from 'debug'
2import { Subject } from 'rxjs'
3import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
4import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
5import { ActivatedRoute, Params, Router } from '@angular/router'
6
7export type AdvancedInputFilter = {
8 label: string
9 queryParams: Params
10}
11
12const logger = debug('peertube:AdvancedInputFilterComponent')
13
14@Component({
15 selector: 'my-advanced-input-filter',
16 templateUrl: './advanced-input-filter.component.html',
17 styleUrls: [ './advanced-input-filter.component.scss' ]
18})
19export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
20 @Input() filters: AdvancedInputFilter[] = []
21
22 @Output() search = new EventEmitter<string>()
23
24 searchValue: string
25
26 private searchStream: Subject<string>
27
28 private viewInitialized = false
29 private emitSearchAfterViewInit = false
30
31 constructor (
32 private route: ActivatedRoute,
33 private router: Router
34 ) { }
35
36 ngOnInit () {
37 this.initSearchStream()
38 this.listenToRouteSearchChange()
39 }
40
41 ngAfterViewInit () {
42 this.viewInitialized = true
43
44 // Init after view init to not send an event too early
45 if (this.emitSearchAfterViewInit) this.emitSearch()
46 }
47
48 onInputSearch (event: Event) {
49 this.scheduleSearchUpdate((event.target as HTMLInputElement).value)
50 }
51
52 onResetTableFilter () {
53 this.immediateSearchUpdate('')
54 }
55
56 hasFilters () {
57 return this.filters.length !== 0
58 }
59
60 private scheduleSearchUpdate (value: string) {
61 this.searchValue = value
62 this.searchStream.next(this.searchValue)
63 }
64
65 private immediateSearchUpdate (value: string) {
66 this.searchValue = value
67
68 this.setQueryParams(this.searchValue)
69 this.emitSearch()
70 }
71
72 private listenToRouteSearchChange () {
73 this.route.queryParams
74 .subscribe(params => {
75 const search = params.search || ''
76
77 logger('On route search change "%s".', search)
78
79 this.searchValue = search
80 this.emitSearch()
81 })
82 }
83
84 private initSearchStream () {
85 this.searchStream = new Subject()
86
87 this.searchStream
88 .pipe(
89 debounceTime(300),
90 distinctUntilChanged()
91 )
92 .subscribe(() => {
93 this.setQueryParams(this.searchValue)
94
95 this.emitSearch()
96 })
97 }
98
99 private emitSearch () {
100 if (!this.viewInitialized) {
101 this.emitSearchAfterViewInit = true
102 return
103 }
104
105 logger('On search "%s".', this.searchValue)
106
107 this.search.emit(this.searchValue)
108 }
109
110 private setQueryParams (search: string) {
111 const queryParams: Params = {}
112
113 if (search) Object.assign(queryParams, { search })
114 this.router.navigate([ ], { queryParams })
115 }
116}
diff --git a/client/src/app/shared/shared-forms/index.ts b/client/src/app/shared/shared-forms/index.ts
index 1d859b991..727416a40 100644
--- a/client/src/app/shared/shared-forms/index.ts
+++ b/client/src/app/shared/shared-forms/index.ts
@@ -1,12 +1,14 @@
1export * from './form-validator.service' 1export * from './advanced-input-filter.component'
2export * from './form-reactive' 2export * from './form-reactive'
3export * from './select' 3export * from './form-validator.service'
4export * from './input-toggle-hidden.component' 4export * from './form-validator.service'
5export * from './input-switch.component' 5export * from './input-switch.component'
6export * from './input-toggle-hidden.component'
6export * from './markdown-textarea.component' 7export * from './markdown-textarea.component'
7export * from './peertube-checkbox.component' 8export * from './peertube-checkbox.component'
8export * from './preview-upload.component' 9export * from './preview-upload.component'
9export * from './reactive-file.component' 10export * from './reactive-file.component'
11export * from './select'
12export * from './shared-form.module'
10export * from './textarea-autoresize.directive' 13export * from './textarea-autoresize.directive'
11export * from './timestamp-input.component' 14export * from './timestamp-input.component'
12export * from './shared-form.module'
diff --git a/client/src/app/shared/shared-forms/input-switch.component.scss b/client/src/app/shared/shared-forms/input-switch.component.scss
index c14950bd7..290a70db8 100644
--- a/client/src/app/shared/shared-forms/input-switch.component.scss
+++ b/client/src/app/shared/shared-forms/input-switch.component.scss
@@ -5,7 +5,7 @@ input {
5 position: absolute; 5 position: absolute;
6 visibility: hidden; 6 visibility: hidden;
7 7
8 & + label { 8 + label {
9 cursor: pointer; 9 cursor: pointer;
10 text-indent: -9999px; 10 text-indent: -9999px;
11 width: 35px; 11 width: 35px;
@@ -16,7 +16,7 @@ input {
16 position: relative; 16 position: relative;
17 margin: 0; 17 margin: 0;
18 18
19 &:after { 19 &::after {
20 content: ''; 20 content: '';
21 position: absolute; 21 position: absolute;
22 top: 3px; 22 top: 3px;
@@ -28,7 +28,7 @@ input {
28 transition: 0.3s ease-out; 28 transition: 0.3s ease-out;
29 } 29 }
30 30
31 &:active:after { 31 &:active::after {
32 width: 40px; 32 width: 40px;
33 } 33 }
34 } 34 }
@@ -36,7 +36,7 @@ input {
36 &:checked + label { 36 &:checked + label {
37 background: pvar(--mainColor); 37 background: pvar(--mainColor);
38 38
39 &:after { 39 &::after {
40 left: calc(100% - 3px); 40 left: calc(100% - 3px);
41 transform: translateX(-100%); 41 transform: translateX(-100%);
42 } 42 }
diff --git a/client/src/app/shared/shared-forms/markdown-textarea.component.scss b/client/src/app/shared/shared-forms/markdown-textarea.component.scss
index 8203c7d1c..1f72dbc32 100644
--- a/client/src/app/shared/shared-forms/markdown-textarea.component.scss
+++ b/client/src/app/shared/shared-forms/markdown-textarea.component.scss
@@ -18,7 +18,7 @@ $input-border-radius: 3px;
18 18
19 font-family: monospace; 19 font-family: monospace;
20 font-size: 13px; 20 font-size: 13px;
21 border-bottom: none; 21 border-bottom: 0;
22 border-bottom-left-radius: unset; 22 border-bottom-left-radius: unset;
23 border-bottom-right-radius: unset; 23 border-bottom-right-radius: unset;
24 } 24 }
@@ -51,7 +51,8 @@ $input-border-radius: 3px;
51 opacity: 0.6; 51 opacity: 0.6;
52 } 52 }
53 53
54 &:hover, &:active { 54 &:hover,
55 &:active {
55 svg { 56 svg {
56 opacity: 1; 57 opacity: 1;
57 } 58 }
@@ -105,6 +106,8 @@ $input-border-radius: 3px;
105} 106}
106 107
107@mixin maximized-base { 108@mixin maximized-base {
109 $nav-preview-vertical-padding: 40px;
110
108 flex-direction: row; 111 flex-direction: row;
109 z-index: #{z(header) - 1}; 112 z-index: #{z(header) - 1};
110 position: fixed; 113 position: fixed;
@@ -115,20 +118,18 @@ $input-border-radius: 3px;
115 width: calc(100% - #{$menu-width}); 118 width: calc(100% - #{$menu-width});
116 height: calc(100vh - #{$header-height}) !important; 119 height: calc(100vh - #{$header-height}) !important;
117 120
118 $nav-preview-vertical-padding: 40px;
119
120 .nav-preview { 121 .nav-preview {
121 @include nav-preview-medium(); 122 @include nav-preview-medium();
122 padding-top: #{$nav-preview-vertical-padding / 2}; 123 padding-top: #{$nav-preview-vertical-padding / 2};
123 padding-bottom: #{$nav-preview-vertical-padding / 2}; 124 padding-bottom: #{$nav-preview-vertical-padding / 2};
124 padding-left: 0px; 125 padding-left: 0;
125 padding-right: 0px; 126 padding-right: 0;
126 position: absolute; 127 position: absolute;
127 background-color: pvar(--mainBackgroundColor); 128 background-color: pvar(--mainBackgroundColor);
128 width: 100% !important; 129 width: 100% !important;
129 border-top: none; 130 border-top: 0;
130 border-left: none; 131 border-left: 0;
131 border-right: none; 132 border-right: 0;
132 133
133 :last-child { 134 :last-child {
134 margin-right: pvar(--horizontalMarginContent); 135 margin-right: pvar(--horizontalMarginContent);
@@ -148,7 +149,7 @@ $input-border-radius: 3px;
148 margin-top: #{$nav-preview-tab-height + $nav-preview-vertical-padding} !important; 149 margin-top: #{$nav-preview-tab-height + $nav-preview-vertical-padding} !important;
149 height: calc(100vh - #{$header-height + $nav-preview-tab-height + $nav-preview-vertical-padding}) !important; 150 height: calc(100vh - #{$header-height + $nav-preview-tab-height + $nav-preview-vertical-padding}) !important;
150 width: 50% !important; 151 width: 50% !important;
151 border: none !important; 152 border: 0 !important;
152 border-radius: unset !important; 153 border-radius: unset !important;
153 } 154 }
154 155
@@ -249,11 +250,11 @@ $input-border-radius: 3px;
249} 250}
250 251
251@media only screen and (min-width: $small-view) { 252@media only screen and (min-width: $small-view) {
253 @include maximized-in-medium-view();
254
252 :host-context(.expanded) { 255 :host-context(.expanded) {
253 @include in-medium-view(); 256 @include in-medium-view();
254 } 257 }
255
256 @include maximized-in-medium-view();
257} 258}
258 259
259@media only screen and (min-width: #{$small-view + $menu-width}) { 260@media only screen and (min-width: #{$small-view + $menu-width}) {
diff --git a/client/src/app/shared/shared-forms/peertube-checkbox.component.scss b/client/src/app/shared/shared-forms/peertube-checkbox.component.scss
index cf8540dc3..203b82d0b 100644
--- a/client/src/app/shared/shared-forms/peertube-checkbox.component.scss
+++ b/client/src/app/shared/shared-forms/peertube-checkbox.component.scss
@@ -46,7 +46,7 @@
46 line-height: 12px; 46 line-height: 12px;
47 font-weight: 500; 47 font-weight: 500;
48 color: pvar(--inputPlaceholderColor); 48 color: pvar(--inputPlaceholderColor);
49 background-color: rgba(217,225,232,.1); 49 background-color: rgba(217, 225, 232, .1);
50 border: 1px solid rgba(217,225,232,.5); 50 border: 1px solid rgba(217, 225, 232, .5);
51 } 51 }
52} \ No newline at end of file 52}
diff --git a/client/src/app/shared/shared-forms/preview-upload.component.scss b/client/src/app/shared/shared-forms/preview-upload.component.scss
index 88eccd5f7..c2ee0d6a9 100644
--- a/client/src/app/shared/shared-forms/preview-upload.component.scss
+++ b/client/src/app/shared/shared-forms/preview-upload.component.scss
@@ -21,7 +21,7 @@
21 max-width: 100%; 21 max-width: 100%;
22 22
23 &.no-image { 23 &.no-image {
24 border: 2px solid grey; 24 border: 2px solid #808080;
25 background-color: pvar(--mainBackgroundColor); 25 background-color: pvar(--mainBackgroundColor);
26 } 26 }
27 } 27 }
diff --git a/client/src/app/shared/shared-forms/select/select-shared.component.scss b/client/src/app/shared/shared-forms/select/select-shared.component.scss
index 80196b8df..7006adab1 100644
--- a/client/src/app/shared/shared-forms/select/select-shared.component.scss
+++ b/client/src/app/shared/shared-forms/select/select-shared.component.scss
@@ -32,7 +32,7 @@ ng-select ::ng-deep {
32} 32}
33 33
34.root { 34.root {
35 display:flex; 35 display: flex;
36 align-items: center; 36 align-items: center;
37 37
38 > my-select-options { 38 > my-select-options {
@@ -41,9 +41,9 @@ ng-select ::ng-deep {
41} 41}
42 42
43my-select-options + input { 43my-select-options + input {
44 margin-left: 5px;
45
46 @include peertube-input-text($form-base-input-width); 44 @include peertube-input-text($form-base-input-width);
45
46 margin-left: 5px;
47 display: block; 47 display: block;
48} 48}
49 49
diff --git a/client/src/app/shared/shared-forms/shared-form.module.ts b/client/src/app/shared/shared-forms/shared-form.module.ts
index 9bdd138a1..5417f7342 100644
--- a/client/src/app/shared/shared-forms/shared-form.module.ts
+++ b/client/src/app/shared/shared-forms/shared-form.module.ts
@@ -5,6 +5,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'
5import { NgSelectModule } from '@ng-select/ng-select' 5import { NgSelectModule } from '@ng-select/ng-select'
6import { SharedGlobalIconModule } from '../shared-icons' 6import { SharedGlobalIconModule } from '../shared-icons'
7import { SharedMainModule } from '../shared-main/shared-main.module' 7import { SharedMainModule } from '../shared-main/shared-main.module'
8import { AdvancedInputFilterComponent } from './advanced-input-filter.component'
8import { DynamicFormFieldComponent } from './dynamic-form-field.component' 9import { DynamicFormFieldComponent } from './dynamic-form-field.component'
9import { FormValidatorService } from './form-validator.service' 10import { FormValidatorService } from './form-validator.service'
10import { InputSwitchComponent } from './input-switch.component' 11import { InputSwitchComponent } from './input-switch.component'
@@ -52,7 +53,9 @@ import { TimestampInputComponent } from './timestamp-input.component'
52 SelectCheckboxComponent, 53 SelectCheckboxComponent,
53 SelectCustomValueComponent, 54 SelectCustomValueComponent,
54 55
55 DynamicFormFieldComponent 56 DynamicFormFieldComponent,
57
58 AdvancedInputFilterComponent
56 ], 59 ],
57 60
58 exports: [ 61 exports: [
@@ -78,7 +81,9 @@ import { TimestampInputComponent } from './timestamp-input.component'
78 SelectCheckboxComponent, 81 SelectCheckboxComponent,
79 SelectCustomValueComponent, 82 SelectCustomValueComponent,
80 83
81 DynamicFormFieldComponent 84 DynamicFormFieldComponent,
85
86 AdvancedInputFilterComponent
82 ], 87 ],
83 88
84 providers: [ 89 providers: [
diff --git a/client/src/app/shared/shared-forms/timestamp-input.component.scss b/client/src/app/shared/shared-forms/timestamp-input.component.scss
index 66e9aa032..36f5711a6 100644
--- a/client/src/app/shared/shared-forms/timestamp-input.component.scss
+++ b/client/src/app/shared/shared-forms/timestamp-input.component.scss
@@ -4,8 +4,7 @@ p-inputmask {
4 ::ng-deep input { 4 ::ng-deep input {
5 width: 80px; 5 width: 80px;
6 font-size: 15px; 6 font-size: 15px;
7 7 border: 0;
8 border: none;
9 8
10 &:focus-within, 9 &:focus-within,
11 &:focus { 10 &:focus {
diff --git a/client/src/app/shared/shared-instance/instance-about-accordion.component.scss b/client/src/app/shared/shared-instance/instance-about-accordion.component.scss
index 2f6b420e3..615e08bcc 100644
--- a/client/src/app/shared/shared-instance/instance-about-accordion.component.scss
+++ b/client/src/app/shared/shared-instance/instance-about-accordion.component.scss
@@ -1,6 +1,6 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3@import "./_bootstrap-variables"; 3@import './_bootstrap-variables';
4 4
5@import '~bootstrap/scss/functions'; 5@import '~bootstrap/scss/functions';
6@import '~bootstrap/scss/variables'; 6@import '~bootstrap/scss/variables';
@@ -30,7 +30,7 @@ ngb-accordion ::ng-deep {
30 background-color: unset; 30 background-color: unset;
31 padding: 0; 31 padding: 0;
32 32
33 & + .collapse.show { 33 + .collapse.show {
34 background-color: var(--submenuBackgroundColor); 34 background-color: var(--submenuBackgroundColor);
35 } 35 }
36 } 36 }
diff --git a/client/src/app/shared/shared-instance/instance-features-table.component.scss b/client/src/app/shared/shared-instance/instance-features-table.component.scss
index d17e91fc2..11cf11616 100644
--- a/client/src/app/shared/shared-instance/instance-features-table.component.scss
+++ b/client/src/app/shared/shared-instance/instance-features-table.component.scss
@@ -19,7 +19,7 @@ table {
19 .more-info { 19 .more-info {
20 font-style: italic; 20 font-style: italic;
21 font-weight: initial; 21 font-weight: initial;
22 font-size: 14px 22 font-size: 14px;
23 } 23 }
24 } 24 }
25 25
diff --git a/client/src/app/shared/shared-main/account/account.model.ts b/client/src/app/shared/shared-main/account/account.model.ts
index 65e6798d4..6d9f0ee65 100644
--- a/client/src/app/shared/shared-main/account/account.model.ts
+++ b/client/src/app/shared/shared-main/account/account.model.ts
@@ -14,7 +14,7 @@ export class Account extends Actor implements ServerAccount {
14 userId?: number 14 userId?: number
15 15
16 static GET_ACTOR_AVATAR_URL (actor: { avatar?: { url?: string, path: string } }) { 16 static GET_ACTOR_AVATAR_URL (actor: { avatar?: { url?: string, path: string } }) {
17 return Actor.GET_ACTOR_AVATAR_URL(actor) || this.GET_DEFAULT_AVATAR_URL() 17 return Actor.GET_ACTOR_AVATAR_URL(actor)
18 } 18 }
19 19
20 static GET_DEFAULT_AVATAR_URL () { 20 static GET_DEFAULT_AVATAR_URL () {
@@ -24,8 +24,6 @@ export class Account extends Actor implements ServerAccount {
24 constructor (hash: ServerAccount) { 24 constructor (hash: ServerAccount) {
25 super(hash) 25 super(hash)
26 26
27 this.updateComputedAttributes()
28
29 this.displayName = hash.displayName 27 this.displayName = hash.displayName
30 this.description = hash.description 28 this.description = hash.description
31 this.userId = hash.userId 29 this.userId = hash.userId
@@ -40,16 +38,9 @@ export class Account extends Actor implements ServerAccount {
40 38
41 updateAvatar (newAvatar: ActorImage) { 39 updateAvatar (newAvatar: ActorImage) {
42 this.avatar = newAvatar 40 this.avatar = newAvatar
43
44 this.updateComputedAttributes()
45 } 41 }
46 42
47 resetAvatar () { 43 resetAvatar () {
48 this.avatar = null 44 this.avatar = null
49 this.avatarUrl = Account.GET_DEFAULT_AVATAR_URL()
50 }
51
52 private updateComputedAttributes () {
53 this.avatarUrl = Account.GET_ACTOR_AVATAR_URL(this)
54 } 45 }
55} 46}
diff --git a/client/src/app/shared/shared-main/account/actor.model.ts b/client/src/app/shared/shared-main/account/actor.model.ts
index 4b036341f..6ba0bb09e 100644
--- a/client/src/app/shared/shared-main/account/actor.model.ts
+++ b/client/src/app/shared/shared-main/account/actor.model.ts
@@ -15,7 +15,6 @@ export abstract class Actor implements ServerActor {
15 updatedAt: Date | string 15 updatedAt: Date | string
16 16
17 avatar: ActorImage 17 avatar: ActorImage
18 avatarUrl: string
19 18
20 isLocal: boolean 19 isLocal: boolean
21 20
diff --git a/client/src/app/shared/shared-main/buttons/action-dropdown.component.scss b/client/src/app/shared/shared-main/buttons/action-dropdown.component.scss
index 724a04efc..b9a4d46dc 100644
--- a/client/src/app/shared/shared-main/buttons/action-dropdown.component.scss
+++ b/client/src/app/shared/shared-main/buttons/action-dropdown.component.scss
@@ -8,6 +8,9 @@
8.action-button { 8.action-button {
9 @include peertube-button; 9 @include peertube-button;
10 10
11 display: inline-block;
12 padding: 0 10px;
13
11 &.button-styled { 14 &.button-styled {
12 15
13 &.grey { 16 &.grey {
@@ -18,14 +21,13 @@
18 @include orange-button; 21 @include orange-button;
19 } 22 }
20 23
21 &:hover, &:active, &:focus { 24 &:hover,
25 &:active,
26 &:focus {
22 background-color: $grey-background-color; 27 background-color: $grey-background-color;
23 } 28 }
24 } 29 }
25 30
26 display: inline-block;
27 padding: 0 10px;
28
29 &::after { 31 &::after {
30 display: none; 32 display: none;
31 } 33 }
@@ -64,7 +66,8 @@
64 @include dropdown-with-icon-item; 66 @include dropdown-with-icon-item;
65 } 67 }
66 68
67 a, span { 69 a,
70 span {
68 display: block; 71 display: block;
69 width: 100%; 72 width: 100%;
70 } 73 }
diff --git a/client/src/app/shared/shared-main/buttons/button.component.scss b/client/src/app/shared/shared-main/buttons/button.component.scss
index f73b7b808..09b5f95d7 100644
--- a/client/src/app/shared/shared-main/buttons/button.component.scss
+++ b/client/src/app/shared/shared-main/buttons/button.component.scss
@@ -1,6 +1,16 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4@mixin responsive-label {
5 .action-button {
6 padding: 0 13px;
7 }
8
9 .button-label {
10 display: none;
11 }
12}
13
4:host { 14:host {
5 outline: none; 15 outline: none;
6} 16}
@@ -46,12 +56,12 @@ span[class$=-button] {
46// In a table, try to minimize the space taken by this button 56// In a table, try to minimize the space taken by this button
47@media screen and (max-width: 1400px) { 57@media screen and (max-width: 1400px) {
48 :host-context(td) { 58 :host-context(td) {
49 .action-button { 59 @include responsive-label;
50 padding: 0 13px; 60 }
51 } 61}
52 62
53 .button-label { 63@media screen and (max-width: $small-view) {
54 display: none; 64 .responsive-label {
55 } 65 @include responsive-label;
56 } 66 }
57} 67}
diff --git a/client/src/app/shared/shared-main/buttons/button.component.ts b/client/src/app/shared/shared-main/buttons/button.component.ts
index 1d2be0bf9..ee74b3d12 100644
--- a/client/src/app/shared/shared-main/buttons/button.component.ts
+++ b/client/src/app/shared/shared-main/buttons/button.component.ts
@@ -3,7 +3,7 @@ import { GlobalIconName } from '@app/shared/shared-icons'
3 3
4@Component({ 4@Component({
5 selector: 'my-button', 5 selector: 'my-button',
6 styleUrls: ['./button.component.scss'], 6 styleUrls: [ './button.component.scss' ],
7 templateUrl: './button.component.html' 7 templateUrl: './button.component.html'
8}) 8})
9 9
@@ -14,6 +14,7 @@ export class ButtonComponent {
14 @Input() title: string = undefined 14 @Input() title: string = undefined
15 @Input() loading = false 15 @Input() loading = false
16 @Input() disabled = false 16 @Input() disabled = false
17 @Input() responsiveLabel = false
17 18
18 getTitle () { 19 getTitle () {
19 return this.title || this.label 20 return this.title || this.label
@@ -22,7 +23,8 @@ export class ButtonComponent {
22 getClasses () { 23 getClasses () {
23 return { 24 return {
24 [this.className]: true, 25 [this.className]: true,
25 disabled: this.disabled 26 disabled: this.disabled,
27 'responsive-label': this.responsiveLabel
26 } 28 }
27 } 29 }
28} 30}
diff --git a/client/src/app/shared/shared-main/buttons/delete-button.component.html b/client/src/app/shared/shared-main/buttons/delete-button.component.html
index c94d8d0c9..d7a6702a7 100644
--- a/client/src/app/shared/shared-main/buttons/delete-button.component.html
+++ b/client/src/app/shared/shared-main/buttons/delete-button.component.html
@@ -1,4 +1,7 @@
1<span class="action-button action-button-delete grey-button" [ngbTooltip]="title" role="button" tabindex="0"> 1<span
2 class="action-button action-button-delete grey-button"
3 [ngClass]="{ 'responsive-label': responsiveLabel }" [ngbTooltip]="title" role="button" tabindex="0"
4>
2 <my-global-icon iconName="delete" aria-hidden="true"></my-global-icon> 5 <my-global-icon iconName="delete" aria-hidden="true"></my-global-icon>
3 6
4 <span class="button-label" *ngIf="label">{{ label }}</span> 7 <span class="button-label" *ngIf="label">{{ label }}</span>
diff --git a/client/src/app/shared/shared-main/buttons/delete-button.component.ts b/client/src/app/shared/shared-main/buttons/delete-button.component.ts
index 18995422a..c091f5309 100644
--- a/client/src/app/shared/shared-main/buttons/delete-button.component.ts
+++ b/client/src/app/shared/shared-main/buttons/delete-button.component.ts
@@ -9,6 +9,7 @@ import { Component, Input, OnInit } from '@angular/core'
9export class DeleteButtonComponent implements OnInit { 9export class DeleteButtonComponent implements OnInit {
10 @Input() label: string 10 @Input() label: string
11 @Input() title: string 11 @Input() title: string
12 @Input() responsiveLabel = false
12 13
13 ngOnInit () { 14 ngOnInit () {
14 // <my-delete-button /> No label 15 // <my-delete-button /> No label
diff --git a/client/src/app/shared/shared-main/buttons/edit-button.component.html b/client/src/app/shared/shared-main/buttons/edit-button.component.html
index ecb709be1..8beeee6c4 100644
--- a/client/src/app/shared/shared-main/buttons/edit-button.component.html
+++ b/client/src/app/shared/shared-main/buttons/edit-button.component.html
@@ -1,4 +1,7 @@
1<a class="action-button action-button-edit grey-button" [routerLink]="routerLink" [ngbTooltip]="title"> 1<a
2 class="action-button action-button-edit grey-button"
3 [ngClass]="{ 'responsive-label': responsiveLabel }" [routerLink]="routerLink" [ngbTooltip]="title"
4>
2 <my-global-icon iconName="edit" aria-hidden="true"></my-global-icon> 5 <my-global-icon iconName="edit" aria-hidden="true"></my-global-icon>
3 6
4 <span class="button-label" *ngIf="label">{{ label }}</span> 7 <span class="button-label" *ngIf="label">{{ label }}</span>
diff --git a/client/src/app/shared/shared-main/buttons/edit-button.component.ts b/client/src/app/shared/shared-main/buttons/edit-button.component.ts
index 4b76551ca..24c8625ff 100644
--- a/client/src/app/shared/shared-main/buttons/edit-button.component.ts
+++ b/client/src/app/shared/shared-main/buttons/edit-button.component.ts
@@ -5,11 +5,11 @@ import { Component, Input, OnInit } from '@angular/core'
5 styleUrls: [ './button.component.scss' ], 5 styleUrls: [ './button.component.scss' ],
6 templateUrl: './edit-button.component.html' 6 templateUrl: './edit-button.component.html'
7}) 7})
8
9export class EditButtonComponent implements OnInit { 8export class EditButtonComponent implements OnInit {
10 @Input() label: string 9 @Input() label: string
11 @Input() title: string 10 @Input() title: string
12 @Input() routerLink: string[] | string = [] 11 @Input() routerLink: string[] | string = []
12 @Input() responsiveLabel = false
13 13
14 ngOnInit () { 14 ngOnInit () {
15 // <my-edit-button /> No label 15 // <my-edit-button /> No label
diff --git a/client/src/app/shared/shared-main/date/date-toggle.component.scss b/client/src/app/shared/shared-main/date/date-toggle.component.scss
index 86700d1d4..b87f7c475 100644
--- a/client/src/app/shared/shared-main/date/date-toggle.component.scss
+++ b/client/src/app/shared/shared-main/date/date-toggle.component.scss
@@ -1,5 +1,5 @@
1.date-toggle { 1.date-toggle {
2 &:hover { 2 &:hover {
3 cursor: default 3 cursor: default;
4 } 4 }
5} 5}
diff --git a/client/src/app/shared/shared-main/feeds/feed.component.scss b/client/src/app/shared/shared-main/feeds/feed.component.scss
index b655ee708..d39f31d70 100644
--- a/client/src/app/shared/shared-main/feeds/feed.component.scss
+++ b/client/src/app/shared/shared-main/feeds/feed.component.scss
@@ -5,14 +5,14 @@
5 width: 100%; 5 width: 100%;
6 6
7 a { 7 a {
8 color: black; 8 color: #000;
9 display: block; 9 display: block;
10 } 10 }
11} 11}
12 12
13my-global-icon { 13my-global-icon {
14 @include apply-svg-color(pvar(--mainForegroundColor));
15
14 cursor: pointer; 16 cursor: pointer;
15 width: 100%; 17 width: 100%;
16
17 @include apply-svg-color(pvar(--mainForegroundColor))
18} 18}
diff --git a/client/src/app/shared/shared-main/loaders/loader.component.scss b/client/src/app/shared/shared-main/loaders/loader.component.scss
index ffac9c707..64138afe4 100644
--- a/client/src/app/shared/shared-main/loaders/loader.component.scss
+++ b/client/src/app/shared/shared-main/loaders/loader.component.scss
@@ -20,7 +20,7 @@
20 border: 4px solid; 20 border: 4px solid;
21 border-radius: 50%; 21 border-radius: 50%;
22 animation: loader 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; 22 animation: loader 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
23 border-color: #999999 transparent transparent transparent; 23 border-color: #999999 transparent transparent;
24} 24}
25 25
26.loader div:nth-child(1) { 26.loader div:nth-child(1) {
diff --git a/client/src/app/shared/shared-main/misc/help.component.scss b/client/src/app/shared/shared-main/misc/help.component.scss
index ccc91ffab..68d7ad48f 100644
--- a/client/src/app/shared/shared-main/misc/help.component.scss
+++ b/client/src/app/shared/shared-main/misc/help.component.scss
@@ -2,20 +2,19 @@
2@import '_mixins'; 2@import '_mixins';
3 3
4.help-tooltip-button { 4.help-tooltip-button {
5 cursor: pointer; 5 @include disable-outline;
6 border: none;
7 6
7 cursor: pointer;
8 border: 0;
8 margin: 5px; 9 margin: 5px;
9 10
10 my-global-icon { 11 my-global-icon {
12 @include apply-svg-color(pvar(--greyForegroundColor));
13
11 width: 17px; 14 width: 17px;
12 position: relative; 15 position: relative;
13 top: -1px; 16 top: -1px;
14
15 @include apply-svg-color(pvar(--greyForegroundColor))
16 } 17 }
17
18 @include disable-outline;
19} 18}
20 19
21::ng-deep { 20::ng-deep {
diff --git a/client/src/app/shared/shared-main/misc/list-overflow.component.html b/client/src/app/shared/shared-main/misc/list-overflow.component.html
index 986572801..b2e0982f1 100644
--- a/client/src/app/shared/shared-main/misc/list-overflow.component.html
+++ b/client/src/app/shared/shared-main/misc/list-overflow.component.html
@@ -2,19 +2,19 @@
2 <span [id]="getId(id)" #itemsRendered *ngFor="let item of items; index as id"> 2 <span [id]="getId(id)" #itemsRendered *ngFor="let item of items; index as id">
3 <ng-container *ngTemplateOutlet="itemTemplate; context: {item: item}"></ng-container> 3 <ng-container *ngTemplateOutlet="itemTemplate; context: {item: item}"></ng-container>
4 </span> 4 </span>
5 5
6 <ng-container *ngIf="isMenuDisplayed()"> 6 <ng-container *ngIf="isMenuDisplayed()">
7 <button *ngIf="isInMobileView" class="btn btn-outline-secondary btn-sm list-overflow-menu" (click)="toggleModal()"> 7 <button *ngIf="isInMobileView" class="btn btn-outline-secondary btn-sm list-overflow-menu" (click)="toggleModal()">
8 <span class="glyphicon glyphicon-chevron-down"></span> 8 <span class="glyphicon glyphicon-chevron-down"></span>
9 </button> 9 </button>
10 10
11 <div *ngIf="!isInMobileView" class="list-overflow-menu" ngbDropdown container="body" #dropdown="ngbDropdown" (mouseleave)="closeDropdownIfHovered(dropdown)" (mouseenter)="openDropdownOnHover(dropdown)"> 11 <div *ngIf="!isInMobileView" class="list-overflow-menu" ngbDropdown container="body" #dropdown="ngbDropdown" (mouseleave)="closeDropdownIfHovered(dropdown)" (mouseenter)="openDropdownOnHover(dropdown)">
12 <button class="btn btn-outline-secondary btn-sm" [ngClass]="{ routeActive: active }" 12 <button class="btn btn-outline-secondary btn-sm" [ngClass]="{ 'route-active': active }"
13 ngbDropdownAnchor (click)="dropdownAnchorClicked(dropdown)" role="button" 13 ngbDropdownAnchor (click)="dropdownAnchorClicked(dropdown)" role="button"
14 > 14 >
15 <span class="glyphicon glyphicon-chevron-down"></span> 15 <span class="glyphicon glyphicon-chevron-down"></span>
16 </button> 16 </button>
17 17
18 <div ngbDropdownMenu> 18 <div ngbDropdownMenu>
19 <a *ngFor="let item of items | slice:showItemsUntilIndexExcluded:items.length" 19 <a *ngFor="let item of items | slice:showItemsUntilIndexExcluded:items.length"
20 [routerLink]="item.routerLink" routerLinkActive="active" class="dropdown-item"> 20 [routerLink]="item.routerLink" routerLinkActive="active" class="dropdown-item">
diff --git a/client/src/app/shared/shared-main/misc/list-overflow.component.scss b/client/src/app/shared/shared-main/misc/list-overflow.component.scss
index 1ec044489..7e31d3850 100644
--- a/client/src/app/shared/shared-main/misc/list-overflow.component.scss
+++ b/client/src/app/shared/shared-main/misc/list-overflow.component.scss
@@ -15,13 +15,13 @@
15 15
16button { 16button {
17 width: 30px; 17 width: 30px;
18 border: none; 18 border: 0;
19 19
20 &::after { 20 &::after {
21 display: none; 21 display: none;
22 } 22 }
23 23
24 &.routeActive { 24 &.route-active {
25 &::after { 25 &::after {
26 display: inherit; 26 display: inherit;
27 border: 2px solid pvar(--mainColor); 27 border: 2px solid pvar(--mainColor);
@@ -36,7 +36,7 @@ button {
36 margin-top: 0 !important; 36 margin-top: 0 !important;
37 position: static; 37 position: static;
38 right: auto; 38 right: auto;
39 bottom: auto 39 bottom: auto;
40} 40}
41 41
42.modal-body { 42.modal-body {
diff --git a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss
index 84dd7dce3..ffabb3646 100644
--- a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss
+++ b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss
@@ -11,12 +11,12 @@
11 } 11 }
12} 12}
13 13
14::ng-deep .dropdown-toggle::after { 14.sub-menu ::ng-deep .dropdown-toggle::after {
15 position: relative; 15 position: relative;
16 top: 2px; 16 top: 2px;
17} 17}
18 18
19::ng-deep .dropdown-menu { 19.sub-menu ::ng-deep .dropdown-menu {
20 margin-top: 0 !important; 20 margin-top: 0 !important;
21} 21}
22 22
diff --git a/client/src/app/shared/shared-main/users/user-notification.model.ts b/client/src/app/shared/shared-main/users/user-notification.model.ts
index 88a4811da..ed5791794 100644
--- a/client/src/app/shared/shared-main/users/user-notification.model.ts
+++ b/client/src/app/shared/shared-main/users/user-notification.model.ts
@@ -258,10 +258,10 @@ export class UserNotification implements UserNotificationServer {
258 } 258 }
259 259
260 private setAccountAvatarUrl (actor: { avatarUrl?: string, avatar?: { url?: string, path: string } }) { 260 private setAccountAvatarUrl (actor: { avatarUrl?: string, avatar?: { url?: string, path: string } }) {
261 actor.avatarUrl = Account.GET_ACTOR_AVATAR_URL(actor) 261 actor.avatarUrl = Account.GET_ACTOR_AVATAR_URL(actor) || Account.GET_DEFAULT_AVATAR_URL()
262 } 262 }
263 263
264 private setVideoChannelAvatarUrl (actor: { avatarUrl?: string, avatar?: { url?: string, path: string } }) { 264 private setVideoChannelAvatarUrl (actor: { avatarUrl?: string, avatar?: { url?: string, path: string } }) {
265 actor.avatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(actor) 265 actor.avatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(actor) || VideoChannel.GET_DEFAULT_AVATAR_URL()
266 } 266 }
267} 267}
diff --git a/client/src/app/shared/shared-main/users/user-notifications.component.scss b/client/src/app/shared/shared-main/users/user-notifications.component.scss
index 5166bd559..b69d4b5d6 100644
--- a/client/src/app/shared/shared-main/users/user-notifications.component.scss
+++ b/client/src/app/shared/shared-main/users/user-notifications.component.scss
@@ -21,16 +21,19 @@
21 } 21 }
22 22
23 my-global-icon { 23 my-global-icon {
24 @include apply-svg-color(#333);
25
24 width: 24px; 26 width: 24px;
25 margin-right: 11px; 27 margin-right: 11px;
26 margin-left: 3px; 28 margin-left: 3px;
27
28 @include apply-svg-color(#333);
29 } 29 }
30 30
31 .avatar { 31 .avatar {
32 @include avatar(30px); 32 width: 30px;
33 33 height: 30px;
34 min-width: 30px;
35 min-height: 30px;
36 border-radius: 5px;
34 margin-right: 10px; 37 margin-right: 10px;
35 } 38 }
36 39
diff --git a/client/src/app/shared/shared-main/users/user-quota.component.scss b/client/src/app/shared/shared-main/users/user-quota.component.scss
index c670559d3..c06cafe29 100644
--- a/client/src/app/shared/shared-main/users/user-quota.component.scss
+++ b/client/src/app/shared/shared-main/users/user-quota.component.scss
@@ -11,7 +11,8 @@ label {
11 margin-right: 5px; 11 margin-right: 5px;
12 } 12 }
13 13
14 &, .progress { 14 &,
15 .progress {
15 width: 100% !important; 16 width: 100% !important;
16 } 17 }
17 18
diff --git a/client/src/app/shared/shared-main/video-channel/video-channel.model.ts b/client/src/app/shared/shared-main/video-channel/video-channel.model.ts
index 1ba3fcc0e..c40dd5311 100644
--- a/client/src/app/shared/shared-main/video-channel/video-channel.model.ts
+++ b/client/src/app/shared/shared-main/video-channel/video-channel.model.ts
@@ -18,14 +18,13 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
18 18
19 ownerAccount?: ServerAccount 19 ownerAccount?: ServerAccount
20 ownerBy?: string 20 ownerBy?: string
21 ownerAvatarUrl?: string
22 21
23 videosCount?: number 22 videosCount?: number
24 23
25 viewsPerDay?: ViewsPerDate[] 24 viewsPerDay?: ViewsPerDate[]
26 25
27 static GET_ACTOR_AVATAR_URL (actor: object) { 26 static GET_ACTOR_AVATAR_URL (actor: object) {
28 return Actor.GET_ACTOR_AVATAR_URL(actor) || this.GET_DEFAULT_AVATAR_URL() 27 return Actor.GET_ACTOR_AVATAR_URL(actor)
29 } 28 }
30 29
31 static GET_ACTOR_BANNER_URL (channel: ServerVideoChannel) { 30 static GET_ACTOR_BANNER_URL (channel: ServerVideoChannel) {
@@ -67,7 +66,6 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
67 if (hash.ownerAccount) { 66 if (hash.ownerAccount) {
68 this.ownerAccount = hash.ownerAccount 67 this.ownerAccount = hash.ownerAccount
69 this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host) 68 this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host)
70 this.ownerAvatarUrl = Account.GET_ACTOR_AVATAR_URL(this.ownerAccount)
71 } 69 }
72 70
73 this.updateComputedAttributes() 71 this.updateComputedAttributes()
@@ -94,7 +92,6 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
94 } 92 }
95 93
96 updateComputedAttributes () { 94 updateComputedAttributes () {
97 this.avatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(this)
98 this.bannerUrl = VideoChannel.GET_ACTOR_BANNER_URL(this) 95 this.bannerUrl = VideoChannel.GET_ACTOR_BANNER_URL(this)
99 } 96 }
100} 97}
diff --git a/client/src/app/shared/shared-main/video/video.model.ts b/client/src/app/shared/shared-main/video/video.model.ts
index 14c507295..526d10e32 100644
--- a/client/src/app/shared/shared-main/video/video.model.ts
+++ b/client/src/app/shared/shared-main/video/video.model.ts
@@ -20,8 +20,6 @@ export class Video implements VideoServerModel {
20 byVideoChannel: string 20 byVideoChannel: string
21 byAccount: string 21 byAccount: string
22 22
23 videoChannelAvatarUrl: string
24
25 createdAt: Date 23 createdAt: Date
26 updatedAt: Date 24 updatedAt: Date
27 publishedAt: Date 25 publishedAt: Date
@@ -143,7 +141,6 @@ export class Video implements VideoServerModel {
143 141
144 this.byAccount = Actor.CREATE_BY_STRING(hash.account.name, hash.account.host) 142 this.byAccount = Actor.CREATE_BY_STRING(hash.account.name, hash.account.host)
145 this.byVideoChannel = Actor.CREATE_BY_STRING(hash.channel.name, hash.channel.host) 143 this.byVideoChannel = Actor.CREATE_BY_STRING(hash.channel.name, hash.channel.host)
146 this.videoChannelAvatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(this.channel)
147 144
148 this.category.label = peertubeTranslate(this.category.label, translations) 145 this.category.label = peertubeTranslate(this.category.label, translations)
149 this.licence.label = peertubeTranslate(this.licence.label, translations) 146 this.licence.label = peertubeTranslate(this.licence.label, translations)
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 0b708b692..7b17bd2ab 100644
--- a/client/src/app/shared/shared-main/video/video.service.ts
+++ b/client/src/app/shared/shared-main/video/video.service.ts
@@ -124,7 +124,17 @@ export class VideoService implements VideosProvider {
124 124
125 let params = new HttpParams() 125 let params = new HttpParams()
126 params = this.restService.addRestGetParams(params, pagination, sort) 126 params = this.restService.addRestGetParams(params, pagination, sort)
127 params = this.restService.addObjectParams(params, { search }) 127
128 if (search) {
129 const filters = this.restService.parseQueryStringFilter(search, {
130 isLive: {
131 prefix: 'isLive:',
132 isBoolean: true
133 }
134 })
135
136 params = this.restService.addObjectParams(params, filters)
137 }
128 138
129 return this.authHttp 139 return this.authHttp
130 .get<ResultList<Video>>(UserService.BASE_USERS_URL + 'me/videos', { params }) 140 .get<ResultList<Video>>(UserService.BASE_USERS_URL + 'me/videos', { params })
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.html b/client/src/app/shared/shared-moderation/account-blocklist.component.html
index 3f2f55559..a9fac0810 100644
--- a/client/src/app/shared/shared-moderation/account-blocklist.component.html
+++ b/client/src/app/shared/shared-moderation/account-blocklist.component.html
@@ -11,13 +11,8 @@
11> 11>
12 <ng-template pTemplate="caption"> 12 <ng-template pTemplate="caption">
13 <div class="caption"> 13 <div class="caption">
14 <div class="ml-auto has-feedback has-clear"> 14 <div class="ml-auto">
15 <input 15 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
16 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
17 (keyup)="onSearch($event)"
18 >
19 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
20 <span class="sr-only" i18n>Clear filters</span>
21 </div> 16 </div>
22 </div> 17 </div>
23 </ng-template> 18 </ng-template>
@@ -38,7 +33,7 @@
38 <td> 33 <td>
39 <a [href]="accountBlock.blockedAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> 34 <a [href]="accountBlock.blockedAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
40 <div class="chip two-lines"> 35 <div class="chip two-lines">
41 <my-account-avatar [account]="accountBlock.blockedAccount"></my-account-avatar> 36 <my-actor-avatar [account]="accountBlock.blockedAccount"></my-actor-avatar>
42 <div> 37 <div>
43 {{ accountBlock.blockedAccount.displayName }} 38 {{ accountBlock.blockedAccount.displayName }}
44 <span class="text-muted">{{ accountBlock.blockedAccount.nameWithHost }}</span> 39 <span class="text-muted">{{ accountBlock.blockedAccount.nameWithHost }}</span>
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.scss b/client/src/app/shared/shared-moderation/account-blocklist.component.scss
index 3eede44eb..bc441811e 100644
--- a/client/src/app/shared/shared-moderation/account-blocklist.component.scss
+++ b/client/src/app/shared/shared-moderation/account-blocklist.component.scss
@@ -1,15 +1,6 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4.caption {
5 justify-content: flex-end;
6
7 input {
8 @include peertube-input-text(250px);
9 flex-grow: 1;
10 }
11}
12
13.chip { 4.chip {
14 @include chip; 5 @include chip;
15} 6}
@@ -17,4 +8,4 @@
17.unblock-button { 8.unblock-button {
18 @include peertube-button; 9 @include peertube-button;
19 @include grey-button; 10 @include grey-button;
20} \ No newline at end of file 11}
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.ts b/client/src/app/shared/shared-moderation/account-blocklist.component.ts
index 1bce65bf0..1146aeec0 100644
--- a/client/src/app/shared/shared-moderation/account-blocklist.component.ts
+++ b/client/src/app/shared/shared-moderation/account-blocklist.component.ts
@@ -44,12 +44,12 @@ export class GenericAccountBlocklistComponent extends RestTable implements OnIni
44 : $localize`Account ${blockedAccount.nameWithHost} unmuted by your instance.` 44 : $localize`Account ${blockedAccount.nameWithHost} unmuted by your instance.`
45 ) 45 )
46 46
47 this.loadData() 47 this.reloadData()
48 } 48 }
49 ) 49 )
50 } 50 }
51 51
52 protected loadData () { 52 protected reloadData () {
53 const operation = this.mode === BlocklistComponentType.Account 53 const operation = this.mode === BlocklistComponentType.Account
54 ? this.blocklistService.getUserAccountBlocklist({ 54 ? this.blocklistService.getUserAccountBlocklist({
55 pagination: this.pagination, 55 pagination: this.pagination,
diff --git a/client/src/app/shared/shared-moderation/moderation.scss b/client/src/app/shared/shared-moderation/moderation.scss
index cdcc12fe0..b13d06f03 100644
--- a/client/src/app/shared/shared-moderation/moderation.scss
+++ b/client/src/app/shared/shared-moderation/moderation.scss
@@ -17,33 +17,25 @@
17 word-wrap: break-word; 17 word-wrap: break-word;
18 18
19 ::ng-deep p:last-child { 19 ::ng-deep p:last-child {
20 margin-bottom: 0px !important; 20 margin-bottom: 0 !important;
21 } 21 }
22 } 22 }
23} 23}
24 24
25.screenratio { 25.screenratio {
26 div {
27 @include miniature-thumbnail;
28
29 display: inline-flex;
30 justify-content: center;
31 align-items: center;
32 color: pvar(--inputPlaceholderColor);
33 }
34
35 @include block-ratio($selector: 'div, ::ng-deep iframe') { 26 @include block-ratio($selector: 'div, ::ng-deep iframe') {
36 width: 100% !important; 27 width: 100% !important;
37 height: 100% !important; 28 height: 100% !important;
38 left: 0; 29 left: 0;
39 }; 30 };
40}
41 31
42.input-group { 32 div {
43 @include peertube-input-group(300px); 33 @include miniature-thumbnail;
44 34
45 .dropdown-toggle::after { 35 display: inline-flex;
46 margin-left: 0; 36 justify-content: center;
37 align-items: center;
38 color: pvar(--inputPlaceholderColor);
47 } 39 }
48} 40}
49 41
@@ -51,15 +43,6 @@
51 @include chip; 43 @include chip;
52} 44}
53 45
54.caption {
55 justify-content: flex-end;
56
57 input {
58 @include peertube-input-text(250px);
59 flex-grow: 1;
60 }
61}
62
63my-action-dropdown.show { 46my-action-dropdown.show {
64 ::ng-deep .dropdown-root { 47 ::ng-deep .dropdown-root {
65 display: block !important; 48 display: block !important;
@@ -93,15 +76,15 @@ my-action-dropdown.show {
93 display: inline-flex; 76 display: inline-flex;
94 77
95 .table-video-image { 78 .table-video-image {
96 @include miniature-thumbnail;
97
98 $image-height: 45px; 79 $image-height: 45px;
99 80
81 @include miniature-thumbnail;
82
100 height: $image-height; 83 height: $image-height;
101 width: #{(16/9) * $image-height}; 84 width: #{(16/9) * $image-height};
102 margin-right: 0.5rem; 85 margin-right: 0.5rem;
103 border-radius: 2px; 86 border-radius: 2px;
104 border: none; 87 border: 0;
105 background: transparent; 88 background: transparent;
106 display: inline-flex; 89 display: inline-flex;
107 justify-content: center; 90 justify-content: center;
@@ -139,7 +122,7 @@ my-action-dropdown.show {
139 122
140 div .glyphicon { 123 div .glyphicon {
141 font-size: 80%; 124 font-size: 80%;
142 color: gray; 125 color: #808080;
143 margin-left: 0.1rem; 126 margin-left: 0.1rem;
144 } 127 }
145 128
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.html b/client/src/app/shared/shared-moderation/server-blocklist.component.html
index 537186f05..c6d29bb21 100644
--- a/client/src/app/shared/shared-moderation/server-blocklist.component.html
+++ b/client/src/app/shared/shared-moderation/server-blocklist.component.html
@@ -4,8 +4,9 @@
4</h1> 4</h1>
5 5
6<p-table 6<p-table
7 [value]="blockedServers" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" 7 [value]="blockedServers" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
8 [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)" 8 [sortField]="sort.field" [sortOrder]="sort.order" (onPage)="onPage($event)"
9 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
9 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 10 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
10 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted instances" 11 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted instances"
11> 12>
@@ -18,13 +19,8 @@
18 </a> 19 </a>
19 </div> 20 </div>
20 21
21 <div class="ml-auto has-feedback has-clear"> 22 <div class="ml-auto">
22 <input 23 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
23 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
24 (keyup)="onSearch($event)"
25 >
26 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
27 <span class="sr-only" i18n>Clear filters</span>
28 </div> 24 </div>
29 </div> 25 </div>
30 </ng-template> 26 </ng-template>
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.scss b/client/src/app/shared/shared-moderation/server-blocklist.component.scss
index 31db4d92b..a22972c5f 100644
--- a/client/src/app/shared/shared-moderation/server-blocklist.component.scss
+++ b/client/src/app/shared/shared-moderation/server-blocklist.component.scss
@@ -5,7 +5,8 @@ a {
5 @include disable-default-a-behaviour; 5 @include disable-default-a-behaviour;
6 display: inline-block; 6 display: inline-block;
7 7
8 &, &:hover { 8 &,
9 &:hover {
9 color: pvar(--mainForegroundColor); 10 color: pvar(--mainForegroundColor);
10 } 11 }
11 12
@@ -15,15 +16,6 @@ a {
15 } 16 }
16} 17}
17 18
18.caption {
19 justify-content: flex-end;
20
21 input {
22 @include peertube-input-text(250px);
23 flex-grow: 1;
24 }
25}
26
27.unblock-button { 19.unblock-button {
28 @include peertube-button; 20 @include peertube-button;
29 @include grey-button; 21 @include grey-button;
@@ -33,15 +25,6 @@ a {
33 @include create-button; 25 @include create-button;
34} 26}
35 27
36.caption {
37 justify-content: flex-end;
38
39 input {
40 @include peertube-input-text(250px);
41 flex-grow: 1;
42 }
43}
44
45.chip { 28.chip {
46 @include chip; 29 @include chip;
47} 30}
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.ts b/client/src/app/shared/shared-moderation/server-blocklist.component.ts
index 546fd53c3..274d8f6e9 100644
--- a/client/src/app/shared/shared-moderation/server-blocklist.component.ts
+++ b/client/src/app/shared/shared-moderation/server-blocklist.component.ts
@@ -46,7 +46,7 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit
46 : $localize`Instance ${host} unmuted by your instance.` 46 : $localize`Instance ${host} unmuted by your instance.`
47 ) 47 )
48 48
49 this.loadData() 49 this.reloadData()
50 } 50 }
51 ) 51 )
52 } 52 }
@@ -69,13 +69,13 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit
69 : $localize`Instance ${domain} muted by your instance.` 69 : $localize`Instance ${domain} muted by your instance.`
70 ) 70 )
71 71
72 this.loadData() 72 this.reloadData()
73 } 73 }
74 ) 74 )
75 }) 75 })
76 } 76 }
77 77
78 protected loadData () { 78 protected reloadData () {
79 const operation = this.mode === BlocklistComponentType.Account 79 const operation = this.mode === BlocklistComponentType.Account
80 ? this.blocklistService.getUserServerBlocklist({ 80 ? this.blocklistService.getUserServerBlocklist({
81 pagination: this.pagination, 81 pagination: this.pagination,
diff --git a/client/src/app/shared/shared-moderation/shared-moderation.module.ts b/client/src/app/shared/shared-moderation/shared-moderation.module.ts
index c7e201792..95213e2bd 100644
--- a/client/src/app/shared/shared-moderation/shared-moderation.module.ts
+++ b/client/src/app/shared/shared-moderation/shared-moderation.module.ts
@@ -13,7 +13,7 @@ import { UserBanModalComponent } from './user-ban-modal.component'
13import { UserModerationDropdownComponent } from './user-moderation-dropdown.component' 13import { UserModerationDropdownComponent } from './user-moderation-dropdown.component'
14import { VideoBlockComponent } from './video-block.component' 14import { VideoBlockComponent } from './video-block.component'
15import { VideoBlockService } from './video-block.service' 15import { VideoBlockService } from './video-block.service'
16import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-account-avatar.module' 16import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module'
17 17
18@NgModule({ 18@NgModule({
19 imports: [ 19 imports: [
@@ -21,7 +21,7 @@ import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-accou
21 SharedFormModule, 21 SharedFormModule,
22 SharedGlobalIconModule, 22 SharedGlobalIconModule,
23 SharedVideoCommentModule, 23 SharedVideoCommentModule,
24 SharedAccountAvatarModule 24 SharedActorImageModule
25 ], 25 ],
26 26
27 declarations: [ 27 declarations: [
diff --git a/client/src/app/shared/shared-moderation/video-block.component.scss b/client/src/app/shared/shared-moderation/video-block.component.scss
index afa0d96f7..a6e33070b 100644
--- a/client/src/app/shared/shared-moderation/video-block.component.scss
+++ b/client/src/app/shared/shared-moderation/video-block.component.scss
@@ -7,5 +7,5 @@ textarea {
7 7
8.live-info { 8.live-info {
9 font-size: 15px; 9 font-size: 15px;
10 margin: 40px 0 20px 0; 10 margin: 40px 0 20px;
11} 11}
diff --git a/client/src/app/shared/shared-search/advanced-search.model.ts b/client/src/app/shared/shared-search/advanced-search.model.ts
index 30badc8fa..0e3924841 100644
--- a/client/src/app/shared/shared-search/advanced-search.model.ts
+++ b/client/src/app/shared/shared-search/advanced-search.model.ts
@@ -1,4 +1,4 @@
1import { NSFWQuery, SearchTargetType } from '@shared/models' 1import { BooleanBothQuery, SearchTargetType } from '@shared/models'
2 2
3export class AdvancedSearch { 3export class AdvancedSearch {
4 startDate: string // ISO 8601 4 startDate: string // ISO 8601
@@ -7,7 +7,7 @@ export class AdvancedSearch {
7 originallyPublishedStartDate: string // ISO 8601 7 originallyPublishedStartDate: string // ISO 8601
8 originallyPublishedEndDate: string // ISO 8601 8 originallyPublishedEndDate: string // ISO 8601
9 9
10 nsfw: NSFWQuery 10 nsfw: BooleanBothQuery
11 11
12 categoryOneOf: string 12 categoryOneOf: string
13 13
@@ -33,7 +33,7 @@ export class AdvancedSearch {
33 endDate?: string 33 endDate?: string
34 originallyPublishedStartDate?: string 34 originallyPublishedStartDate?: string
35 originallyPublishedEndDate?: string 35 originallyPublishedEndDate?: string
36 nsfw?: NSFWQuery 36 nsfw?: BooleanBothQuery
37 categoryOneOf?: string 37 categoryOneOf?: string
38 licenceOneOf?: string 38 licenceOneOf?: string
39 languageOneOf?: string 39 languageOneOf?: string
diff --git a/client/src/app/shared/shared-thumbnail/video-thumbnail.component.scss b/client/src/app/shared/shared-thumbnail/video-thumbnail.component.scss
index ea59ab346..e678d6edf 100644
--- a/client/src/app/shared/shared-thumbnail/video-thumbnail.component.scss
+++ b/client/src/app/shared/shared-thumbnail/video-thumbnail.component.scss
@@ -11,7 +11,7 @@
11 width: 100%; 11 width: 100%;
12 position: absolute; 12 position: absolute;
13 bottom: 0; 13 bottom: 0;
14 background-color: rgba(0, 0, 0, 0.20); 14 background-color: rgba(0, 0, 0, 0.2);
15 15
16 div { 16 div {
17 height: 100%; 17 height: 100%;
@@ -39,8 +39,8 @@
39 top: 5px; 39 top: 5px;
40 font-weight: $font-bold; 40 font-weight: $font-bold;
41 41
42 &.warning { background-color: orange; } 42 &.warning { background-color: #ffa500; }
43 &.danger { background-color: red; } 43 &.danger { background-color: #ff0000; }
44} 44}
45 45
46.video-thumbnail-duration-overlay, 46.video-thumbnail-duration-overlay,
@@ -77,9 +77,9 @@
77 padding: 3px; 77 padding: 3px;
78 78
79 my-global-icon { 79 my-global-icon {
80 @include apply-svg-color(#fff);
81
80 width: 22px; 82 width: 22px;
81 height: 22px; 83 height: 22px;
82
83 @include apply-svg-color(#fff);
84 } 84 }
85} 85}
diff --git a/client/src/app/shared/shared-user-settings/user-video-settings.component.html b/client/src/app/shared/shared-user-settings/user-video-settings.component.html
index aa261fdce..a49e11485 100644
--- a/client/src/app/shared/shared-user-settings/user-video-settings.component.html
+++ b/client/src/app/shared/shared-user-settings/user-video-settings.component.html
@@ -5,7 +5,7 @@
5 <my-help> 5 <my-help>
6 <ng-template ptTemplate="customHtml"> 6 <ng-template ptTemplate="customHtml">
7 <ng-container i18n> 7 <ng-container i18n>
8 With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video. 8 With <strong>Hide</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video.
9 </ng-container> 9 </ng-container>
10 </ng-template> 10 </ng-template>
11 </my-help> 11 </my-help>
@@ -13,7 +13,7 @@
13 <div class="peertube-select-container"> 13 <div class="peertube-select-container">
14 <select id="nsfwPolicy" formControlName="nsfwPolicy" class="form-control"> 14 <select id="nsfwPolicy" formControlName="nsfwPolicy" class="form-control">
15 <option i18n value="undefined" disabled>Policy for sensitive videos</option> 15 <option i18n value="undefined" disabled>Policy for sensitive videos</option>
16 <option i18n value="do_not_list">Do not list</option> 16 <option i18n value="do_not_list">Hide</option>
17 <option i18n value="blur">Blur thumbnails</option> 17 <option i18n value="blur">Blur thumbnails</option>
18 <option i18n value="display">Display</option> 18 <option i18n value="display">Display</option>
19 </select> 19 </select>
diff --git a/client/src/app/shared/shared-user-settings/user-video-settings.component.ts b/client/src/app/shared/shared-user-settings/user-video-settings.component.ts
index d74c2b2d8..ae95030c7 100644
--- a/client/src/app/shared/shared-user-settings/user-video-settings.component.ts
+++ b/client/src/app/shared/shared-user-settings/user-video-settings.component.ts
@@ -38,8 +38,6 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
38 ngOnInit () { 38 ngOnInit () {
39 this.allLanguagesGroup = $localize`All languages` 39 this.allLanguagesGroup = $localize`All languages`
40 40
41 let oldForm: any
42
43 this.buildForm({ 41 this.buildForm({
44 nsfwPolicy: null, 42 nsfwPolicy: null,
45 webTorrentEnabled: null, 43 webTorrentEnabled: null,
@@ -73,16 +71,7 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
73 videoLanguages 71 videoLanguages
74 }) 72 })
75 73
76 if (this.reactiveUpdate) { 74 if (this.reactiveUpdate) this.handleReactiveUpdate()
77 oldForm = { ...this.form.value }
78
79 this.formValuesWatcher = this.form.valueChanges.subscribe((formValue: any) => {
80 const updatedKey = Object.keys(formValue).find(k => formValue[k] !== oldForm[k])
81 oldForm = { ...this.form.value }
82
83 this.updateDetails([ updatedKey ])
84 })
85 }
86 }) 75 })
87 } 76 }
88 77
@@ -96,7 +85,7 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
96 const autoPlayVideo = this.form.value['autoPlayVideo'] 85 const autoPlayVideo = this.form.value['autoPlayVideo']
97 const autoPlayNextVideo = this.form.value['autoPlayNextVideo'] 86 const autoPlayNextVideo = this.form.value['autoPlayNextVideo']
98 87
99 const videoLanguagesForm = this.form.value['videoLanguages'] 88 let videoLanguagesForm = this.form.value['videoLanguages']
100 89
101 if (Array.isArray(videoLanguagesForm)) { 90 if (Array.isArray(videoLanguagesForm)) {
102 if (videoLanguagesForm.length > 20) { 91 if (videoLanguagesForm.length > 20) {
@@ -104,13 +93,14 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
104 return 93 return
105 } 94 }
106 95
96 // Automatically use "All languages" if the user did not select any language
107 if (videoLanguagesForm.length === 0) { 97 if (videoLanguagesForm.length === 0) {
108 this.notifier.error($localize`You need to enable at least 1 video language.`) 98 videoLanguagesForm = [ this.allLanguagesGroup ]
109 return 99 this.form.patchValue({ videoLanguages: [ { group: this.allLanguagesGroup } ] })
110 } 100 }
111 } 101 }
112 102
113 const videoLanguages = this.getVideoLanguages(videoLanguagesForm) 103 const videoLanguages = this.buildLanguagesFromForm(videoLanguagesForm)
114 104
115 let details: UserUpdateMe = { 105 let details: UserUpdateMe = {
116 nsfwPolicy, 106 nsfwPolicy,
@@ -127,22 +117,13 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
127 if (onlyKeys) details = pick(details, onlyKeys) 117 if (onlyKeys) details = pick(details, onlyKeys)
128 118
129 if (this.authService.isLoggedIn()) { 119 if (this.authService.isLoggedIn()) {
130 this.userService.updateMyProfile(details).subscribe( 120 return this.updateLoggedProfile(details)
131 () => {
132 this.authService.refreshUserInformation()
133
134 if (this.notifyOnUpdate) this.notifier.success($localize`Video settings updated.`)
135 },
136
137 err => this.notifier.error(err.message)
138 )
139 } else {
140 this.userService.updateMyAnonymousProfile(details)
141 if (this.notifyOnUpdate) this.notifier.success($localize`Display/Video settings updated.`)
142 } 121 }
122
123 return this.updateAnonymousProfile(details)
143 } 124 }
144 125
145 private getVideoLanguages (videoLanguages: ItemSelectCheckboxValue[]) { 126 private buildLanguagesFromForm (videoLanguages: ItemSelectCheckboxValue[]) {
146 if (!Array.isArray(videoLanguages)) return undefined 127 if (!Array.isArray(videoLanguages)) return undefined
147 128
148 // null means "All" 129 // null means "All"
@@ -166,4 +147,34 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
166 return l.id + '' 147 return l.id + ''
167 }) 148 })
168 } 149 }
150
151 private handleReactiveUpdate () {
152 let oldForm = { ...this.form.value }
153
154 this.formValuesWatcher = this.form.valueChanges.subscribe((formValue: any) => {
155 const updatedKey = Object.keys(formValue)
156 .find(k => formValue[k] !== oldForm[k])
157
158 oldForm = { ...this.form.value }
159
160 this.updateDetails([ updatedKey ])
161 })
162 }
163
164 private updateLoggedProfile (details: UserUpdateMe) {
165 this.userService.updateMyProfile(details).subscribe(
166 () => {
167 this.authService.refreshUserInformation()
168
169 if (this.notifyOnUpdate) this.notifier.success($localize`Video settings updated.`)
170 },
171
172 err => this.notifier.error(err.message)
173 )
174 }
175
176 private updateAnonymousProfile (details: UserUpdateMe) {
177 this.userService.updateMyAnonymousProfile(details)
178 if (this.notifyOnUpdate) this.notifier.success($localize`Display/Video settings updated.`)
179 }
169} 180}
diff --git a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.scss b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.scss
index 698c5866a..73db0d090 100644
--- a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.scss
+++ b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.scss
@@ -3,4 +3,4 @@
3.btn-remote-follow { 3.btn-remote-follow {
4 @include peertube-button; 4 @include peertube-button;
5 @include orange-button; 5 @include orange-button;
6} \ No newline at end of file 6}
diff --git a/client/src/app/shared/shared-user-subscription/subscribe-button.component.scss b/client/src/app/shared/shared-user-subscription/subscribe-button.component.scss
index f6cdc11c0..897ee7799 100644
--- a/client/src/app/shared/shared-user-subscription/subscribe-button.component.scss
+++ b/client/src/app/shared/shared-user-subscription/subscribe-button.component.scss
@@ -8,8 +8,8 @@
8 float: right; 8 float: right;
9 padding: 0; 9 padding: 0;
10 10
11 & > .btn, 11 > .btn,
12 & > .dropdown > .dropdown-toggle { 12 > .dropdown > .dropdown-toggle {
13 font-size: 15px; 13 font-size: 15px;
14 } 14 }
15 15
@@ -20,7 +20,7 @@
20 &.big { 20 &.big {
21 height: 35px; 21 height: 35px;
22 22
23 & > button:first-child { 23 > button:first-child {
24 width: max-content; 24 width: max-content;
25 min-width: 175px; 25 min-width: 175px;
26 } 26 }
@@ -29,7 +29,7 @@
29 span:first-child { 29 span:first-child {
30 line-height: 80%; 30 line-height: 80%;
31 } 31 }
32 32
33 span:not(:first-child) { 33 span:not(:first-child) {
34 font-size: 75%; 34 font-size: 75%;
35 } 35 }
@@ -37,15 +37,15 @@
37 } 37 }
38 38
39 // Unlogged 39 // Unlogged
40 & > .dropdown > .dropdown-toggle span { 40 > .dropdown > .dropdown-toggle span {
41 padding-right: 3px; 41 padding-right: 3px;
42 } 42 }
43 43
44 // Logged 44 // Logged
45 & > .btn { 45 > .btn {
46 padding-right: 4px; 46 padding-right: 4px;
47 47
48 & + .dropdown > button { 48 + .dropdown > button {
49 padding-left: 2px; 49 padding-left: 2px;
50 50
51 &::after { 51 &::after {
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 0f09778df..c5aeb3c12 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
@@ -190,13 +190,7 @@ export class VideoCommentService {
190 const filters = this.restService.parseQueryStringFilter(search, { 190 const filters = this.restService.parseQueryStringFilter(search, {
191 isLocal: { 191 isLocal: {
192 prefix: 'local:', 192 prefix: 'local:',
193 isBoolean: true, 193 isBoolean: true
194 handler: v => {
195 if (v === 'true') return v
196 if (v === 'false') return v
197
198 return undefined
199 }
200 }, 194 },
201 195
202 searchAccount: { prefix: 'account:' }, 196 searchAccount: { prefix: 'account:' },
diff --git a/client/src/app/shared/shared-video-miniature/abstract-video-list.scss b/client/src/app/shared/shared-video-miniature/abstract-video-list.scss
index 467ca1d2c..d9cf7a14f 100644
--- a/client/src/app/shared/shared-video-miniature/abstract-video-list.scss
+++ b/client/src/app/shared/shared-video-miniature/abstract-video-list.scss
@@ -3,7 +3,7 @@
3@import '_mixins'; 3@import '_mixins';
4@import '_miniature'; 4@import '_miniature';
5 5
6$iconSize: 16px; 6$icon-size: 16px;
7 7
8::ng-deep my-video-list-header { 8::ng-deep my-video-list-header {
9 display: flex; 9 display: flex;
@@ -17,20 +17,19 @@ $iconSize: 16px;
17 17
18 my-feed { 18 my-feed {
19 display: inline-block; 19 display: inline-block;
20 width: calc(#{$iconSize} - 2px); 20 width: calc(#{$icon-size} - 2px);
21 } 21 }
22 22
23 .moderation-block { 23 .moderation-block {
24
25 my-global-icon {
26 position: relative;
27 width: $iconSize;
28 }
29
30 margin-left: .4rem; 24 margin-left: .4rem;
31 display: flex; 25 display: flex;
32 justify-content: flex-end; 26 justify-content: flex-end;
33 align-items: center; 27 align-items: center;
28
29 my-global-icon {
30 position: relative;
31 width: $icon-size;
32 }
34 } 33 }
35} 34}
36 35
@@ -72,7 +71,7 @@ $iconSize: 16px;
72 71
73 .title-page { 72 .title-page {
74 margin-bottom: 10px; 73 margin-bottom: 10px;
75 margin-right: 0px; 74 margin-right: 0;
76 } 75 }
77 } 76 }
78} 77}
diff --git a/client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts b/client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts
index 32cfdfd68..03be6d2ff 100644
--- a/client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts
+++ b/client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts
@@ -13,7 +13,7 @@ import { VideoDownloadComponent } from './video-download.component'
13import { VideoMiniatureComponent } from './video-miniature.component' 13import { VideoMiniatureComponent } from './video-miniature.component'
14import { VideosSelectionComponent } from './videos-selection.component' 14import { VideosSelectionComponent } from './videos-selection.component'
15import { VideoListHeaderComponent } from './video-list-header.component' 15import { VideoListHeaderComponent } from './video-list-header.component'
16import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-account-avatar.module' 16import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module'
17 17
18@NgModule({ 18@NgModule({
19 imports: [ 19 imports: [
@@ -25,7 +25,7 @@ import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-accou
25 SharedGlobalIconModule, 25 SharedGlobalIconModule,
26 SharedVideoLiveModule, 26 SharedVideoLiveModule,
27 SharedVideoModule, 27 SharedVideoModule,
28 SharedAccountAvatarModule 28 SharedActorImageModule
29 ], 29 ],
30 30
31 declarations: [ 31 declarations: [
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.scss b/client/src/app/shared/shared-video-miniature/video-download.component.scss
index 7f6e03c87..b689b1046 100644
--- a/client/src/app/shared/shared-video-miniature/video-download.component.scss
+++ b/client/src/app/shared/shared-video-miniature/video-download.component.scss
@@ -28,7 +28,7 @@
28 28
29 border-top-right-radius: 0; 29 border-top-right-radius: 0;
30 border-bottom-right-radius: 0; 30 border-bottom-right-radius: 0;
31 border-right: none; 31 border-right: 0;
32 32
33 select { 33 select {
34 height: inherit; 34 height: inherit;
@@ -85,7 +85,7 @@
85 &.metadata-attribute-tags { 85 &.metadata-attribute-tags {
86 .metadata-attribute-value:not(:nth-child(2)) { 86 .metadata-attribute-value:not(:nth-child(2)) {
87 &::before { 87 &::before {
88 content: ', ' 88 content: ', ';
89 } 89 }
90 } 90 }
91 } 91 }
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.html b/client/src/app/shared/shared-video-miniature/video-miniature.component.html
index bc19127aa..645be92bd 100644
--- a/client/src/app/shared/shared-video-miniature/video-miniature.component.html
+++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.html
@@ -10,14 +10,15 @@
10 <div class="video-bottom"> 10 <div class="video-bottom">
11 <div class="video-miniature-information"> 11 <div class="video-miniature-information">
12 <div class="d-flex video-miniature-meta"> 12 <div class="d-flex video-miniature-meta">
13 <a *ngIf="displayOptions.avatar && displayOwnerVideoChannel()" class="channel-avatar" [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle"> 13 <my-actor-avatar
14 <img [src]="getAvatarUrl()" alt="" /> 14 *ngIf="displayOptions.avatar && displayOwnerVideoChannel()" [title]="channelLinkTitle"
15 </a> 15 [channel]="video.channel" [size]="actorImageSize" [internalHref]="[ '/video-channels', video.byVideoChannel ]"
16 ></my-actor-avatar>
16 17
17 <my-account-avatar 18 <my-actor-avatar
18 *ngIf="displayOptions.avatar && displayOwnerAccount()" [title]="channelLinkTitle" 19 *ngIf="displayOptions.avatar && displayOwnerAccount()" [title]="channelLinkTitle"
19 [account]="video.account" size="40" [internalHref]="'/video-channels/' + video.byVideoChannel" 20 [account]="video.account" [size]="actorImageSize" [internalHref]="[ '/video-channels', video.byVideoChannel ]"
20 ></my-account-avatar> 21 ></my-actor-avatar>
21 22
22 <div class="w-100 d-flex flex-column"> 23 <div class="w-100 d-flex flex-column">
23 <a *ngIf="!videoHref" tabindex="-1" class="video-miniature-name" 24 <a *ngIf="!videoHref" tabindex="-1" class="video-miniature-name"
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.scss b/client/src/app/shared/shared-video-miniature/video-miniature.component.scss
index f6f2925f0..5df89d019 100644
--- a/client/src/app/shared/shared-video-miniature/video-miniature.component.scss
+++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.scss
@@ -12,15 +12,10 @@ $more-button-width: 40px;
12 width: calc(100% - #{$more-button-width}); 12 width: calc(100% - #{$more-button-width});
13} 13}
14 14
15my-account-avatar, 15my-actor-avatar {
16.channel-avatar {
17 margin: 10px 10px 0 0; 16 margin: 10px 10px 0 0;
18} 17}
19 18
20.channel-avatar img{
21 @include channel-avatar(40px);
22}
23
24.video-miniature-created-at-views { 19.video-miniature-created-at-views {
25 font-size: 13px; 20 font-size: 13px;
26} 21}
@@ -46,7 +41,7 @@ my-account-avatar,
46} 41}
47 42
48.video-info-blocked { 43.video-info-blocked {
49 color: red; 44 color: #ff0000;
50 45
51 .blocked-reason::before { 46 .blocked-reason::before {
52 content: ' - '; 47 content: ' - ';
@@ -54,7 +49,7 @@ my-account-avatar,
54} 49}
55 50
56.video-info-nsfw { 51.video-info-nsfw {
57 color: red; 52 color: #ff0000;
58} 53}
59 54
60.video-actions { 55.video-actions {
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts
index 8d66aaee2..b58c118be 100644
--- a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts
+++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts
@@ -12,6 +12,7 @@ import {
12} from '@angular/core' 12} from '@angular/core'
13import { AuthService, ScreenService, ServerService, User } from '@app/core' 13import { AuthService, ScreenService, ServerService, User } from '@app/core'
14import { ServerConfig, VideoPlaylistType, VideoPrivacy, VideoState } from '@shared/models' 14import { ServerConfig, VideoPlaylistType, VideoPrivacy, VideoState } from '@shared/models'
15import { ActorAvatarSize } from '../shared-actor-image/actor-avatar.component'
15import { Video } from '../shared-main' 16import { Video } from '../shared-main'
16import { VideoPlaylistService } from '../shared-video-playlist' 17import { VideoPlaylistService } from '../shared-video-playlist'
17import { VideoActionsDisplayType } from './video-actions-dropdown.component' 18import { VideoActionsDisplayType } from './video-actions-dropdown.component'
@@ -51,6 +52,8 @@ export class VideoMiniatureComponent implements OnInit {
51 } 52 }
52 @Input() displayVideoActions = true 53 @Input() displayVideoActions = true
53 54
55 @Input() actorImageSize: ActorAvatarSize = '40'
56
54 @Input() displayAsRow = false 57 @Input() displayAsRow = false
55 58
56 @Input() videoLinkType: VideoLinkType = 'internal' 59 @Input() videoLinkType: VideoLinkType = 'internal'
@@ -180,14 +183,6 @@ export class VideoMiniatureComponent implements OnInit {
180 return '' 183 return ''
181 } 184 }
182 185
183 getAvatarUrl () {
184 if (this.displayOwnerAccount()) {
185 return this.video.account.avatar?.url
186 }
187
188 return this.video.videoChannelAvatarUrl
189 }
190
191 loadActions () { 186 loadActions () {
192 if (this.displayVideoActions) this.showActions = true 187 if (this.displayVideoActions) this.showActions = true
193 188
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.html b/client/src/app/shared/shared-video-miniature/videos-selection.component.html
index dec9e99f3..4ee90ce7f 100644
--- a/client/src/app/shared/shared-video-miniature/videos-selection.component.html
+++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.html
@@ -1,9 +1,9 @@
1<div class="no-results" i18n *ngIf="hasDoneFirstQuery && videos.length === 0">No results.</div> 1<div class="no-results" i18n *ngIf="hasDoneFirstQuery && videos.length === 0">{{ noResultMessage }}</div>
2 2
3<div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()" class="videos"> 3<div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()" class="videos">
4 <div class="video" *ngFor="let video of videos; let i = index; trackBy: videoById"> 4 <div class="video" *ngFor="let video of videos; let i = index; trackBy: videoById">
5 5
6 <div class="checkbox-container"> 6 <div class="checkbox-container" *ngIf="enableSelection">
7 <my-peertube-checkbox [inputName]="'video-check-' + video.id" [(ngModel)]="_selection[video.id]"></my-peertube-checkbox> 7 <my-peertube-checkbox [inputName]="'video-check-' + video.id" [(ngModel)]="_selection[video.id]"></my-peertube-checkbox>
8 </div> 8 </div>
9 9
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
index f8c3800d7..d64ee9b98 100644
--- a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
+++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
@@ -31,6 +31,9 @@ export class VideosSelectionComponent extends AbstractVideoList implements OnIni
31 @Input() pagination: ComponentPagination 31 @Input() pagination: ComponentPagination
32 @Input() titlePage: string 32 @Input() titlePage: string
33 @Input() miniatureDisplayOptions: MiniatureDisplayOptions 33 @Input() miniatureDisplayOptions: MiniatureDisplayOptions
34 @Input() noResultMessage = $localize`No results.`
35 @Input() enableSelection = true
36 @Input() loadOnInit = true
34 37
35 @Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<ResultList<Video>> 38 @Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<ResultList<Video>>
36 39
diff --git a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss
index b84cacece..cb1168196 100644
--- a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss
+++ b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss
@@ -126,7 +126,7 @@ $timestamp-margin-right: 10px;
126 border-top: 1px solid $separator-border-color; 126 border-top: 1px solid $separator-border-color;
127} 127}
128 128
129.new-playlist-button { 129.new-playlist-button {
130 cursor: pointer; 130 cursor: pointer;
131 131
132 my-global-icon { 132 my-global-icon {
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss
index 572f7d7a8..9ccd03912 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss
+++ b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss
@@ -84,21 +84,23 @@ my-video-thumbnail,
84 width: auto; 84 width: auto;
85 } 85 }
86 86
87 .video-info-account, .video-info-timestamp { 87 .video-info-account,
88 .video-info-timestamp {
88 color: pvar(--greyForegroundColor); 89 color: pvar(--greyForegroundColor);
89 } 90 }
90 } 91 }
91 } 92 }
92 93
93 .video-info-name { 94 .video-info-name {
95 @include ellipsis;
96
94 font-size: 18px; 97 font-size: 18px;
95 font-weight: $font-semibold; 98 font-weight: $font-semibold;
96 display: inline-block; 99 display: inline-block;
97
98 @include ellipsis;
99 } 100 }
100 101
101 .more, my-edit-button { 102 .more,
103 my-edit-button {
102 justify-self: flex-end; 104 justify-self: flex-end;
103 margin-left: auto; 105 margin-left: auto;
104 cursor: pointer; 106 cursor: pointer;
@@ -118,7 +120,7 @@ my-video-thumbnail,
118 display: flex; 120 display: flex;
119 121
120 &::after { 122 &::after {
121 border: none; 123 border: 0;
122 } 124 }
123 } 125 }
124 } 126 }
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss
index 99089166c..a46a6e475 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss
+++ b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss
@@ -6,7 +6,7 @@
6 display: inline-block; 6 display: inline-block;
7 width: 100%; 7 width: 100%;
8 8
9 &.no-videos:not(.to-manage){ 9 &.no-videos:not(.to-manage) {
10 a { 10 a {
11 cursor: default !important; 11 cursor: default !important;
12 } 12 }
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist.model.ts b/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
index 9bec16d77..5b6ba9dbf 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
+++ b/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
@@ -37,10 +37,8 @@ export class VideoPlaylist implements ServerVideoPlaylist {
37 embedUrl: string 37 embedUrl: string
38 38
39 ownerBy: string 39 ownerBy: string
40 ownerAvatarUrl: string
41 40
42 videoChannelBy?: string 41 videoChannelBy?: string
43 videoChannelAvatarUrl?: string
44 42
45 private thumbnailVersion: number 43 private thumbnailVersion: number
46 private originThumbnailUrl: string 44 private originThumbnailUrl: string
@@ -78,12 +76,10 @@ export class VideoPlaylist implements ServerVideoPlaylist {
78 76
79 this.ownerAccount = hash.ownerAccount 77 this.ownerAccount = hash.ownerAccount
80 this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host) 78 this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host)
81 this.ownerAvatarUrl = Account.GET_ACTOR_AVATAR_URL(this.ownerAccount)
82 79
83 if (hash.videoChannel) { 80 if (hash.videoChannel) {
84 this.videoChannel = hash.videoChannel 81 this.videoChannel = hash.videoChannel
85 this.videoChannelBy = Actor.CREATE_BY_STRING(hash.videoChannel.name, hash.videoChannel.host) 82 this.videoChannelBy = Actor.CREATE_BY_STRING(hash.videoChannel.name, hash.videoChannel.host)
86 this.videoChannelAvatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(this.videoChannel)
87 } 83 }
88 84
89 this.privacy.label = peertubeTranslate(this.privacy.label, translations) 85 this.privacy.label = peertubeTranslate(this.privacy.label, translations)