aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/shared')
-rw-r--r--client/src/app/shared/form-validators/user-validators.ts2
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-details.component.html12
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-details.component.scss4
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.html10
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.scss11
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-message-modal.component.scss5
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html4
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss51
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar.component.html4
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar.component.ts24
-rw-r--r--client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts2
-rw-r--r--client/src/app/shared/shared-forms/advanced-input-filter.component.html13
-rw-r--r--client/src/app/shared/shared-forms/advanced-input-filter.component.scss1
-rw-r--r--client/src/app/shared/shared-forms/dynamic-form-field.component.html2
-rw-r--r--client/src/app/shared/shared-forms/index.ts2
-rw-r--r--client/src/app/shared/shared-forms/input-switch.component.html2
-rw-r--r--client/src/app/shared/shared-forms/input-text.component.html20
-rw-r--r--client/src/app/shared/shared-forms/input-text.component.scss (renamed from client/src/app/shared/shared-forms/input-toggle-hidden.component.scss)11
-rw-r--r--client/src/app/shared/shared-forms/input-text.component.ts (renamed from client/src/app/shared/shared-forms/input-toggle-hidden.component.ts)10
-rw-r--r--client/src/app/shared/shared-forms/input-toggle-hidden.component.html21
-rw-r--r--client/src/app/shared/shared-forms/markdown-textarea.component.scss3
-rw-r--r--client/src/app/shared/shared-forms/peertube-checkbox.component.html8
-rw-r--r--client/src/app/shared/shared-forms/peertube-checkbox.component.scss16
-rw-r--r--client/src/app/shared/shared-forms/reactive-file.component.ts2
-rw-r--r--client/src/app/shared/shared-forms/select/select-channel.component.html2
-rw-r--r--client/src/app/shared/shared-forms/select/select-checkbox-all.component.ts8
-rw-r--r--client/src/app/shared/shared-forms/select/select-checkbox.component.html4
-rw-r--r--client/src/app/shared/shared-forms/select/select-checkbox.component.scss2
-rw-r--r--client/src/app/shared/shared-forms/shared-form.module.ts6
-rw-r--r--client/src/app/shared/shared-forms/timestamp-input.component.scss1
-rw-r--r--client/src/app/shared/shared-icons/global-icon.component.scss5
-rw-r--r--client/src/app/shared/shared-icons/global-icon.component.ts12
-rw-r--r--client/src/app/shared/shared-instance/feature-boolean.component.html5
-rw-r--r--client/src/app/shared/shared-instance/feature-boolean.component.scss4
-rw-r--r--client/src/app/shared/shared-instance/instance-about-accordion.component.html6
-rw-r--r--client/src/app/shared/shared-instance/instance-about-accordion.component.scss27
-rw-r--r--client/src/app/shared/shared-instance/instance-about-accordion.component.ts7
-rw-r--r--client/src/app/shared/shared-instance/instance-features-table.component.html2
-rw-r--r--client/src/app/shared/shared-instance/instance-features-table.component.scss6
-rw-r--r--client/src/app/shared/shared-instance/instance-features-table.component.ts18
-rw-r--r--client/src/app/shared/shared-instance/instance-statistics.component.html18
-rw-r--r--client/src/app/shared/shared-instance/instance-statistics.component.scss29
-rw-r--r--client/src/app/shared/shared-instance/shared-instance.module.ts2
-rw-r--r--client/src/app/shared/shared-main/angular/autofocus.directive.ts4
-rw-r--r--client/src/app/shared/shared-main/angular/from-now.pipe.ts34
-rw-r--r--client/src/app/shared/shared-main/buttons/action-dropdown.component.html2
-rw-r--r--client/src/app/shared/shared-main/buttons/action-dropdown.component.ts2
-rw-r--r--client/src/app/shared/shared-main/buttons/button.component.html14
-rw-r--r--client/src/app/shared/shared-main/buttons/button.component.scss40
-rw-r--r--client/src/app/shared/shared-main/buttons/button.component.ts12
-rw-r--r--client/src/app/shared/shared-main/buttons/delete-button.component.html8
-rw-r--r--client/src/app/shared/shared-main/buttons/delete-button.component.ts7
-rw-r--r--client/src/app/shared/shared-main/buttons/edit-button.component.html8
-rw-r--r--client/src/app/shared/shared-main/buttons/edit-button.component.ts13
-rw-r--r--client/src/app/shared/shared-main/loaders/index.ts1
-rw-r--r--client/src/app/shared/shared-main/loaders/loader.component.html8
-rw-r--r--client/src/app/shared/shared-main/loaders/loader.component.scss45
-rw-r--r--client/src/app/shared/shared-main/loaders/loader.component.ts22
-rw-r--r--client/src/app/shared/shared-main/loaders/small-loader.component.html3
-rw-r--r--client/src/app/shared/shared-main/loaders/small-loader.component.ts11
-rw-r--r--client/src/app/shared/shared-main/misc/channels-setup-message.component.html2
-rw-r--r--client/src/app/shared/shared-main/misc/channels-setup-message.component.scss34
-rw-r--r--client/src/app/shared/shared-main/misc/list-overflow.component.html12
-rw-r--r--client/src/app/shared/shared-main/misc/list-overflow.component.scss3
-rw-r--r--client/src/app/shared/shared-main/misc/list-overflow.component.ts9
-rw-r--r--client/src/app/shared/shared-main/misc/simple-search-input.component.html15
-rw-r--r--client/src/app/shared/shared-main/misc/simple-search-input.component.scss6
-rw-r--r--client/src/app/shared/shared-main/misc/top-menu-dropdown.component.html12
-rw-r--r--client/src/app/shared/shared-main/shared-main.module.ts4
-rw-r--r--client/src/app/shared/shared-main/users/user-quota.component.html2
-rw-r--r--client/src/app/shared/shared-main/users/user-quota.component.scss5
-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.ts11
-rw-r--r--client/src/app/shared/shared-moderation/account-block-badges.component.html8
-rw-r--r--client/src/app/shared/shared-moderation/account-block-badges.component.scss3
-rw-r--r--client/src/app/shared/shared-moderation/account-blocklist.component.html4
-rw-r--r--client/src/app/shared/shared-moderation/report-modals/report.component.html5
-rw-r--r--client/src/app/shared/shared-moderation/report-modals/video-report.component.html44
-rw-r--r--client/src/app/shared/shared-moderation/server-blocklist.component.html6
-rw-r--r--client/src/app/shared/shared-moderation/user-ban-modal.component.scss1
-rw-r--r--client/src/app/shared/shared-moderation/user-ban-modal.component.ts21
-rw-r--r--client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts3
-rw-r--r--client/src/app/shared/shared-moderation/video-block.component.scss1
-rw-r--r--client/src/app/shared/shared-moderation/video-block.component.ts8
-rw-r--r--client/src/app/shared/shared-share-modal/video-share.component.html24
-rw-r--r--client/src/app/shared/shared-share-modal/video-share.component.scss26
-rw-r--r--client/src/app/shared/shared-tables/table-expander-icon.component.ts2
-rw-r--r--client/src/app/shared/shared-tables/video-cell.component.scss7
-rw-r--r--client/src/app/shared/shared-user-settings/user-interface-settings.component.html6
-rw-r--r--client/src/app/shared/shared-user-settings/user-interface-settings.component.scss5
-rw-r--r--client/src/app/shared/shared-user-settings/user-interface-settings.component.ts27
-rw-r--r--client/src/app/shared/shared-user-settings/user-video-settings.component.html8
-rw-r--r--client/src/app/shared/shared-user-settings/user-video-settings.component.scss11
-rw-r--r--client/src/app/shared/shared-user-subscription/remote-subscribe.component.html2
-rw-r--r--client/src/app/shared/shared-user-subscription/subscribe-button.component.html14
-rw-r--r--client/src/app/shared/shared-user-subscription/subscribe-button.component.scss14
-rw-r--r--client/src/app/shared/shared-video-live/live-documentation-link.component.html6
-rw-r--r--client/src/app/shared/shared-video-live/live-stream-information.component.html16
-rw-r--r--client/src/app/shared/shared-video-live/live-stream-information.component.scss9
-rw-r--r--client/src/app/shared/shared-video-miniature/video-download.component.html29
-rw-r--r--client/src/app/shared/shared-video-miniature/video-download.component.scss5
-rw-r--r--client/src/app/shared/shared-video-miniature/video-download.component.ts4
-rw-r--r--client/src/app/shared/shared-video-miniature/video-filters-header.component.html1
-rw-r--r--client/src/app/shared/shared-video-miniature/video-filters-header.component.scss4
-rw-r--r--client/src/app/shared/shared-video-miniature/video-miniature.component.scss4
-rw-r--r--client/src/app/shared/shared-video-miniature/video-miniature.component.ts2
-rw-r--r--client/src/app/shared/shared-video-miniature/videos-list.component.html6
-rw-r--r--client/src/app/shared/shared-video-miniature/videos-list.component.scss8
-rw-r--r--client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.html10
-rw-r--r--client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss45
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.html2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss74
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss2
113 files changed, 573 insertions, 662 deletions
diff --git a/client/src/app/shared/form-validators/user-validators.ts b/client/src/app/shared/form-validators/user-validators.ts
index 6d0dea64e..3262853d8 100644
--- a/client/src/app/shared/form-validators/user-validators.ts
+++ b/client/src/app/shared/form-validators/user-validators.ts
@@ -61,7 +61,7 @@ export const USER_EXISTING_PASSWORD_VALIDATOR: BuildFormValidator = {
61 } 61 }
62} 62}
63 63
64export const USER_PASSWORD_VALIDATOR: BuildFormValidator = { 64export const USER_PASSWORD_VALIDATOR = {
65 VALIDATORS: [ 65 VALIDATORS: [
66 Validators.required, 66 Validators.required,
67 Validators.minLength(6), 67 Validators.minLength(6),
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 986de15ed..b04d46e32 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
@@ -17,9 +17,10 @@
17 </a> 17 </a>
18 18
19 <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reporter:&quot;' + abuse.reporterAccount.displayName + '&quot;' }" 19 <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reporter:&quot;' + abuse.reporterAccount.displayName + '&quot;' }"
20 class="ml-auto muted abuse-details-links" i18n 20 class="ms-auto 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}}
23 <my-global-icon iconName="flag"></my-global-icon>
23 </a> 24 </a>
24 </span> 25 </span>
25 </div> 26 </div>
@@ -37,9 +38,10 @@
37 </a> 38 </a>
38 39
39 <a *ngIf="isAdminView" [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reportee:&quot;' +abuse.flaggedAccount.displayName + '&quot;' }" 40 <a *ngIf="isAdminView" [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reportee:&quot;' +abuse.flaggedAccount.displayName + '&quot;' }"
40 class="ml-auto muted abuse-details-links" i18n 41 class="ms-auto muted abuse-details-links" i18n
41 > 42 >
42 {abuse.countReportsForReportee, plural, =1 {1 report} other {{{ abuse.countReportsForReportee }} reports}}<span class="ml-1 glyphicon glyphicon-flag"></span> 43 {abuse.countReportsForReportee, plural, =1 {1 report} other {{{ abuse.countReportsForReportee }} reports}}
44 <my-global-icon iconName="flag"></my-global-icon>
43 </a> 45 </a>
44 </span> 46 </span>
45 </div> 47 </div>
@@ -53,7 +55,7 @@
53 <div class="mt-3 d-flex"> 55 <div class="mt-3 d-flex">
54 <span class="moderation-expanded-label"> 56 <span class="moderation-expanded-label">
55 <ng-container i18n>Report</ng-container> 57 <ng-container i18n>Report</ng-container>
56 <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': '#' + abuse.id }" class="ml-1 muted">#{{ abuse.id }}</a> 58 <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': '#' + abuse.id }" class="ms-1 muted">#{{ abuse.id }}</a>
57 </span> 59 </span>
58 <span class="moderation-expanded-text" [innerHTML]="abuse.reasonHtml"></span> 60 <span class="moderation-expanded-text" [innerHTML]="abuse.reasonHtml"></span>
59 </div> 61 </div>
diff --git a/client/src/app/shared/shared-abuse-list/abuse-details.component.scss b/client/src/app/shared/shared-abuse-list/abuse-details.component.scss
index 37bf4cc56..bd43ed459 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-details.component.scss
+++ b/client/src/app/shared/shared-abuse-list/abuse-details.component.scss
@@ -15,3 +15,7 @@
15.abuse-details-links { 15.abuse-details-links {
16 @include disable-default-a-behaviour; 16 @include disable-default-a-behaviour;
17} 17}
18
19my-global-icon[iconName=flag] {
20 width: 15px;
21}
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 f0a27c6e2..f79054d03 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
@@ -8,7 +8,7 @@
8> 8>
9 <ng-template pTemplate="caption"> 9 <ng-template pTemplate="caption">
10 <div class="caption"> 10 <div class="caption">
11 <div class="ml-auto"> 11 <div class="ms-auto">
12 <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter> 12 <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter>
13 </div> 13 </div>
14 </div> 14 </div>
@@ -70,7 +70,7 @@
70 </span> 70 </span>
71 71
72 <span name> 72 <span name>
73 <span *ngIf="abuse.video.blacklisted" i18n-title title="The video was blocked" class="glyphicon glyphicon-ban-circle"></span> 73 <my-global-icon *ngIf="abuse.video.blacklisted" iconName="no" i18n-title title="The video was blocked"></my-global-icon>
74 </span> 74 </span>
75 </my-video-cell> 75 </my-video-cell>
76 </td> 76 </td>
@@ -80,7 +80,7 @@
80 <div class="table-video-text"> 80 <div class="table-video-text">
81 <div> 81 <div>
82 {{ abuse.video.name }} 82 {{ abuse.video.name }}
83 <span class="glyphicon glyphicon-trash"></span> 83 <my-global-icon iconName="delete"></my-global-icon>
84 </div> 84 </div>
85 <div i18n>by {{ abuse.video.channel?.displayName }} on {{ abuse.video.channel?.host }} </div> 85 <div i18n>by {{ abuse.video.channel?.displayName }} on {{ abuse.video.channel?.host }} </div>
86 </div> 86 </div>
@@ -116,8 +116,8 @@
116 <td class="c-hand" [pRowToggler]="abuse">{{ abuse.createdAt | date: 'short' }}</td> 116 <td class="c-hand" [pRowToggler]="abuse">{{ abuse.createdAt | date: 'short' }}</td>
117 117
118 <td class="c-hand abuse-states" [pRowToggler]="abuse"> 118 <td class="c-hand abuse-states" [pRowToggler]="abuse">
119 <span *ngIf="isAbuseAccepted(abuse)" [title]="abuse.state.label" class="glyphicon glyphicon-ok"></span> 119 <my-global-icon *ngIf="isAbuseAccepted(abuse)" [title]="abuse.state.label" iconName="tick"></my-global-icon>
120 <span *ngIf="isAbuseRejected(abuse)" [title]="abuse.state.label" class="glyphicon glyphicon-remove"></span> 120 <my-global-icon *ngIf="isAbuseRejected(abuse)" [title]="abuse.state.label" iconName="cross"></my-global-icon>
121 </td> 121 </td>
122 122
123 <td class="c-hand abuse-messages" (click)="openAbuseMessagesModal(abuse)"> 123 <td class="c-hand abuse-messages" (click)="openAbuseMessagesModal(abuse)">
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.scss b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.scss
index 2d8acae58..4852f2e8b 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.scss
+++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.scss
@@ -7,10 +7,6 @@
7 color: var(--greyForegroundColor); 7 color: var(--greyForegroundColor);
8} 8}
9 9
10.abuse-states .glyphicon-comment {
11 @include margin-left(0.5rem);
12}
13
14.abuse-messages { 10.abuse-messages {
15 my-global-icon { 11 my-global-icon {
16 @include margin-left(3px); 12 @include margin-left(3px);
@@ -20,3 +16,10 @@
20 top: -2px; 16 top: -2px;
21 } 17 }
22} 18}
19
20.table-video-text my-global-icon,
21my-video-cell my-global-icon {
22 width: 15px;
23 position: relative;
24 top: -2px;
25}
diff --git a/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.scss b/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.scss
index d77e44a9d..3b43a4a4d 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.scss
+++ b/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.scss
@@ -25,7 +25,6 @@ textarea {
25 25
26.no-messages { 26.no-messages {
27 display: flex; 27 display: flex;
28 font-size: 15px;
29 justify-content: center; 28 justify-content: center;
30} 29}
31 30
@@ -45,10 +44,6 @@ textarea {
45 color: var(--mainForegroundColor); 44 color: var(--mainForegroundColor);
46 background-color: var(--greyBackgroundColor); 45 background-color: var(--greyBackgroundColor);
47 46
48 .content {
49 font-size: 15px;
50 }
51
52 .date { 47 .date {
53 font-size: 13px; 48 font-size: 13px;
54 color: var(--greyForegroundColor); 49 color: var(--greyForegroundColor);
diff --git a/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html
index e9c5fadcf..b63bf1f92 100644
--- a/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html
+++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html
@@ -6,7 +6,7 @@
6 6
7 <div *ngIf="editable && !hasAvatar()" class="actor-img-edit-button" [ngbTooltip]="avatarFormat" placement="right" container="body"> 7 <div *ngIf="editable && !hasAvatar()" class="actor-img-edit-button" [ngbTooltip]="avatarFormat" placement="right" container="body">
8 <my-global-icon iconName="upload"></my-global-icon> 8 <my-global-icon iconName="upload"></my-global-icon>
9 <label class="sr-only" for="avatarfile" i18n>Upload a new avatar</label> 9 <label class="visually-hidden" for="avatarfile" i18n>Upload a new avatar</label>
10 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/> 10 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
11 </div> 11 </div>
12 12
@@ -15,7 +15,7 @@
15 #avatarPopover="ngbPopover" [ngbPopover]="avatarEditContent" popoverClass="popover-image-info" autoClose="outside" placement="right" 15 #avatarPopover="ngbPopover" [ngbPopover]="avatarEditContent" popoverClass="popover-image-info" autoClose="outside" placement="right"
16 > 16 >
17 <my-global-icon iconName="edit"></my-global-icon> 17 <my-global-icon iconName="edit"></my-global-icon>
18 <label class="sr-only" for="avatarMenu" i18n>Change your avatar</label> 18 <label class="visually-hidden" for="avatarMenu" i18n>Change your avatar</label>
19 </div> 19 </div>
20 20
21 </div> 21 </div>
diff --git a/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss
index 923f0029b..fd8cd7ffc 100644
--- a/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss
+++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss
@@ -3,37 +3,36 @@
3 3
4.actor { 4.actor {
5 display: flex; 5 display: flex;
6}
6 7
7 my-actor-avatar { 8my-actor-avatar {
8 @include margin-right(15px); 9 @include margin-right(15px);
9 } 10}
10 11
11 .actor-info { 12.actor-info {
12 display: inline-flex; 13 display: inline-flex;
13 flex-direction: column; 14 flex-direction: column;
15}
14 16
15 .actor-info-display-name { 17.actor-info-display-name {
16 @include peertube-word-wrap; 18 @include peertube-word-wrap;
17 19
18 font-size: 20px; 20 font-size: 20px;
19 font-weight: $font-bold; 21 font-weight: $font-bold;
20 22
21 @media screen and (max-width: $small-view) { 23 @media screen and (max-width: $small-view) {
22 font-size: 16px; 24 font-size: 16px;
23 } 25 }
24 } 26}
25 27
26 .actor-info-username { 28.actor-info-username {
27 position: relative; 29 position: relative;
28 font-size: 14px; 30 font-size: 14px;
29 color: pvar(--greyForegroundColor); 31 color: pvar(--greyForegroundColor);
30 } 32}
31 33
32 .actor-info-followers { 34.actor-info-followers {
33 font-size: 15px; 35 padding-bottom: .5rem;
34 padding-bottom: .5rem;
35 }
36 }
37} 36}
38 37
39.actor-img-edit-container { 38.actor-img-edit-container {
@@ -46,3 +45,7 @@
46 right: 45px; 45 right: 45px;
47 border-radius: 50%; 46 border-radius: 50%;
48} 47}
48
49.dropdown-item {
50 @include dropdown-with-icon-item;
51}
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
index c285b6cc3..ba025da4d 100644
--- a/client/src/app/shared/shared-actor-image/actor-avatar.component.html
+++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.html
@@ -1,7 +1,7 @@
1<ng-template #img> 1<ng-template #img>
2 <img *ngIf="previewImage || avatarUrl || !initial" [class]="getClass('avatar')" [src]="previewImage || avatarUrl || defaultAvatarUrl" [alt]="alt" /> 2 <img *ngIf="previewImage || avatarUrl || !initial" [class]="classes" [src]="previewImage || avatarUrl || defaultAvatarUrl" [alt]="alt" />
3 3
4 <div *ngIf="!avatarUrl && initial" [ngClass]="getClass('initial')"> 4 <div *ngIf="!avatarUrl && initial" [ngClass]="classes">
5 <span>{{ initial }}</span> 5 <span>{{ initial }}</span>
6 </div> 6 </div>
7</ng-template> 7</ng-template>
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
index 600984aa2..fa4f1e51f 100644
--- a/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
+++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
@@ -1,4 +1,4 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input, OnChanges } from '@angular/core'
2import { VideoChannel } from '../shared-main' 2import { VideoChannel } from '../shared-main'
3import { Account } from '../shared-main/account/account.model' 3import { Account } from '../shared-main/account/account.model'
4 4
@@ -15,7 +15,7 @@ export type ActorAvatarSize = '18' | '25' | '28' | '32' | '34' | '35' | '36' | '
15 styleUrls: [ './actor-avatar.component.scss' ], 15 styleUrls: [ './actor-avatar.component.scss' ],
16 templateUrl: './actor-avatar.component.html' 16 templateUrl: './actor-avatar.component.html'
17}) 17})
18export class ActorAvatarComponent { 18export class ActorAvatarComponent implements OnChanges {
19 private _title: string 19 private _title: string
20 20
21 @Input() account: ActorInput 21 @Input() account: ActorInput
@@ -42,6 +42,8 @@ export class ActorAvatarComponent {
42 return '' 42 return ''
43 } 43 }
44 44
45 classes: string[] = []
46
45 get alt () { 47 get alt () {
46 if (this.account) return $localize`Account avatar` 48 if (this.account) return $localize`Account avatar`
47 if (this.channel) return $localize`Channel avatar` 49 if (this.channel) return $localize`Channel avatar`
@@ -68,20 +70,18 @@ export class ActorAvatarComponent {
68 return name.slice(0, 1) 70 return name.slice(0, 1)
69 } 71 }
70 72
71 getClass (type: 'avatar' | 'initial') { 73 ngOnChanges () {
72 const base = [ 'avatar' ] 74 this.classes = [ 'avatar' ]
73 75
74 if (this.size) base.push(`avatar-${this.size}`) 76 if (this.size) this.classes.push(`avatar-${this.size}`)
75 77
76 if (this.channel) base.push('channel') 78 if (this.channel) this.classes.push('channel')
77 else base.push('account') 79 else this.classes.push('account')
78 80
79 if (type === 'initial' && this.initial) { 81 if (!this.avatarUrl && this.initial) {
80 base.push('initial') 82 this.classes.push('initial')
81 base.push(this.getColorTheme()) 83 this.classes.push(this.getColorTheme())
82 } 84 }
83
84 return base
85 } 85 }
86 86
87 hasActor () { 87 hasActor () {
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts
index 0e4d5fb12..7d3498d4c 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts
@@ -7,7 +7,7 @@ import { MiniatureDisplayOptions } from '../../shared-video-miniature'
7import { CustomMarkupComponent } from './shared' 7import { CustomMarkupComponent } from './shared'
8 8
9/* 9/*
10 * Markup component list videos depending on criterias 10 * Markup component list videos depending on criteria
11*/ 11*/
12 12
13@Component({ 13@Component({
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
index 7031cb53b..e4f318385 100644
--- a/client/src/app/shared/shared-forms/advanced-input-filter.component.html
+++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.html
@@ -1,6 +1,7 @@
1<div class="input-group has-feedback has-clear"> 1<div class="input-group has-clear" ngbDropdown placement="bottom-left auto" container="body">
2 <div *ngIf="hasFilters()" class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body"> 2
3 <div class="input-group-text" ngbDropdownToggle> 3 <ng-container *ngIf="hasFilters()">
4 <div class="input-group-text c-hand" ngbDropdownToggle>
4 <span class="caret" aria-haspopup="menu" role="button"></span> 5 <span class="caret" aria-haspopup="menu" role="button"></span>
5 </div> 6 </div>
6 7
@@ -15,14 +16,14 @@
15 </button> 16 </button>
16 </ng-container> 17 </ng-container>
17 </div> 18 </div>
18 </div> 19 </ng-container>
19 20
20 <input 21 <input
21 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..." 22 type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
23 class="last-in-group"
22 [(ngModel)]="searchValue" 24 [(ngModel)]="searchValue"
23 (keyup)="onInputSearch($event)" 25 (keyup)="onInputSearch($event)"
24 > 26 >
25 27
26 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="onResetTableFilter()"></a> 28 <my-global-icon iconName="cross" role="button" class="form-control-clear" title="Clear filter" i18n-title (click)="onResetTableFilter()"></my-global-icon>
27 <span class="sr-only" i18n>Clear filters</span>
28</div> 29</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
index ee1b3b508..ed85385fd 100644
--- a/client/src/app/shared/shared-forms/advanced-input-filter.component.scss
+++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.scss
@@ -23,7 +23,6 @@ my-global-icon {
23 width: 18px; 23 width: 18px;
24 height: 18px; 24 height: 18px;
25 25
26
27 &.no-visible { 26 &.no-visible {
28 @include margin-right($size + $margin); 27 @include margin-right($size + $margin);
29 28
diff --git a/client/src/app/shared/shared-forms/dynamic-form-field.component.html b/client/src/app/shared/shared-forms/dynamic-form-field.component.html
index 2ef61ecfc..2ec436ac6 100644
--- a/client/src/app/shared/shared-forms/dynamic-form-field.component.html
+++ b/client/src/app/shared/shared-forms/dynamic-form-field.component.html
@@ -18,7 +18,7 @@
18 </select> 18 </select>
19 </div> 19 </div>
20 20
21 <my-input-toggle-hidden *ngIf="setting.type === 'input-password'" [formControlName]="setting.name" [inputId]="setting.name"></my-input-toggle-hidden> 21 <my-input-text *ngIf="setting.type === 'input-password'" [formControlName]="setting.name" [inputId]="setting.name"></my-input-text>
22 22
23 <textarea *ngIf="setting.type === 'input-textarea'" type="text" [id]="setting.name" [formControlName]="setting.name"></textarea> 23 <textarea *ngIf="setting.type === 'input-textarea'" type="text" [id]="setting.name" [formControlName]="setting.name"></textarea>
24 24
diff --git a/client/src/app/shared/shared-forms/index.ts b/client/src/app/shared/shared-forms/index.ts
index 727416a40..495785e7b 100644
--- a/client/src/app/shared/shared-forms/index.ts
+++ b/client/src/app/shared/shared-forms/index.ts
@@ -3,7 +3,7 @@ export * from './form-reactive'
3export * from './form-validator.service' 3export * from './form-validator.service'
4export * from './form-validator.service' 4export * from './form-validator.service'
5export * from './input-switch.component' 5export * from './input-switch.component'
6export * from './input-toggle-hidden.component' 6export * from './input-text.component'
7export * from './markdown-textarea.component' 7export * from './markdown-textarea.component'
8export * from './peertube-checkbox.component' 8export * from './peertube-checkbox.component'
9export * from './preview-upload.component' 9export * from './preview-upload.component'
diff --git a/client/src/app/shared/shared-forms/input-switch.component.html b/client/src/app/shared/shared-forms/input-switch.component.html
index ce1dee470..9a466055a 100644
--- a/client/src/app/shared/shared-forms/input-switch.component.html
+++ b/client/src/app/shared/shared-forms/input-switch.component.html
@@ -1,4 +1,4 @@
1<div (click)="update()"> 1<div (click)="update()">
2 <input type="checkbox" [checked]="checked"/> 2 <input type="checkbox" [checked]="checked"/>
3 <label class="ml-auto">Toggle</label> 3 <label class="ms-auto">Toggle</label>
4</div> 4</div>
diff --git a/client/src/app/shared/shared-forms/input-text.component.html b/client/src/app/shared/shared-forms/input-text.component.html
new file mode 100644
index 000000000..f890c4f02
--- /dev/null
+++ b/client/src/app/shared/shared-forms/input-text.component.html
@@ -0,0 +1,20 @@
1<div class="input-group">
2 <input
3 [id]="inputId" [autocomplete]="autocomplete" [value]="value" [placeholder]="placeholder" [tabindex]="tabindex"
4 [(ngModel)]="value" (ngModelChange)="update()" [readonly]="readonly"
5 #input (click)="input.select()" (input)="update()" (change)="update()" [type]="inputType" class="form-control"
6 />
7
8 <button *ngIf="withToggle" (click)="toggle()" type="button" class="btn btn-outline-secondary" [title]="toggleTitle">
9 <my-global-icon *ngIf="show" iconName="eye-open"></my-global-icon>
10 <my-global-icon *ngIf="!show" iconName="eye-close"></my-global-icon>
11 </button>
12
13 <button
14 *ngIf="withCopy" [cdkCopyToClipboard]="input.value" (click)="activateCopiedMessage()" type="button"
15 class="btn btn-outline-secondary text-uppercase" i18n-title title="Copy"
16 >
17 <my-global-icon iconName="copy"></my-global-icon>
18 <span class="copy-text">Copy</span>
19 </button>
20</div>
diff --git a/client/src/app/shared/shared-forms/input-toggle-hidden.component.scss b/client/src/app/shared/shared-forms/input-text.component.scss
index ef4236ebc..ae8bf5fec 100644
--- a/client/src/app/shared/shared-forms/input-toggle-hidden.component.scss
+++ b/client/src/app/shared/shared-forms/input-text.component.scss
@@ -5,11 +5,14 @@ input {
5 @include peertube-input-text(auto); 5 @include peertube-input-text(auto);
6 @include padding-left(15px !important); 6 @include padding-left(15px !important);
7 @include padding-right(15px !important); 7 @include padding-right(15px !important);
8}
8 9
9 // set again properties of peertube-input-text that are overriden by .input-group 10.btn {
10 font-size: 15px !important; 11 @include button-with-icon(18px);
11} 12}
12 13
13.eye-button { 14.copy-text {
14 line-height: 1 !important; 15 font-size: 14px;
16 margin-left: 5px;
17 vertical-align: top;
15} 18}
diff --git a/client/src/app/shared/shared-forms/input-toggle-hidden.component.ts b/client/src/app/shared/shared-forms/input-text.component.ts
index e03353fe1..ed4637c17 100644
--- a/client/src/app/shared/shared-forms/input-toggle-hidden.component.ts
+++ b/client/src/app/shared/shared-forms/input-text.component.ts
@@ -3,18 +3,18 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
3import { Notifier } from '@app/core' 3import { Notifier } from '@app/core'
4 4
5@Component({ 5@Component({
6 selector: 'my-input-toggle-hidden', 6 selector: 'my-input-text',
7 templateUrl: './input-toggle-hidden.component.html', 7 templateUrl: './input-text.component.html',
8 styleUrls: [ './input-toggle-hidden.component.scss' ], 8 styleUrls: [ './input-text.component.scss' ],
9 providers: [ 9 providers: [
10 { 10 {
11 provide: NG_VALUE_ACCESSOR, 11 provide: NG_VALUE_ACCESSOR,
12 useExisting: forwardRef(() => InputToggleHiddenComponent), 12 useExisting: forwardRef(() => InputTextComponent),
13 multi: true 13 multi: true
14 } 14 }
15 ] 15 ]
16}) 16})
17export class InputToggleHiddenComponent implements ControlValueAccessor { 17export class InputTextComponent implements ControlValueAccessor {
18 @Input() inputId = Math.random().toString(11).slice(2, 8) // id cannot be left empty or undefined 18 @Input() inputId = Math.random().toString(11).slice(2, 8) // id cannot be left empty or undefined
19 @Input() value = '' 19 @Input() value = ''
20 @Input() autocomplete = 'off' 20 @Input() autocomplete = 'off'
diff --git a/client/src/app/shared/shared-forms/input-toggle-hidden.component.html b/client/src/app/shared/shared-forms/input-toggle-hidden.component.html
deleted file mode 100644
index dfe646d2f..000000000
--- a/client/src/app/shared/shared-forms/input-toggle-hidden.component.html
+++ /dev/null
@@ -1,21 +0,0 @@
1<div class="input-group input-group-sm">
2 <input
3 [id]="inputId" [autocomplete]="autocomplete" [value]="value" [placeholder]="placeholder" [tabindex]="tabindex"
4 [(ngModel)]="value" (ngModelChange)="update()" [readonly]="readonly"
5 #input (click)="input.select()" (input)="update()" (change)="update()" [type]="inputType" class="form-control"
6 />
7
8 <div *ngIf="withToggle || withCopy" class="input-group-append">
9 <button *ngIf="withToggle" (click)="toggle()" type="button" class="btn btn-outline-secondary eye-button" [title]="toggleTitle">
10 <span class="glyphicon glyphicon-eye-{{ show ? 'open' : 'close' }}"></span>
11 </button>
12
13 <button
14 *ngIf="withCopy" [cdkCopyToClipboard]="input.value" (click)="activateCopiedMessage()" type="button"
15 class="btn btn-outline-secondary text-uppercase" i18n-title title="Copy"
16 >
17 <span class="glyphicon glyphicon-duplicate"></span>
18 Copy
19 </button>
20 </div>
21</div>
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 5939bb999..116630d8a 100644
--- a/client/src/app/shared/shared-forms/markdown-textarea.component.scss
+++ b/client/src/app/shared/shared-forms/markdown-textarea.component.scss
@@ -49,7 +49,7 @@ $input-border-radius: 3px;
49 49
50 svg { 50 svg {
51 stroke: var(--mainForegroundColor); 51 stroke: var(--mainForegroundColor);
52 opacity: 0.6; 52 opacity: 0.7;
53 } 53 }
54 54
55 &:hover, 55 &:hover,
@@ -102,7 +102,6 @@ $input-border-radius: 3px;
102 min-height: 75px; 102 min-height: 75px;
103 padding: $base-padding; 103 padding: $base-padding;
104 overflow-y: auto; 104 overflow-y: auto;
105 font-size: 15px;
106 word-wrap: break-word; 105 word-wrap: break-word;
107} 106}
108 107
diff --git a/client/src/app/shared/shared-forms/peertube-checkbox.component.html b/client/src/app/shared/shared-forms/peertube-checkbox.component.html
index c679e1403..cd06e75ba 100644
--- a/client/src/app/shared/shared-forms/peertube-checkbox.component.html
+++ b/client/src/app/shared/shared-forms/peertube-checkbox.component.html
@@ -1,6 +1,6 @@
1<div class="root flex-column"> 1<div class="root flex-column">
2 <div class="d-flex"> 2 <div class="d-flex">
3 <label class="form-group-checkbox"> 3 <label>
4 <input 4 <input
5 type="checkbox" 5 type="checkbox"
6 [(ngModel)]="checked" 6 [(ngModel)]="checked"
@@ -33,10 +33,10 @@
33 <div *ngIf="recommended" class="recommended" i18n>Recommended</div> 33 <div *ngIf="recommended" class="recommended" i18n>Recommended</div>
34 </div> 34 </div>
35 35
36 <div class="ml-4 d-flex flex-column"> 36 <div class="ms-4 d-flex flex-column">
37 <small class="wrapper mt-2 muted"> 37 <div class="wrapper form-group-description">
38 <ng-content select="description"></ng-content> 38 <ng-content select="description"></ng-content>
39 </small> 39 </div>
40 40
41 <span class="wrapper mt-3"> 41 <span class="wrapper mt-3">
42 <ng-content select="extra"></ng-content> 42 <ng-content select="extra"></ng-content>
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 4e384e7e0..ac9127677 100644
--- a/client/src/app/shared/shared-forms/peertube-checkbox.component.scss
+++ b/client/src/app/shared/shared-forms/peertube-checkbox.component.scss
@@ -4,14 +4,9 @@
4.root { 4.root {
5 display: flex; 5 display: flex;
6 6
7 .form-group-checkbox { 7 label {
8 display: flex; 8 display: flex;
9 align-items: center; 9 font-size: $form-input-font-size;
10
11 .label-text {
12 font-weight: $font-regular;
13 margin: 0;
14 }
15 10
16 input { 11 input {
17 @include peertube-checkbox(1px); 12 @include peertube-checkbox(1px);
@@ -23,12 +18,11 @@
23 } 18 }
24 19
25 my-help { 20 my-help {
26 position: relative; 21 line-height: 17px;
27 top: 2px;
28 } 22 }
29 23
30 small { 24 .form-group-description {
31 font-size: 90%; 25 margin-top: 2px;
32 } 26 }
33 27
34 .wrapper:empty { 28 .wrapper:empty {
diff --git a/client/src/app/shared/shared-forms/reactive-file.component.ts b/client/src/app/shared/shared-forms/reactive-file.component.ts
index 50b7d4c3e..48055a51c 100644
--- a/client/src/app/shared/shared-forms/reactive-file.component.ts
+++ b/client/src/app/shared/shared-forms/reactive-file.component.ts
@@ -57,7 +57,7 @@ export class ReactiveFileComponent implements OnInit, ControlValueAccessor {
57 57
58 const extension = '.' + file.name.split('.').pop() 58 const extension = '.' + file.name.split('.').pop()
59 if (this.extensions.includes(extension.toLowerCase()) === false) { 59 if (this.extensions.includes(extension.toLowerCase()) === false) {
60 const message = $localize`PeerTube cannot handle this kind of file. Accepted extensions are ${this.allowedExtensionsMessage}}.` 60 const message = $localize`PeerTube cannot handle this kind of file. Accepted extensions are ${this.allowedExtensionsMessage}.`
61 this.notifier.error(message) 61 this.notifier.error(message)
62 62
63 return 63 return
diff --git a/client/src/app/shared/shared-forms/select/select-channel.component.html b/client/src/app/shared/shared-forms/select/select-channel.component.html
index f83f17a16..b49fd36fa 100644
--- a/client/src/app/shared/shared-forms/select/select-channel.component.html
+++ b/client/src/app/shared/shared-forms/select/select-channel.component.html
@@ -7,7 +7,7 @@
7 [searchable]="searchable" 7 [searchable]="searchable"
8> 8>
9 <ng-option *ngFor="let channel of channels" [value]="channel.id"> 9 <ng-option *ngFor="let channel of channels" [value]="channel.id">
10 <img alt="" class="avatar mr-1" [src]="channel.avatarPath" /> 10 <img alt="" class="avatar me-1" [src]="channel.avatarPath" />
11 {{ channel.label }} 11 {{ channel.label }}
12 </ng-option> 12 </ng-option>
13</ng-select> 13</ng-select>
diff --git a/client/src/app/shared/shared-forms/select/select-checkbox-all.component.ts b/client/src/app/shared/shared-forms/select/select-checkbox-all.component.ts
index ebf7b77a6..2c3226f68 100644
--- a/client/src/app/shared/shared-forms/select/select-checkbox-all.component.ts
+++ b/client/src/app/shared/shared-forms/select/select-checkbox-all.component.ts
@@ -1,6 +1,7 @@
1import { Component, forwardRef, Input } from '@angular/core' 1import { Component, forwardRef, Input } from '@angular/core'
2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' 2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
3import { Notifier } from '@app/core' 3import { Notifier } from '@app/core'
4import { prepareIcu } from '@app/helpers'
4import { SelectOptionsItem } from '../../../../types/select-options-item.model' 5import { SelectOptionsItem } from '../../../../types/select-options-item.model'
5import { ItemSelectCheckboxValue } from './select-checkbox.component' 6import { ItemSelectCheckboxValue } from './select-checkbox.component'
6 7
@@ -78,7 +79,12 @@ export class SelectCheckboxAllComponent implements ControlValueAccessor {
78 if (!outputItems) return true 79 if (!outputItems) return true
79 80
80 if (outputItems.length >= this.maxItems) { 81 if (outputItems.length >= this.maxItems) {
81 this.notifier.error($localize`You can't select more than ${this.maxItems} items`) 82 this.notifier.error(
83 prepareIcu($localize`You can't select more than {maxItems, plural, =1 {1 item} other {{maxItems} items}}`)(
84 { maxItems: this.maxItems },
85 $localize`You can't select more than ${this.maxItems} items`
86 )
87 )
82 88
83 return false 89 return false
84 } 90 }
diff --git a/client/src/app/shared/shared-forms/select/select-checkbox.component.html b/client/src/app/shared/shared-forms/select/select-checkbox.component.html
index 03db2875b..2799ccdcc 100644
--- a/client/src/app/shared/shared-forms/select/select-checkbox.component.html
+++ b/client/src/app/shared/shared-forms/select/select-checkbox.component.html
@@ -22,7 +22,7 @@
22> 22>
23 23
24 <ng-template ng-optgroup-tmp let-item="item" let-item$="item$" let-index="index"> 24 <ng-template ng-optgroup-tmp let-item="item" let-item$="item$" let-index="index">
25 <div class="form-group-checkbox"> 25 <div class="checkbox-wrapper">
26 <input id="item-{{index}}" type="checkbox" [ngModel]="item$.selected"/> 26 <input id="item-{{index}}" type="checkbox" [ngModel]="item$.selected"/>
27 <span role="checkbox" [attr.aria-checked]="item$.selected"></span> 27 <span role="checkbox" [attr.aria-checked]="item$.selected"></span>
28 <span>{{ item.group }}</span> 28 <span>{{ item.group }}</span>
@@ -30,7 +30,7 @@
30 </ng-template> 30 </ng-template>
31 31
32 <ng-template ng-option-tmp let-item="item" let-item$="item$" let-index="index"> 32 <ng-template ng-option-tmp let-item="item" let-item$="item$" let-index="index">
33 <div class="form-group-checkbox"> 33 <div class="checkbox-wrapper">
34 <input id="item-{{index}}" type="checkbox" [ngModel]="item$.selected"/> 34 <input id="item-{{index}}" type="checkbox" [ngModel]="item$.selected"/>
35 <span role="checkbox" [attr.aria-checked]="item$.selected"></span> 35 <span role="checkbox" [attr.aria-checked]="item$.selected"></span>
36 <span>{{ item.label }}</span> 36 <span>{{ item.label }}</span>
diff --git a/client/src/app/shared/shared-forms/select/select-checkbox.component.scss b/client/src/app/shared/shared-forms/select/select-checkbox.component.scss
index d47c4f9da..892f22dff 100644
--- a/client/src/app/shared/shared-forms/select/select-checkbox.component.scss
+++ b/client/src/app/shared/shared-forms/select/select-checkbox.component.scss
@@ -7,7 +7,7 @@ ng-select ::ng-deep {
7 align-items: center; 7 align-items: center;
8 } 8 }
9 9
10 .form-group-checkbox { 10 .checkbox-wrapper {
11 display: flex; 11 display: flex;
12 align-items: center; 12 align-items: center;
13 13
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 60c2f66ae..81f076db6 100644
--- a/client/src/app/shared/shared-forms/shared-form.module.ts
+++ b/client/src/app/shared/shared-forms/shared-form.module.ts
@@ -9,7 +9,7 @@ import { AdvancedInputFilterComponent } from './advanced-input-filter.component'
9import { DynamicFormFieldComponent } from './dynamic-form-field.component' 9import { DynamicFormFieldComponent } from './dynamic-form-field.component'
10import { FormValidatorService } from './form-validator.service' 10import { FormValidatorService } from './form-validator.service'
11import { InputSwitchComponent } from './input-switch.component' 11import { InputSwitchComponent } from './input-switch.component'
12import { InputToggleHiddenComponent } from './input-toggle-hidden.component' 12import { InputTextComponent } from './input-text.component'
13import { MarkdownTextareaComponent } from './markdown-textarea.component' 13import { MarkdownTextareaComponent } from './markdown-textarea.component'
14import { PeertubeCheckboxComponent } from './peertube-checkbox.component' 14import { PeertubeCheckboxComponent } from './peertube-checkbox.component'
15import { PreviewUploadComponent } from './preview-upload.component' 15import { PreviewUploadComponent } from './preview-upload.component'
@@ -40,7 +40,7 @@ import { TimestampInputComponent } from './timestamp-input.component'
40 ], 40 ],
41 41
42 declarations: [ 42 declarations: [
43 InputToggleHiddenComponent, 43 InputTextComponent,
44 MarkdownTextareaComponent, 44 MarkdownTextareaComponent,
45 PeertubeCheckboxComponent, 45 PeertubeCheckboxComponent,
46 PreviewUploadComponent, 46 PreviewUploadComponent,
@@ -71,7 +71,7 @@ import { TimestampInputComponent } from './timestamp-input.component'
71 InputMaskModule, 71 InputMaskModule,
72 NgSelectModule, 72 NgSelectModule,
73 73
74 InputToggleHiddenComponent, 74 InputTextComponent,
75 MarkdownTextareaComponent, 75 MarkdownTextareaComponent,
76 PeertubeCheckboxComponent, 76 PeertubeCheckboxComponent,
77 PreviewUploadComponent, 77 PreviewUploadComponent,
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 27d6fa173..e69a06947 100644
--- a/client/src/app/shared/shared-forms/timestamp-input.component.scss
+++ b/client/src/app/shared/shared-forms/timestamp-input.component.scss
@@ -4,7 +4,6 @@
4p-inputmask { 4p-inputmask {
5 ::ng-deep input { 5 ::ng-deep input {
6 width: 80px; 6 width: 80px;
7 font-size: 15px;
8 7
9 &:focus-within, 8 &:focus-within,
10 &:focus { 9 &:focus {
diff --git a/client/src/app/shared/shared-icons/global-icon.component.scss b/client/src/app/shared/shared-icons/global-icon.component.scss
index 6795d6628..10180829f 100644
--- a/client/src/app/shared/shared-icons/global-icon.component.scss
+++ b/client/src/app/shared/shared-icons/global-icon.component.scss
@@ -3,4 +3,9 @@
3 width: inherit; 3 width: inherit;
4 height: inherit; 4 height: inherit;
5 } 5 }
6
7 .feather-flag {
8 margin-left: -1px;
9 }
6} 10}
11
diff --git a/client/src/app/shared/shared-icons/global-icon.component.ts b/client/src/app/shared/shared-icons/global-icon.component.ts
index ba23edde0..55eb45a75 100644
--- a/client/src/app/shared/shared-icons/global-icon.component.ts
+++ b/client/src/app/shared/shared-icons/global-icon.component.ts
@@ -21,6 +21,7 @@ const icons = {
21 local: require('!!raw-loader?!../../../assets/images/misc/local.svg').default, 21 local: require('!!raw-loader?!../../../assets/images/misc/local.svg').default,
22 22
23 // feather icons 23 // feather icons
24 copy: require('!!raw-loader?!../../../assets/images/feather/copy.svg').default,
24 flag: require('!!raw-loader?!../../../assets/images/feather/flag.svg').default, 25 flag: require('!!raw-loader?!../../../assets/images/feather/flag.svg').default,
25 playlists: require('!!raw-loader?!../../../assets/images/feather/list.svg').default, 26 playlists: require('!!raw-loader?!../../../assets/images/feather/list.svg').default,
26 syndication: require('!!raw-loader?!../../../assets/images/feather/syndication.svg').default, 27 syndication: require('!!raw-loader?!../../../assets/images/feather/syndication.svg').default,
@@ -28,7 +29,6 @@ const icons = {
28 alert: require('!!raw-loader?!../../../assets/images/feather/alert.svg').default, 29 alert: require('!!raw-loader?!../../../assets/images/feather/alert.svg').default,
29 globe: require('!!raw-loader?!../../../assets/images/feather/globe.svg').default, 30 globe: require('!!raw-loader?!../../../assets/images/feather/globe.svg').default,
30 home: require('!!raw-loader?!../../../assets/images/feather/home.svg').default, 31 home: require('!!raw-loader?!../../../assets/images/feather/home.svg').default,
31 'recently-added': require('!!raw-loader?!../../../assets/images/feather/recently-added.svg').default,
32 trending: require('!!raw-loader?!../../../assets/images/feather/trending.svg').default, 32 trending: require('!!raw-loader?!../../../assets/images/feather/trending.svg').default,
33 search: require('!!raw-loader?!../../../assets/images/feather/search.svg').default, 33 search: require('!!raw-loader?!../../../assets/images/feather/search.svg').default,
34 upload: require('!!raw-loader?!../../../assets/images/feather/upload.svg').default, 34 upload: require('!!raw-loader?!../../../assets/images/feather/upload.svg').default,
@@ -62,8 +62,9 @@ const icons = {
62 'exit-fullscreen': require('!!raw-loader?!../../../assets/images/feather/minimize.svg').default, 62 'exit-fullscreen': require('!!raw-loader?!../../../assets/images/feather/minimize.svg').default,
63 film: require('!!raw-loader?!../../../assets/images/feather/film.svg').default, 63 film: require('!!raw-loader?!../../../assets/images/feather/film.svg').default,
64 edit: require('!!raw-loader?!../../../assets/images/feather/edit-2.svg').default, 64 edit: require('!!raw-loader?!../../../assets/images/feather/edit-2.svg').default,
65 sensitive: require('!!raw-loader?!../../../assets/images/feather/eye.svg').default, 65 'external-link': require('!!raw-loader?!../../../assets/images/feather/external-link.svg').default,
66 unsensitive: require('!!raw-loader?!../../../assets/images/feather/eye-off.svg').default, 66 'eye-open': require('!!raw-loader?!../../../assets/images/feather/eye.svg').default,
67 'eye-close': require('!!raw-loader?!../../../assets/images/feather/eye-off.svg').default,
67 refresh: require('!!raw-loader?!../../../assets/images/feather/refresh-cw.svg').default, 68 refresh: require('!!raw-loader?!../../../assets/images/feather/refresh-cw.svg').default,
68 command: require('!!raw-loader?!../../../assets/images/feather/command.svg').default, 69 command: require('!!raw-loader?!../../../assets/images/feather/command.svg').default,
69 go: require('!!raw-loader?!../../../assets/images/feather/arrow-up-right.svg').default, 70 go: require('!!raw-loader?!../../../assets/images/feather/arrow-up-right.svg').default,
@@ -89,6 +90,7 @@ export type GlobalIconName = keyof typeof icons
89}) 90})
90export class GlobalIconComponent implements OnInit { 91export class GlobalIconComponent implements OnInit {
91 @Input() iconName: GlobalIconName 92 @Input() iconName: GlobalIconName
93 @Input() width: string
92 94
93 constructor ( 95 constructor (
94 private el: ElementRef, 96 private el: ElementRef,
@@ -104,6 +106,10 @@ export class GlobalIconComponent implements OnInit {
104 'filter:internal.common.svg-icons.get-content.params', 106 'filter:internal.common.svg-icons.get-content.params',
105 'filter:internal.common.svg-icons.get-content.result' 107 'filter:internal.common.svg-icons.get-content.result'
106 ) 108 )
109
110 if (this.width) {
111 nativeElement.style.width = this.width
112 }
107 } 113 }
108 114
109 private getSVGContent (options: { name: string }) { 115 private getSVGContent (options: { name: string }) {
diff --git a/client/src/app/shared/shared-instance/feature-boolean.component.html b/client/src/app/shared/shared-instance/feature-boolean.component.html
index ccb8a30cc..6de5d2075 100644
--- a/client/src/app/shared/shared-instance/feature-boolean.component.html
+++ b/client/src/app/shared/shared-instance/feature-boolean.component.html
@@ -1,3 +1,2 @@
1<span *ngIf="value === true" class="glyphicon glyphicon-ok" i18n-aria-label aria-label="yes"></span> 1<my-global-icon *ngIf="value === true" iconName="tick" i18n-aria-label aria-label="yes"></my-global-icon>
2<span *ngIf="value === false" class="glyphicon glyphicon-remove" i18n-aria-label aria-label="no"></span> 2<my-global-icon *ngIf="value === false" iconName="cross" i18n-aria-label aria-label="no"></my-global-icon>
3
diff --git a/client/src/app/shared/shared-instance/feature-boolean.component.scss b/client/src/app/shared/shared-instance/feature-boolean.component.scss
index 8683ecc55..29b8c3e02 100644
--- a/client/src/app/shared/shared-instance/feature-boolean.component.scss
+++ b/client/src/app/shared/shared-instance/feature-boolean.component.scss
@@ -1,10 +1,10 @@
1@use '_variables' as *; 1@use '_variables' as *;
2@use '_mixins' as *; 2@use '_mixins' as *;
3 3
4.glyphicon-ok { 4my-global-icon[iconName=tick] {
5 color: $green; 5 color: $green;
6} 6}
7 7
8.glyphicon-remove { 8my-global-icon[iconName=cross] {
9 color: $red; 9 color: $red;
10} 10}
diff --git a/client/src/app/shared/shared-instance/instance-about-accordion.component.html b/client/src/app/shared/shared-instance/instance-about-accordion.component.html
index 73e511d1c..466d73ca4 100644
--- a/client/src/app/shared/shared-instance/instance-about-accordion.component.html
+++ b/client/src/app/shared/shared-instance/instance-about-accordion.component.html
@@ -1,6 +1,6 @@
1<h2 class="instance-name">{{ about?.instance.name }}</h2> 1<h2 *ngIf="displayInstanceName" class="instance-name">{{ about?.instance.name }}</h2>
2 2
3<div class="instance-short-description">{{ about?.instance.shortDescription }}</div> 3<div *ngIf="displayInstanceShortDescription" class="instance-short-description">{{ about?.instance.shortDescription }}</div>
4 4
5<ngb-accordion #accordion="ngbAccordion" [closeOthers]="true"> 5<ngb-accordion #accordion="ngbAccordion" [closeOthers]="true">
6 <ngb-panel *ngIf="panels.features" id="instance-features" i18n-title title="Features found on this instance"> 6 <ngb-panel *ngIf="panels.features" id="instance-features" i18n-title title="Features found on this instance">
@@ -32,7 +32,7 @@
32 </ng-template> 32 </ng-template>
33 </ngb-panel> 33 </ngb-panel>
34 34
35 <ngb-panel *ngIf="termsPanel" id="terms" i18n-title title="Terms"> 35 <ngb-panel *ngIf="termsPanel" id="terms" [title]="getTermsTitle()">
36 <ng-template ngbPanelContent> 36 <ng-template ngbPanelContent>
37 <div class="block" [innerHTML]="aboutHtml.terms"></div> 37 <div class="block" [innerHTML]="aboutHtml.terms"></div>
38 </ng-template> 38 </ng-template>
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 be6099a97..c8e288462 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
@@ -8,34 +8,9 @@
8.instance-short-description { 8.instance-short-description {
9 @include ellipsis-multiline(1rem, 3); 9 @include ellipsis-multiline(1rem, 3);
10 10
11 margin-top: 20px; 11 margin: 25px 0;
12 margin-bottom: 20px;
13} 12}
14 13
15.block { 14.block {
16 font-size: 15px;
17 margin-bottom: 15px; 15 margin-bottom: 15px;
18} 16}
19
20ngb-accordion ::ng-deep {
21 .card {
22 border-color: var(--mainBackgroundColor);
23
24 .card-header {
25 background-color: unset;
26 padding: 0;
27
28 + .collapse.show {
29 background-color: var(--submenuBackgroundColor);
30 }
31 }
32 }
33
34 .btn {
35 @include peertube-button;
36 @include grey-button;
37
38 border-radius: unset;
39 width: 100%;
40 }
41}
diff --git a/client/src/app/shared/shared-instance/instance-about-accordion.component.ts b/client/src/app/shared/shared-instance/instance-about-accordion.component.ts
index b9f57e2a4..e13703c03 100644
--- a/client/src/app/shared/shared-instance/instance-about-accordion.component.ts
+++ b/client/src/app/shared/shared-instance/instance-about-accordion.component.ts
@@ -15,6 +15,9 @@ export class InstanceAboutAccordionComponent implements OnInit {
15 15
16 @Output() init: EventEmitter<InstanceAboutAccordionComponent> = new EventEmitter<InstanceAboutAccordionComponent>() 16 @Output() init: EventEmitter<InstanceAboutAccordionComponent> = new EventEmitter<InstanceAboutAccordionComponent>()
17 17
18 @Input() displayInstanceName = true
19 @Input() displayInstanceShortDescription = true
20
18 @Input() pluginScope: PluginClientScope 21 @Input() pluginScope: PluginClientScope
19 @Input() pluginHook: ClientFilterHookName 22 @Input() pluginHook: ClientFilterHookName
20 23
@@ -66,6 +69,10 @@ export class InstanceAboutAccordionComponent implements OnInit {
66 return !!(this.aboutHtml?.administrator || this.about?.instance.maintenanceLifetime || this.about?.instance.businessModel) 69 return !!(this.aboutHtml?.administrator || this.about?.instance.maintenanceLifetime || this.about?.instance.businessModel)
67 } 70 }
68 71
72 getTermsTitle () {
73 return $localize`Terms of ${this.about.instance.name}`
74 }
75
69 get moderationPanel () { 76 get moderationPanel () {
70 return this.panels.moderation && !!this.aboutHtml.moderationInformation 77 return this.panels.moderation && !!this.aboutHtml.moderationInformation
71 } 78 }
diff --git a/client/src/app/shared/shared-instance/instance-features-table.component.html b/client/src/app/shared/shared-instance/instance-features-table.component.html
index 1fdef95ff..761243bfe 100644
--- a/client/src/app/shared/shared-instance/instance-features-table.component.html
+++ b/client/src/app/shared/shared-instance/instance-features-table.component.html
@@ -1,6 +1,6 @@
1<div *ngIf="serverConfig" class="feature-table"> 1<div *ngIf="serverConfig" class="feature-table">
2 2
3 <table class="table" *ngIf="serverConfig"> 3 <table *ngIf="serverConfig">
4 <caption i18n>Features found on this instance</caption> 4 <caption i18n>Features found on this instance</caption>
5 <tr> 5 <tr>
6 <th i18n class="label" scope="row">PeerTube version</th> 6 <th i18n class="label" scope="row">PeerTube version</th>
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 56ca105f4..bfae0c112 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
@@ -4,6 +4,7 @@
4table { 4table {
5 font-size: 14px; 5 font-size: 14px;
6 color: pvar(--mainForegroundColor); 6 color: pvar(--mainForegroundColor);
7 width: 100%;
7 8
8 .label, 9 .label,
9 .sub-label { 10 .sub-label {
@@ -24,13 +25,14 @@ table {
24 } 25 }
25 } 26 }
26 27
28 th,
27 td { 29 td {
28 vertical-align: middle; 30 padding: 0.75rem;
31 border-top: 1px solid #dee2e6;
29 } 32 }
30 33
31 caption { 34 caption {
32 caption-side: top; 35 caption-side: top;
33 font-size: 15px;
34 font-weight: $font-semibold; 36 font-weight: $font-semibold;
35 color: pvar(--mainForegroundColor); 37 color: pvar(--mainForegroundColor);
36 } 38 }
diff --git a/client/src/app/shared/shared-instance/instance-features-table.component.ts b/client/src/app/shared/shared-instance/instance-features-table.component.ts
index 6335de450..e405c5790 100644
--- a/client/src/app/shared/shared-instance/instance-features-table.component.ts
+++ b/client/src/app/shared/shared-instance/instance-features-table.component.ts
@@ -1,5 +1,6 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { ServerService } from '@app/core' 2import { ServerService } from '@app/core'
3import { prepareIcu } from '@app/helpers'
3import { ServerConfig } from '@shared/models' 4import { ServerConfig } from '@shared/models'
4import { PeertubeModalService } from '../shared-main/peertube-modal/peertube-modal.service' 5import { PeertubeModalService } from '../shared-main/peertube-modal/peertube-modal.service'
5 6
@@ -65,15 +66,20 @@ export class InstanceFeaturesTableComponent implements OnInit {
65 66
66 private getApproximateTime (seconds: number) { 67 private getApproximateTime (seconds: number) {
67 const hours = Math.floor(seconds / 3600) 68 const hours = Math.floor(seconds / 3600)
68 let pluralSuffix = ''
69 if (hours > 1) pluralSuffix = 's'
70 if (hours > 0) return `~ ${hours} hour${pluralSuffix}`
71 69
72 const minutes = Math.floor(seconds % 3600 / 60) 70 if (hours !== 0) {
71 return prepareIcu($localize`~ {hours, plural, =1 {1 hour} other {{hours} hours}}`)(
72 { hours },
73 $localize`~ ${hours} hours`
74 )
75 }
73 76
74 if (minutes === 1) return $localize`~ 1 minute` 77 const minutes = Math.floor(seconds % 3600 / 60)
75 78
76 return $localize`~ ${minutes} minutes` 79 return prepareIcu($localize`~ {minutes, plural, =1 {1 minute} other {{minutes} minutes}}`)(
80 { minutes },
81 $localize`~ ${minutes} minutes`
82 )
77 } 83 }
78 84
79 private buildQuotaHelpIndication () { 85 private buildQuotaHelpIndication () {
diff --git a/client/src/app/shared/shared-instance/instance-statistics.component.html b/client/src/app/shared/shared-instance/instance-statistics.component.html
index 2ca61fd94..68b209990 100644
--- a/client/src/app/shared/shared-instance/instance-statistics.component.html
+++ b/client/src/app/shared/shared-instance/instance-statistics.component.html
@@ -10,7 +10,7 @@
10 <p class="stat-value">{{ serverStats.totalUsers | number }}</p> 10 <p class="stat-value">{{ serverStats.totalUsers | number }}</p>
11 <p class="stat-label" i18n>users</p> 11 <p class="stat-label" i18n>users</p>
12 </div> 12 </div>
13 <i class="glyphicon glyphicon-user icon-bottom"></i> 13 <my-global-icon iconName="user"></my-global-icon>
14 </div> 14 </div>
15 </div> 15 </div>
16 16
@@ -20,7 +20,7 @@
20 <p class="stat-value">{{ serverStats.totalLocalVideos | number }}</p> 20 <p class="stat-value">{{ serverStats.totalLocalVideos | number }}</p>
21 <p class="stat-label" i18n>videos</p> 21 <p class="stat-label" i18n>videos</p>
22 </div> 22 </div>
23 <i class="glyphicon glyphicon-facetime-video"></i> 23 <my-global-icon iconName="film"></my-global-icon>
24 </div> 24 </div>
25 </div> 25 </div>
26 26
@@ -30,7 +30,7 @@
30 <p class="stat-value">{{ serverStats.totalLocalVideoViews | number }}</p> 30 <p class="stat-value">{{ serverStats.totalLocalVideoViews | number }}</p>
31 <p class="stat-label" i18n>views</p> 31 <p class="stat-label" i18n>views</p>
32 </div> 32 </div>
33 <i class="glyphicon glyphicon-eye-open"></i> 33 <my-global-icon iconName="eye-open"></my-global-icon>
34 </div> 34 </div>
35 </div> 35 </div>
36 36
@@ -40,7 +40,7 @@
40 <p class="stat-value">{{ serverStats.totalLocalVideoComments | number }}</p> 40 <p class="stat-value">{{ serverStats.totalLocalVideoComments | number }}</p>
41 <p class="stat-label" i18n>comments</p> 41 <p class="stat-label" i18n>comments</p>
42 </div> 42 </div>
43 <i class="glyphicon glyphicon-comment"></i> 43 <my-global-icon iconName="message-circle"></my-global-icon>
44 </div> 44 </div>
45 </div> 45 </div>
46 46
@@ -50,7 +50,7 @@
50 <p class="stat-value">{{ serverStats.totalLocalVideoFilesSize | bytes:1 }}</p> 50 <p class="stat-value">{{ serverStats.totalLocalVideoFilesSize | bytes:1 }}</p>
51 <p class="stat-label" i18n>hosted video</p> 51 <p class="stat-label" i18n>hosted video</p>
52 </div> 52 </div>
53 <i class="glyphicon glyphicon-hdd"></i> 53 <my-global-icon iconName="home"></my-global-icon>
54 </div> 54 </div>
55 </div> 55 </div>
56 </div> 56 </div>
@@ -64,7 +64,7 @@
64 <p class="stat-value">{{ serverStats.totalVideos | number }}</p> 64 <p class="stat-value">{{ serverStats.totalVideos | number }}</p>
65 <p class="stat-label" i18n>videos</p> 65 <p class="stat-label" i18n>videos</p>
66 </div> 66 </div>
67 <i class="glyphicon glyphicon-facetime-video"></i> 67 <my-global-icon iconName="film"></my-global-icon>
68 </div> 68 </div>
69 </div> 69 </div>
70 70
@@ -74,7 +74,7 @@
74 <p class="stat-value">{{ serverStats.totalVideoComments | number }}</p> 74 <p class="stat-value">{{ serverStats.totalVideoComments | number }}</p>
75 <p class="stat-label" i18n>comments</p> 75 <p class="stat-label" i18n>comments</p>
76 </div> 76 </div>
77 <i class="glyphicon glyphicon-comment"></i> 77 <my-global-icon iconName="message-circle"></my-global-icon>
78 </div> 78 </div>
79 </div> 79 </div>
80 80
@@ -84,7 +84,7 @@
84 <p class="stat-value">{{ serverStats.totalInstanceFollowers | number }}</p> 84 <p class="stat-value">{{ serverStats.totalInstanceFollowers | number }}</p>
85 <p class="stat-label" i18n>followers</p> 85 <p class="stat-label" i18n>followers</p>
86 </div> 86 </div>
87 <i class="glyphicon glyphicon-retweet"></i> 87 <my-global-icon iconName="share"></my-global-icon>
88 </div> 88 </div>
89 </div> 89 </div>
90 90
@@ -94,7 +94,7 @@
94 <p class="stat-value">{{ serverStats.totalInstanceFollowing | number }}</p> 94 <p class="stat-value">{{ serverStats.totalInstanceFollowing | number }}</p>
95 <p class="stat-label" i18n>following</p> 95 <p class="stat-label" i18n>following</p>
96 </div> 96 </div>
97 <i class="glyphicon glyphicon-retweet"></i> 97 <my-global-icon iconName="globe"></my-global-icon>
98 </div> 98 </div>
99 </div> 99 </div>
100 </div> 100 </div>
diff --git a/client/src/app/shared/shared-instance/instance-statistics.component.scss b/client/src/app/shared/shared-instance/instance-statistics.component.scss
index 5286ab03a..e1d489d28 100644
--- a/client/src/app/shared/shared-instance/instance-statistics.component.scss
+++ b/client/src/app/shared/shared-instance/instance-statistics.component.scss
@@ -1,3 +1,5 @@
1@use '_variables' as *;
2@use '_mixins' as *;
1 3
2h3 { 4h3 {
3 font-size: 1.25rem; 5 font-size: 1.25rem;
@@ -19,22 +21,19 @@ h3 {
19 margin: 0; 21 margin: 0;
20 } 22 }
21 23
22 .glyphicon {
23 opacity: 0.12;
24 position: absolute;
25 left: 16px;
26 top: -24px;
27
28 &.icon-bottom {
29 top: 4px;
30 }
31
32 &::before {
33 font-size: 8em;
34 }
35 }
36
37 .card-body { 24 .card-body {
38 z-index: 2; 25 z-index: 2;
39 } 26 }
40} 27}
28
29my-global-icon {
30 opacity: 0.12;
31 position: absolute;
32 left: 16px;
33 top: -24px;
34 width: 110px;
35
36 &.icon-bottom {
37 top: 4px;
38 }
39}
diff --git a/client/src/app/shared/shared-instance/shared-instance.module.ts b/client/src/app/shared/shared-instance/shared-instance.module.ts
index 13c681ab8..dfce88e11 100644
--- a/client/src/app/shared/shared-instance/shared-instance.module.ts
+++ b/client/src/app/shared/shared-instance/shared-instance.module.ts
@@ -1,6 +1,7 @@
1 1
2import { NgModule } from '@angular/core' 2import { NgModule } from '@angular/core'
3import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap' 3import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
4import { SharedGlobalIconModule } from '../shared-icons'
4import { SharedMainModule } from '../shared-main/shared-main.module' 5import { SharedMainModule } from '../shared-main/shared-main.module'
5import { FeatureBooleanComponent } from './feature-boolean.component' 6import { FeatureBooleanComponent } from './feature-boolean.component'
6import { InstanceAboutAccordionComponent } from './instance-about-accordion.component' 7import { InstanceAboutAccordionComponent } from './instance-about-accordion.component'
@@ -12,6 +13,7 @@ import { InstanceService } from './instance.service'
12@NgModule({ 13@NgModule({
13 imports: [ 14 imports: [
14 SharedMainModule, 15 SharedMainModule,
16 SharedGlobalIconModule,
15 NgbAccordionModule 17 NgbAccordionModule
16 ], 18 ],
17 19
diff --git a/client/src/app/shared/shared-main/angular/autofocus.directive.ts b/client/src/app/shared/shared-main/angular/autofocus.directive.ts
index 2da492ea1..051635f45 100644
--- a/client/src/app/shared/shared-main/angular/autofocus.directive.ts
+++ b/client/src/app/shared/shared-main/angular/autofocus.directive.ts
@@ -7,6 +7,8 @@ export class AutofocusDirective implements AfterViewInit {
7 constructor (private host: ElementRef) { } 7 constructor (private host: ElementRef) { }
8 8
9 ngAfterViewInit () { 9 ngAfterViewInit () {
10 this.host.nativeElement.focus() 10 const el = this.host.nativeElement as HTMLElement
11
12 el.focus({ preventScroll: true })
11 } 13 }
12} 14}
diff --git a/client/src/app/shared/shared-main/angular/from-now.pipe.ts b/client/src/app/shared/shared-main/angular/from-now.pipe.ts
index d62c1f88e..dc6a25e83 100644
--- a/client/src/app/shared/shared-main/angular/from-now.pipe.ts
+++ b/client/src/app/shared/shared-main/angular/from-now.pipe.ts
@@ -1,37 +1,51 @@
1import { Pipe, PipeTransform } from '@angular/core' 1import { Pipe, PipeTransform } from '@angular/core'
2import { prepareIcu } from '@app/helpers'
2 3
3// Thanks: https://stackoverflow.com/questions/3177836/how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-site 4// Thanks: https://stackoverflow.com/questions/3177836/how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-site
4@Pipe({ name: 'myFromNow' }) 5@Pipe({ name: 'myFromNow' })
5export class FromNowPipe implements PipeTransform { 6export class FromNowPipe implements PipeTransform {
7 private yearICU = prepareIcu($localize`{interval, plural, =1 {1 year ago} other {{interval} years ago}}`)
8 private monthICU = prepareIcu($localize`{interval, plural, =1 {1 month ago} other {{interval} months ago}}`)
9 private weekICU = prepareIcu($localize`{interval, plural, =1 {1 week ago} other {{interval} weeks ago}}`)
10 private dayICU = prepareIcu($localize`{interval, plural, =1 {1 day ago} other {{interval} days ago}}`)
11 private hourICU = prepareIcu($localize`{interval, plural, =1 {1 hour ago} other {{interval} hours ago}}`)
12
6 transform (arg: number | Date | string) { 13 transform (arg: number | Date | string) {
7 const argDate = new Date(arg) 14 const argDate = new Date(arg)
8 const seconds = Math.floor((Date.now() - argDate.getTime()) / 1000) 15 const seconds = Math.floor((Date.now() - argDate.getTime()) / 1000)
9 16
10 let interval = Math.floor(seconds / 31536000) 17 let interval = Math.floor(seconds / 31536000)
11 if (interval > 1) return $localize`${interval} years ago` 18 if (interval >= 1) {
12 if (interval === 1) return $localize`1 year ago` 19 return this.yearICU({ interval }, $localize`${interval} year(s) ago`)
20 }
13 21
14 interval = Math.floor(seconds / 2419200) 22 interval = Math.floor(seconds / 2419200)
15 // 12 months = 360 days, but a year ~ 365 days 23 // 12 months = 360 days, but a year ~ 365 days
16 // Display "1 year ago" rather than "12 months ago" 24 // Display "1 year ago" rather than "12 months ago"
17 if (interval >= 12) return $localize`1 year ago` 25 if (interval >= 12) return $localize`1 year ago`
18 if (interval > 1) return $localize`${interval} months ago` 26
19 if (interval === 1) return $localize`1 month ago` 27 if (interval >= 1) {
28 return this.monthICU({ interval }, $localize`${interval} month(s) ago`)
29 }
20 30
21 interval = Math.floor(seconds / 604800) 31 interval = Math.floor(seconds / 604800)
22 // 4 weeks ~ 28 days, but our month is 30 days 32 // 4 weeks ~ 28 days, but our month is 30 days
23 // Display "1 month ago" rather than "4 weeks ago" 33 // Display "1 month ago" rather than "4 weeks ago"
24 if (interval >= 4) return $localize`1 month ago` 34 if (interval >= 4) return $localize`1 month ago`
25 if (interval > 1) return $localize`${interval} weeks ago` 35
26 if (interval === 1) return $localize`1 week ago` 36 if (interval >= 1) {
37 return this.weekICU({ interval }, $localize`${interval} week(s) ago`)
38 }
27 39
28 interval = Math.floor(seconds / 86400) 40 interval = Math.floor(seconds / 86400)
29 if (interval > 1) return $localize`${interval} days ago` 41 if (interval >= 1) {
30 if (interval === 1) return $localize`1 day ago` 42 return this.dayICU({ interval }, $localize`${interval} day(s) ago`)
43 }
31 44
32 interval = Math.floor(seconds / 3600) 45 interval = Math.floor(seconds / 3600)
33 if (interval > 1) return $localize`${interval} hours ago` 46 if (interval >= 1) {
34 if (interval === 1) return $localize`1 hour ago` 47 return this.hourICU({ interval }, $localize`${interval} hour(s) ago`)
48 }
35 49
36 interval = Math.floor(seconds / 60) 50 interval = Math.floor(seconds / 60)
37 if (interval >= 1) return $localize`${interval} min ago` 51 if (interval >= 1) return $localize`${interval} min ago`
diff --git a/client/src/app/shared/shared-main/buttons/action-dropdown.component.html b/client/src/app/shared/shared-main/buttons/action-dropdown.component.html
index 10dae68f0..017355bd0 100644
--- a/client/src/app/shared/shared-main/buttons/action-dropdown.component.html
+++ b/client/src/app/shared/shared-main/buttons/action-dropdown.component.html
@@ -39,7 +39,7 @@
39 </span> 39 </span>
40 40
41 <h6 41 <h6
42 *ngIf="!action.linkBuilder && action.isHeader" [ngClass]="{ 'with-icon': !!action.iconName }" 42 *ngIf="!action.linkBuilder && action.isHeader && areActionsDisplayed(actions, entry)" [ngClass]="{ 'with-icon': !!action.iconName }"
43 class="dropdown-header" tabindex="0" role="button" [title]="action.title || ''" (click)="action.handler(entry)" (keyup.enter)="action.handler(entry)" 43 class="dropdown-header" tabindex="0" role="button" [title]="action.title || ''" (click)="action.handler(entry)" (keyup.enter)="action.handler(entry)"
44 > 44 >
45 <ng-container *ngTemplateOutlet="templateActionLabel; context:{ $implicit: action }"></ng-container> 45 <ng-container *ngTemplateOutlet="templateActionLabel; context:{ $implicit: action }"></ng-container>
diff --git a/client/src/app/shared/shared-main/buttons/action-dropdown.component.ts b/client/src/app/shared/shared-main/buttons/action-dropdown.component.ts
index 67ac6e1aa..749773f8a 100644
--- a/client/src/app/shared/shared-main/buttons/action-dropdown.component.ts
+++ b/client/src/app/shared/shared-main/buttons/action-dropdown.component.ts
@@ -48,7 +48,7 @@ export class ActionDropdownComponent<T> {
48 return actions.some(a => { 48 return actions.some(a => {
49 if (Array.isArray(a)) return this.areActionsDisplayed(a, entry) 49 if (Array.isArray(a)) return this.areActionsDisplayed(a, entry)
50 50
51 return a.isDisplayed === undefined || a.isDisplayed(entry) 51 return a.isHeader !== true && (a.isDisplayed === undefined || a.isDisplayed(entry))
52 }) 52 })
53 } 53 }
54} 54}
diff --git a/client/src/app/shared/shared-main/buttons/button.component.html b/client/src/app/shared/shared-main/buttons/button.component.html
index d1a4215e6..3e3728623 100644
--- a/client/src/app/shared/shared-main/buttons/button.component.html
+++ b/client/src/app/shared/shared-main/buttons/button.component.html
@@ -1,8 +1,16 @@
1<span class="action-button" [ngClass]="getClasses()" [ngbTooltip]="title" tabindex="0"> 1<div *ngIf="!routerLink" class="action-button" [ngClass]="classes" [ngbTooltip]="title" tabindex="0">
2 <my-small-loader [loading]="loading"></my-small-loader> 2 <ng-container *ngTemplateOutlet="content"></ng-container>
3</div>
4
5<a *ngIf="routerLink" class="action-button" [ngClass]="classes" [ngbTooltip]="title" [routerLink]="routerLink">
6 <ng-container *ngTemplateOutlet="content"></ng-container>
7</a>
8
9<ng-template #content>
10 <my-loader size="sm" [loading]="loading"></my-loader>
3 <my-global-icon *ngIf="icon && !loading" [iconName]="icon"></my-global-icon> 11 <my-global-icon *ngIf="icon && !loading" [iconName]="icon"></my-global-icon>
4 12
5 <span *ngIf="label" class="button-label">{{ label }}</span> 13 <span *ngIf="label" class="button-label">{{ label }}</span>
6 14
7 <ng-content></ng-content> 15 <ng-content></ng-content>
8</span> 16</ng-template>
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 c53b8f2e5..7f0cdf1ed 100644
--- a/client/src/app/shared/shared-main/buttons/button.component.scss
+++ b/client/src/app/shared/shared-main/buttons/button.component.scss
@@ -9,17 +9,14 @@
9 .button-label { 9 .button-label {
10 display: none; 10 display: none;
11 } 11 }
12}
13 12
14:host { 13 my-global-icon {
15 outline: none; 14 margin: 0 !important;
16 display: inline-block; 15 }
17} 16}
18 17
19my-small-loader ::ng-deep .root { 18:host {
20 display: inline-block; 19 display: inline-block;
21 margin: 0 3px 0 0;
22 width: 20px;
23} 20}
24 21
25a[class$=-button], 22a[class$=-button],
@@ -30,35 +27,34 @@ span[class$=-button] {
30} 27}
31 28
32.action-button { 29.action-button {
33 @include peertube-button-link;
34 @include button-with-icon(21px);
35
36 width: 100%; // useful for ellipsis, allow to define a max-width on host component 30 width: 100%; // useful for ellipsis, allow to define a max-width on host component
37 31
38 &.icon-only { 32 &.has-icon {
39 my-global-icon { 33 @include button-with-icon(21px);
40 margin: 0; 34 }
41 } 35
36 &.icon-only my-global-icon {
37 margin: 0 !important;
42 } 38 }
43} 39}
44 40
45.orange-button { 41.orange-button,
42.grey-button {
46 @include peertube-button; 43 @include peertube-button;
47 @include orange-button;
48} 44}
49 45
50.orange-button-link { 46.orange-button-link,
47.grey-button-link {
51 @include peertube-button-link; 48 @include peertube-button-link;
52 @include orange-button;
53} 49}
54 50
55.grey-button { 51.orange-button,
56 @include peertube-button; 52.orange-button-link {
57 @include grey-button; 53 @include orange-button;
58} 54}
59 55
56.grey-button,
60.grey-button-link { 57.grey-button-link {
61 @include peertube-button-link;
62 @include grey-button; 58 @include grey-button;
63} 59}
64 60
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 52936a4d4..10d67831f 100644
--- a/client/src/app/shared/shared-main/buttons/button.component.ts
+++ b/client/src/app/shared/shared-main/buttons/button.component.ts
@@ -1,4 +1,4 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input, OnChanges } from '@angular/core'
2import { GlobalIconName } from '@app/shared/shared-icons' 2import { GlobalIconName } from '@app/shared/shared-icons'
3 3
4@Component({ 4@Component({
@@ -7,20 +7,24 @@ import { GlobalIconName } from '@app/shared/shared-icons'
7 templateUrl: './button.component.html' 7 templateUrl: './button.component.html'
8}) 8})
9 9
10export class ButtonComponent { 10export class ButtonComponent implements OnChanges {
11 @Input() label = '' 11 @Input() label = ''
12 @Input() className = 'grey-button' 12 @Input() className = 'grey-button'
13 @Input() icon: GlobalIconName = undefined 13 @Input() icon: GlobalIconName = undefined
14 @Input() routerLink: string[] | string
14 @Input() title: string = undefined 15 @Input() title: string = undefined
15 @Input() loading = false 16 @Input() loading = false
16 @Input() disabled = false 17 @Input() disabled = false
17 @Input() responsiveLabel = false 18 @Input() responsiveLabel = false
18 19
19 getClasses () { 20 classes: { [id: string]: boolean } = {}
20 return { 21
22 ngOnChanges () {
23 this.classes = {
21 [this.className]: true, 24 [this.className]: true,
22 disabled: this.disabled, 25 disabled: this.disabled,
23 'icon-only': !this.label, 26 'icon-only': !this.label,
27 'has-icon': !!this.icon,
24 'responsive-label': this.responsiveLabel 28 'responsive-label': this.responsiveLabel
25 } 29 }
26 } 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
deleted file mode 100644
index d7a6702a7..000000000
--- a/client/src/app/shared/shared-main/buttons/delete-button.component.html
+++ /dev/null
@@ -1,8 +0,0 @@
1<span
2 class="action-button action-button-delete grey-button"
3 [ngClass]="{ 'responsive-label': responsiveLabel }" [ngbTooltip]="title" role="button" tabindex="0"
4>
5 <my-global-icon iconName="delete" aria-hidden="true"></my-global-icon>
6
7 <span class="button-label" *ngIf="label">{{ label }}</span>
8</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 90735852c..1cab10803 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
@@ -2,17 +2,16 @@ import { Component, Input, OnInit } from '@angular/core'
2 2
3@Component({ 3@Component({
4 selector: 'my-delete-button', 4 selector: 'my-delete-button',
5 styleUrls: [ './button.component.scss' ], 5 template: `
6 templateUrl: './delete-button.component.html' 6 <my-button icon="delete" className="grey-button" [label]="label" [title]="title" [responsiveLabel]="responsiveLabel"></my-button>
7 `
7}) 8})
8
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 @Input() responsiveLabel = false
13 13
14 ngOnInit () { 14 ngOnInit () {
15 // <my-delete-button /> No label
16 if (this.label === undefined && !this.title) { 15 if (this.label === undefined && !this.title) {
17 this.title = $localize`Delete` 16 this.title = $localize`Delete`
18 } 17 }
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
deleted file mode 100644
index 8beeee6c4..000000000
--- a/client/src/app/shared/shared-main/buttons/edit-button.component.html
+++ /dev/null
@@ -1,8 +0,0 @@
1<a
2 class="action-button action-button-edit grey-button"
3 [ngClass]="{ 'responsive-label': responsiveLabel }" [routerLink]="routerLink" [ngbTooltip]="title"
4>
5 <my-global-icon iconName="edit" aria-hidden="true"></my-global-icon>
6
7 <span class="button-label" *ngIf="label">{{ label }}</span>
8</a>
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 24c8625ff..28aacbbff 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
@@ -2,8 +2,13 @@ import { Component, Input, OnInit } from '@angular/core'
2 2
3@Component({ 3@Component({
4 selector: 'my-edit-button', 4 selector: 'my-edit-button',
5 styleUrls: [ './button.component.scss' ], 5 template: `
6 templateUrl: './edit-button.component.html' 6 <my-button
7 icon="edit" className="grey-button-link"
8 [label]="label" [title]="title" [responsiveLabel]="responsiveLabel"
9 [routerLink]="routerLink"
10 ></my-button>
11 `
7}) 12})
8export class EditButtonComponent implements OnInit { 13export class EditButtonComponent implements OnInit {
9 @Input() label: string 14 @Input() label: string
@@ -20,10 +25,6 @@ export class EditButtonComponent implements OnInit {
20 // <my-edit-button label /> Use default label 25 // <my-edit-button label /> Use default label
21 if (this.label === '') { 26 if (this.label === '') {
22 this.label = $localize`Update` 27 this.label = $localize`Update`
23
24 if (!this.title) {
25 this.title = this.label
26 }
27 } 28 }
28 } 29 }
29} 30}
diff --git a/client/src/app/shared/shared-main/loaders/index.ts b/client/src/app/shared/shared-main/loaders/index.ts
index a061914d5..60483727c 100644
--- a/client/src/app/shared/shared-main/loaders/index.ts
+++ b/client/src/app/shared/shared-main/loaders/index.ts
@@ -1,2 +1 @@
1export * from './loader.component' export * from './loader.component'
2export * from './small-loader.component'
diff --git a/client/src/app/shared/shared-main/loaders/loader.component.html b/client/src/app/shared/shared-main/loaders/loader.component.html
deleted file mode 100644
index ca8ed063e..000000000
--- a/client/src/app/shared/shared-main/loaders/loader.component.html
+++ /dev/null
@@ -1,8 +0,0 @@
1<div *ngIf="loading">
2 <div class="loader">
3 <div></div>
4 <div></div>
5 <div></div>
6 <div></div>
7 </div>
8</div>
diff --git a/client/src/app/shared/shared-main/loaders/loader.component.scss b/client/src/app/shared/shared-main/loaders/loader.component.scss
deleted file mode 100644
index b88b0db6a..000000000
--- a/client/src/app/shared/shared-main/loaders/loader.component.scss
+++ /dev/null
@@ -1,45 +0,0 @@
1@use '_variables' as *;
2@use '_mixins' as *;
3
4// Thanks to https://loading.io/css/ (CC0 License)
5
6.loader {
7 display: inline-block;
8 position: relative;
9 width: 50px;
10 height: 50px;
11}
12
13.loader div {
14 box-sizing: border-box;
15 display: block;
16 position: absolute;
17 width: 44px;
18 height: 44px;
19 margin: 6px;
20 border: 4px solid;
21 border-radius: 50%;
22 animation: loader 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
23 border-color: #999999 transparent transparent;
24}
25
26.loader div:nth-child(1) {
27 animation-delay: -0.45s;
28}
29
30.loader div:nth-child(2) {
31 animation-delay: -0.3s;
32}
33
34.loader div:nth-child(3) {
35 animation-delay: -0.15s;
36}
37
38@keyframes loader {
39 0% {
40 transform: rotate(0deg);
41 }
42 100% {
43 transform: rotate(360deg);
44 }
45}
diff --git a/client/src/app/shared/shared-main/loaders/loader.component.ts b/client/src/app/shared/shared-main/loaders/loader.component.ts
index e3b1eea3a..bd038f8b5 100644
--- a/client/src/app/shared/shared-main/loaders/loader.component.ts
+++ b/client/src/app/shared/shared-main/loaders/loader.component.ts
@@ -2,9 +2,27 @@ import { Component, Input } from '@angular/core'
2 2
3@Component({ 3@Component({
4 selector: 'my-loader', 4 selector: 'my-loader',
5 styleUrls: [ './loader.component.scss' ], 5 template: `<div *ngIf="loading" class="spinner-border" [ngStyle]="getStyle()" role="status"></div>`
6 templateUrl: './loader.component.html'
7}) 6})
8export class LoaderComponent { 7export class LoaderComponent {
9 @Input() loading: boolean 8 @Input() loading: boolean
9 @Input() size: 'sm' | 'xl'
10
11 private readonly sizes = {
12 sm: {
13 width: '1rem',
14 height: '1rem',
15 'border-width': '0.15rem'
16 },
17 xl: {
18 width: '3rem',
19 height: '3rem'
20 }
21 }
22
23 getStyle () {
24 if (!this.size) return undefined
25
26 return this.sizes[this.size]
27 }
10} 28}
diff --git a/client/src/app/shared/shared-main/loaders/small-loader.component.html b/client/src/app/shared/shared-main/loaders/small-loader.component.html
deleted file mode 100644
index 7886f8918..000000000
--- a/client/src/app/shared/shared-main/loaders/small-loader.component.html
+++ /dev/null
@@ -1,3 +0,0 @@
1<div class="root" *ngIf="loading">
2 <div class="glyphicon glyphicon-refresh glyphicon-refresh-animate"></div>
3</div>
diff --git a/client/src/app/shared/shared-main/loaders/small-loader.component.ts b/client/src/app/shared/shared-main/loaders/small-loader.component.ts
deleted file mode 100644
index 191877f14..000000000
--- a/client/src/app/shared/shared-main/loaders/small-loader.component.ts
+++ /dev/null
@@ -1,11 +0,0 @@
1import { Component, Input } from '@angular/core'
2
3@Component({
4 selector: 'my-small-loader',
5 styleUrls: [ ],
6 templateUrl: './small-loader.component.html'
7})
8
9export class SmallLoaderComponent {
10 @Input() loading: boolean
11}
diff --git a/client/src/app/shared/shared-main/misc/channels-setup-message.component.html b/client/src/app/shared/shared-main/misc/channels-setup-message.component.html
index 3fe888a35..539df06bd 100644
--- a/client/src/app/shared/shared-main/misc/channels-setup-message.component.html
+++ b/client/src/app/shared/shared-main/misc/channels-setup-message.component.html
@@ -1,4 +1,4 @@
1<div *ngIf="hasChannelNotConfigured()" class="channels-setup-message alert alert-info"> 1<div *ngIf="hasChannelNotConfigured()" class="channels-setup-message alert pt-alert-primary">
2 <my-global-icon iconName="tip"></my-global-icon> 2 <my-global-icon iconName="tip"></my-global-icon>
3 3
4 <div> 4 <div>
diff --git a/client/src/app/shared/shared-main/misc/channels-setup-message.component.scss b/client/src/app/shared/shared-main/misc/channels-setup-message.component.scss
index 7dcba2ca5..2aa176e1b 100644
--- a/client/src/app/shared/shared-main/misc/channels-setup-message.component.scss
+++ b/client/src/app/shared/shared-main/misc/channels-setup-message.component.scss
@@ -5,28 +5,24 @@
5 display: flex; 5 display: flex;
6 align-items: center; 6 align-items: center;
7 justify-content: center; 7 justify-content: center;
8}
8 9
9 my-global-icon { 10my-global-icon {
10 width: 32px; 11 @include apply-svg-color(pvar(--mainColor));
11 align-self: flex-start;
12 12
13 ::ng-deep { 13 width: 32px;
14 svg { 14 align-self: flex-start;
15 fill: #0c5460;
16 }
17 }
18 15
19 + div { 16 + div {
20 margin-left: 10px; 17 margin-left: 10px;
21 text-align: center; 18 text-align: center;
19 }
20}
22 21
23 a.channels-settings-link { 22.channels-settings-link {
24 @include peertube-button-link; 23 @include peertube-button-link;
25 @include grey-button; 24 @include grey-button;
26 25
27 height: fit-content; 26 height: fit-content;
28 margin-top: 10px; 27 margin-top: 10px;
29 }
30 }
31 }
32} 28}
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 b2e0982f1..6f29eaefa 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
@@ -1,18 +1,22 @@
1<div #itemsParent class="d-flex align-items-center text-nowrap w-100 list-overflow-parent"> 1<div #itemsParent class="list-overflow-parent">
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="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
12 *ngIf="!isInMobileView" class="list-overflow-menu"
13 ngbDropdown container="body" #dropdown="ngbDropdown"
14 (mouseleave)="closeDropdownIfHovered(dropdown)" (mouseenter)="openDropdownOnHover(dropdown)"
15 >
12 <button class="btn btn-outline-secondary btn-sm" [ngClass]="{ 'route-active': active }" 16 <button class="btn btn-outline-secondary btn-sm" [ngClass]="{ 'route-active': active }"
13 ngbDropdownAnchor (click)="dropdownAnchorClicked(dropdown)" role="button" 17 ngbDropdownAnchor (click)="dropdownAnchorClicked(dropdown)" role="button"
14 > 18 >
15 <span class="glyphicon glyphicon-chevron-down"></span> 19 <span class="chevron-down"></span>
16 </button> 20 </button>
17 21
18 <div ngbDropdownMenu> 22 <div ngbDropdownMenu>
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 19c055fd3..b06418568 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
@@ -7,6 +7,9 @@
7 7
8.list-overflow-parent { 8.list-overflow-parent {
9 overflow: hidden; 9 overflow: hidden;
10 display: flex;
11 // For the menu icon
12 max-width: calc(100vw - 30px);
10} 13}
11 14
12.list-overflow-menu { 15.list-overflow-menu {
diff --git a/client/src/app/shared/shared-main/misc/list-overflow.component.ts b/client/src/app/shared/shared-main/misc/list-overflow.component.ts
index fbc481093..541991f74 100644
--- a/client/src/app/shared/shared-main/misc/list-overflow.component.ts
+++ b/client/src/app/shared/shared-main/misc/list-overflow.component.ts
@@ -15,6 +15,9 @@ import {
15} from '@angular/core' 15} from '@angular/core'
16import { ScreenService } from '@app/core' 16import { ScreenService } from '@app/core'
17import { NgbDropdown, NgbModal } from '@ng-bootstrap/ng-bootstrap' 17import { NgbDropdown, NgbModal } from '@ng-bootstrap/ng-bootstrap'
18import * as debug from 'debug'
19
20const logger = debug('peertube:main:ListOverflowItem')
18 21
19export interface ListOverflowItem { 22export interface ListOverflowItem {
20 label: string 23 label: string
@@ -37,7 +40,6 @@ export class ListOverflowComponent<T extends ListOverflowItem> implements AfterV
37 40
38 showItemsUntilIndexExcluded: number 41 showItemsUntilIndexExcluded: number
39 active = false 42 active = false
40 isInTouchScreen = false
41 isInMobileView = false 43 isInMobileView = false
42 44
43 private openedOnHover = false 45 private openedOnHover = false
@@ -58,13 +60,14 @@ export class ListOverflowComponent<T extends ListOverflowItem> implements AfterV
58 60
59 @HostListener('window:resize') 61 @HostListener('window:resize')
60 onWindowResize () { 62 onWindowResize () {
61 this.isInTouchScreen = !!this.screenService.isInTouchScreen()
62 this.isInMobileView = !!this.screenService.isInMobileView() 63 this.isInMobileView = !!this.screenService.isInMobileView()
63 64
64 const parentWidth = this.parent.nativeElement.getBoundingClientRect().width 65 const parentWidth = this.parent.nativeElement.getBoundingClientRect().width
65 let showItemsUntilIndexExcluded: number 66 let showItemsUntilIndexExcluded: number
66 let accWidth = 0 67 let accWidth = 0
67 68
69 logger('Parent width is %d', parentWidth)
70
68 for (const [ index, el ] of this.itemsRendered.toArray().entries()) { 71 for (const [ index, el ] of this.itemsRendered.toArray().entries()) {
69 accWidth += el.nativeElement.getBoundingClientRect().width 72 accWidth += el.nativeElement.getBoundingClientRect().width
70 if (showItemsUntilIndexExcluded === undefined) { 73 if (showItemsUntilIndexExcluded === undefined) {
@@ -76,6 +79,8 @@ export class ListOverflowComponent<T extends ListOverflowItem> implements AfterV
76 e.style.visibility = shouldBeVisible ? 'inherit' : 'hidden' 79 e.style.visibility = shouldBeVisible ? 'inherit' : 'hidden'
77 } 80 }
78 81
82 logger('Accumulated children width is %d so exclude index is %d', accWidth, showItemsUntilIndexExcluded)
83
79 this.showItemsUntilIndexExcluded = showItemsUntilIndexExcluded 84 this.showItemsUntilIndexExcluded = showItemsUntilIndexExcluded
80 this.cdr.markForCheck() 85 this.cdr.markForCheck()
81 } 86 }
diff --git a/client/src/app/shared/shared-main/misc/simple-search-input.component.html b/client/src/app/shared/shared-main/misc/simple-search-input.component.html
index 1e2f6c6a9..386d26116 100644
--- a/client/src/app/shared/shared-main/misc/simple-search-input.component.html
+++ b/client/src/app/shared/shared-main/misc/simple-search-input.component.html
@@ -1,17 +1,10 @@
1<div class="root"> 1<div class="root">
2 <div class="input-group has-feedback has-clear"> 2 <div class="input-group has-clear">
3 <input 3 <input #ref type="text" class="last-in-group"
4 #ref 4 [(ngModel)]="value" (keyup.enter)="sendSearch()" [hidden]="!inputShown" [name]="name" [placeholder]="placeholder"
5 type="text"
6 [(ngModel)]="value"
7 (keyup.enter)="sendSearch()"
8 [hidden]="!inputShown"
9 [name]="name"
10 [placeholder]="placeholder"
11 > 5 >
12 6
13 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="onResetFilter()"></a> 7 <my-global-icon iconName="cross" role="button" class="form-control-clear" title="Clear filter" i18n-title (click)="onResetFilter()"></my-global-icon>
14 <span class="sr-only" i18n>Clear filters</span>
15 </div> 8 </div>
16 9
17 <my-global-icon iconName="search" aria-label="Search" role="button" (click)="onIconClick()" [title]="iconTitle"></my-global-icon> 10 <my-global-icon iconName="search" aria-label="Search" role="button" (click)="onIconClick()" [title]="iconTitle"></my-global-icon>
diff --git a/client/src/app/shared/shared-main/misc/simple-search-input.component.scss b/client/src/app/shared/shared-main/misc/simple-search-input.component.scss
index d5fcff760..ee0f7a8d2 100644
--- a/client/src/app/shared/shared-main/misc/simple-search-input.component.scss
+++ b/client/src/app/shared/shared-main/misc/simple-search-input.component.scss
@@ -5,7 +5,7 @@
5 display: flex; 5 display: flex;
6} 6}
7 7
8my-global-icon { 8.root > my-global-icon {
9 @include margin-left(10px); 9 @include margin-left(10px);
10 10
11 height: 28px; 11 height: 28px;
@@ -25,3 +25,7 @@ input {
25 box-shadow: 0 0 5px 0 #a5a5a5; 25 box-shadow: 0 0 5px 0 #a5a5a5;
26 } 26 }
27} 27}
28
29.input-group > my-global-icon {
30 width: 20px;
31}
diff --git a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.html b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.html
index d884e75b2..d96fdbdc6 100644
--- a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.html
+++ b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.html
@@ -1,7 +1,13 @@
1<div class="sub-menu" [ngClass]="{ 'sub-menu-fixed': !isBroadcastMessageDisplayed, 'no-scroll': isModalOpened }"> 1<div class="sub-menu" [ngClass]="{ 'sub-menu-fixed': !isBroadcastMessageDisplayed, 'no-scroll': isModalOpened }">
2 <ng-container *ngFor="let menuEntry of menuEntries; index as id"> 2 <ng-container *ngFor="let menuEntry of menuEntries; index as id">
3 3
4 <a *ngIf="menuEntry.routerLink && isDisplayed(menuEntry)" [routerLink]="menuEntry.routerLink" routerLinkActive="active" class="title-page title-page-settings" #routerLink (click)="onActiveLinkScrollToTop(routerLink)">{{ menuEntry.label }}</a> 4 <a
5 *ngIf="menuEntry.routerLink && isDisplayed(menuEntry)" class="sub-menu-entry"
6 [routerLink]="menuEntry.routerLink" routerLinkActive="active" #routerLink
7 (click)="onActiveLinkScrollToTop(routerLink)"
8 >
9 {{ menuEntry.label }}
10 </a>
5 11
6 <div *ngIf="!menuEntry.routerLink && isDisplayed(menuEntry)" ngbDropdown class="parent-entry" 12 <div *ngIf="!menuEntry.routerLink && isDisplayed(menuEntry)" ngbDropdown class="parent-entry"
7 #dropdown="ngbDropdown" autoClose="true" container="body"> 13 #dropdown="ngbDropdown" autoClose="true" container="body">
@@ -10,7 +16,7 @@
10 tabindex=0 16 tabindex=0
11 [ngClass]="{ active: !!suffixLabels[menuEntry.label] }" 17 [ngClass]="{ active: !!suffixLabels[menuEntry.label] }"
12 (click)="openModal(id)" (keydown.enter)="openModal(id)" 18 (click)="openModal(id)" (keydown.enter)="openModal(id)"
13 role="button" class="title-page title-page-settings"> 19 role="button" class="sub-menu-entry">
14 <ng-container i18n>{{ menuEntry.label }}</ng-container> 20 <ng-container i18n>{{ menuEntry.label }}</ng-container>
15 </span> 21 </span>
16 22
@@ -19,7 +25,7 @@
19 tabindex=0 25 tabindex=0
20 [ngClass]="{ active: !!suffixLabels[menuEntry.label] }" ngbDropdownAnchor 26 [ngClass]="{ active: !!suffixLabels[menuEntry.label] }" ngbDropdownAnchor
21 (click)="dropdownAnchorClicked(dropdown)" (keydown.enter)="dropdownAnchorClicked(dropdown)" 27 (click)="dropdownAnchorClicked(dropdown)" (keydown.enter)="dropdownAnchorClicked(dropdown)"
22 role="button" class="title-page title-page-settings" 28 role="button" class="sub-menu-entry"
23 > 29 >
24 <ng-container i18n>{{ menuEntry.label }}</ng-container> 30 <ng-container i18n>{{ menuEntry.label }}</ng-container>
25 </span> 31 </span>
diff --git a/client/src/app/shared/shared-main/shared-main.module.ts b/client/src/app/shared/shared-main/shared-main.module.ts
index 5629640bc..89f43239f 100644
--- a/client/src/app/shared/shared-main/shared-main.module.ts
+++ b/client/src/app/shared/shared-main/shared-main.module.ts
@@ -34,7 +34,7 @@ import { ActionDropdownComponent, ButtonComponent, DeleteButtonComponent, EditBu
34import { CustomPageService } from './custom-page' 34import { CustomPageService } from './custom-page'
35import { DateToggleComponent } from './date' 35import { DateToggleComponent } from './date'
36import { FeedComponent } from './feeds' 36import { FeedComponent } from './feeds'
37import { LoaderComponent, SmallLoaderComponent } from './loaders' 37import { LoaderComponent } from './loaders'
38import { 38import {
39 ChannelsSetupMessageComponent, 39 ChannelsSetupMessageComponent,
40 HelpComponent, 40 HelpComponent,
@@ -97,7 +97,6 @@ import { VideoChannelService } from './video-channel'
97 FeedComponent, 97 FeedComponent,
98 98
99 LoaderComponent, 99 LoaderComponent,
100 SmallLoaderComponent,
101 100
102 ChannelsSetupMessageComponent, 101 ChannelsSetupMessageComponent,
103 HelpComponent, 102 HelpComponent,
@@ -157,7 +156,6 @@ import { VideoChannelService } from './video-channel'
157 FeedComponent, 156 FeedComponent,
158 157
159 LoaderComponent, 158 LoaderComponent,
160 SmallLoaderComponent,
161 159
162 ChannelsSetupMessageComponent, 160 ChannelsSetupMessageComponent,
163 HelpComponent, 161 HelpComponent,
diff --git a/client/src/app/shared/shared-main/users/user-quota.component.html b/client/src/app/shared/shared-main/users/user-quota.component.html
index dd1fc20d0..0e0d38c2a 100644
--- a/client/src/app/shared/shared-main/users/user-quota.component.html
+++ b/client/src/app/shared/shared-main/users/user-quota.component.html
@@ -12,7 +12,7 @@
12 <div *ngIf="hasDailyQuota()" class="mt-3"> 12 <div *ngIf="hasDailyQuota()" class="mt-3">
13 <label class="user-quota-title" tabindex="0" i18n>Daily video quota</label> 13 <label class="user-quota-title" tabindex="0" i18n>Daily video quota</label>
14 <div class="progress" tabindex="0" [ngbTooltip]="titleVideoQuotaDaily()"> 14 <div class="progress" tabindex="0" [ngbTooltip]="titleVideoQuotaDaily()">
15 <div class="progress-bar secondary" role="progressbar" [style]="{ width: userVideoQuotaDailyPercentage + '%' }" 15 <div class="progress-bar" role="progressbar" [style]="{ width: userVideoQuotaDailyPercentage + '%' }"
16 [attr.aria-valuenow]="userVideoQuotaUsedDaily" aria-valuemin="0" [attr.aria-valuemax]="user.videoQuotaDaily"></div> 16 [attr.aria-valuenow]="userVideoQuotaUsedDaily" aria-valuemin="0" [attr.aria-valuemax]="user.videoQuotaDaily"></div>
17 <span>{{ userVideoQuotaUsedDaily | bytes: 1 }}</span> 17 <span>{{ userVideoQuotaUsedDaily | bytes: 1 }}</span>
18 <span>{{ userVideoQuotaDaily }}</span> 18 <span>{{ userVideoQuotaDaily }}</span>
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 70571bde6..f3e86ce78 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
@@ -1,11 +1,6 @@
1@use '_variables' as *; 1@use '_variables' as *;
2@use '_mixins' as *; 2@use '_mixins' as *;
3 3
4label {
5 font-weight: $font-regular;
6 font-size: 100%;
7}
8
9.user-quota { 4.user-quota {
10 label { 5 label {
11 @include margin-right(5px); 6 @include margin-right(5px);
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 32376bf62..62bd94349 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
@@ -27,6 +27,7 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
27 videosCount?: number 27 videosCount?: number
28 28
29 viewsPerDay?: ViewsPerDate[] 29 viewsPerDay?: ViewsPerDate[]
30 totalViews?: number
30 31
31 static GET_ACTOR_AVATAR_URL (actor: { avatars: { width: number, url?: string, path: string }[] }, size: number) { 32 static GET_ACTOR_AVATAR_URL (actor: { avatars: { width: number, url?: string, path: string }[] }, size: number) {
32 return Actor.GET_ACTOR_AVATAR_URL(actor, size) 33 return Actor.GET_ACTOR_AVATAR_URL(actor, size)
@@ -74,6 +75,10 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
74 this.viewsPerDay = hash.viewsPerDay.map(v => ({ ...v, date: new Date(v.date) })) 75 this.viewsPerDay = hash.viewsPerDay.map(v => ({ ...v, date: new Date(v.date) }))
75 } 76 }
76 77
78 if (hash.totalViews !== null && hash.totalViews !== undefined) {
79 this.totalViews = hash.totalViews
80 }
81
77 if (hash.ownerAccount) { 82 if (hash.ownerAccount) {
78 this.ownerAccount = hash.ownerAccount 83 this.ownerAccount = hash.ownerAccount
79 this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host) 84 this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host)
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 022bb95ad..2e4ab87d7 100644
--- a/client/src/app/shared/shared-main/video/video.model.ts
+++ b/client/src/app/shared/shared-main/video/video.model.ts
@@ -1,6 +1,6 @@
1import { AuthUser } from '@app/core' 1import { AuthUser } from '@app/core'
2import { User } from '@app/core/users/user.model' 2import { User } from '@app/core/users/user.model'
3import { durationToString, getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers' 3import { durationToString, prepareIcu, getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
4import { Actor } from '@app/shared/shared-main/account/actor.model' 4import { Actor } from '@app/shared/shared-main/account/actor.model'
5import { buildVideoWatchPath } from '@shared/core-utils' 5import { buildVideoWatchPath } from '@shared/core-utils'
6import { peertubeTranslate } from '@shared/core-utils/i18n' 6import { peertubeTranslate } from '@shared/core-utils/i18n'
@@ -19,6 +19,9 @@ import {
19} from '@shared/models' 19} from '@shared/models'
20 20
21export class Video implements VideoServerModel { 21export class Video implements VideoServerModel {
22 private static readonly viewsICU = prepareIcu($localize`{views, plural, =0 {No view} =1 {1 view} other {{views} views}}`)
23 private static readonly viewersICU = prepareIcu($localize`{viewers, plural, =0 {No viewers} =1 {1 viewer} other {{viewers} viewers}}`)
24
22 byVideoChannel: string 25 byVideoChannel: string
23 byAccount: string 26 byAccount: string
24 27
@@ -269,12 +272,10 @@ export class Video implements VideoServerModel {
269 } 272 }
270 273
271 getExactNumberOfViews () { 274 getExactNumberOfViews () {
272 if (this.views < 1000) return ''
273
274 if (this.isLive) { 275 if (this.isLive) {
275 return $localize`${this.views} viewers` 276 return Video.viewersICU({ viewers: this.viewers }, $localize`${this.viewers} viewer(s)`)
276 } 277 }
277 278
278 return $localize`${this.views} views` 279 return Video.viewsICU({ views: this.views }, $localize`{${this.views} view(s)}`)
279 } 280 }
280} 281}
diff --git a/client/src/app/shared/shared-moderation/account-block-badges.component.html b/client/src/app/shared/shared-moderation/account-block-badges.component.html
index feac707c2..fd3709676 100644
--- a/client/src/app/shared/shared-moderation/account-block-badges.component.html
+++ b/client/src/app/shared/shared-moderation/account-block-badges.component.html
@@ -1,4 +1,4 @@
1<span *ngIf="account.mutedByUser" class="badge badge-danger" i18n>Muted</span> 1<span *ngIf="account.mutedByUser" class="pt-badge badge-danger" i18n>Muted</span>
2<span *ngIf="account.mutedServerByUser" class="badge badge-danger" i18n>Instance muted</span> 2<span *ngIf="account.mutedServerByUser" class="pt-badge badge-danger" i18n>Instance muted</span>
3<span *ngIf="account.mutedByInstance" class="badge badge-danger" i18n>Muted by your instance</span> 3<span *ngIf="account.mutedByInstance" class="pt-badge badge-danger" i18n>Muted by your instance</span>
4<span *ngIf="account.mutedServerByInstance" class="badge badge-danger" i18n>Instance muted by your instance</span> 4<span *ngIf="account.mutedServerByInstance" class="pt-badge badge-danger" i18n>Instance muted by your instance</span>
diff --git a/client/src/app/shared/shared-moderation/account-block-badges.component.scss b/client/src/app/shared/shared-moderation/account-block-badges.component.scss
index ccc3666aa..301d8305e 100644
--- a/client/src/app/shared/shared-moderation/account-block-badges.component.scss
+++ b/client/src/app/shared/shared-moderation/account-block-badges.component.scss
@@ -1,9 +1,8 @@
1@use '_variables' as *; 1@use '_variables' as *;
2@use '_mixins' as *; 2@use '_mixins' as *;
3 3
4.badge { 4.pt-badge {
5 @include margin-right(10px); 5 @include margin-right(10px);
6 6
7 height: fit-content;
8 font-size: 12px; 7 font-size: 12px;
9} 8}
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 a4f81d824..131110318 100644
--- a/client/src/app/shared/shared-moderation/account-blocklist.component.html
+++ b/client/src/app/shared/shared-moderation/account-blocklist.component.html
@@ -11,7 +11,7 @@
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"> 14 <div class="ms-auto">
15 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> 15 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
16 </div> 16 </div>
17 </div> 17 </div>
@@ -48,7 +48,7 @@
48 48
49 <ng-template pTemplate="emptymessage"> 49 <ng-template pTemplate="emptymessage">
50 <tr> 50 <tr>
51 <td colspan="6"> 51 <td colspan="3">
52 <div class="no-results"> 52 <div class="no-results">
53 <ng-container *ngIf="search" i18n>No account found matching current filters.</ng-container> 53 <ng-container *ngIf="search" i18n>No account found matching current filters.</ng-container>
54 <ng-container *ngIf="!search" i18n>No account found.</ng-container> 54 <ng-container *ngIf="!search" i18n>No account found.</ng-container>
diff --git a/client/src/app/shared/shared-moderation/report-modals/report.component.html b/client/src/app/shared/shared-moderation/report-modals/report.component.html
index 6c99180ef..8e0b0993c 100644
--- a/client/src/app/shared/shared-moderation/report-modals/report.component.html
+++ b/client/src/app/shared/shared-moderation/report-modals/report.component.html
@@ -8,11 +8,11 @@
8 <form novalidate [formGroup]="form" (ngSubmit)="report()"> 8 <form novalidate [formGroup]="form" (ngSubmit)="report()">
9 9
10 <div class="row"> 10 <div class="row">
11 <div class="col-5 form-group"> 11 <div class="col-5">
12 12
13 <label i18n for="reportPredefinedReasons">What is the issue?</label> 13 <label i18n for="reportPredefinedReasons">What is the issue?</label>
14 14
15 <div class="ml-2 mt-2 d-flex flex-column"> 15 <div class="ms-2 mt-2 d-flex flex-column">
16 <ng-container formGroupName="predefinedReasons"> 16 <ng-container formGroupName="predefinedReasons">
17 17
18 <div class="form-group" *ngFor="let reason of predefinedReasons"> 18 <div class="form-group" *ngFor="let reason of predefinedReasons">
@@ -29,7 +29,6 @@
29 29
30 </ng-container> 30 </ng-container>
31 </div> 31 </div>
32
33 </div> 32 </div>
34 33
35 <div class="col-7"> 34 <div class="col-7">
diff --git a/client/src/app/shared/shared-moderation/report-modals/video-report.component.html b/client/src/app/shared/shared-moderation/report-modals/video-report.component.html
index afac108fc..51ca0b9d6 100644
--- a/client/src/app/shared/shared-moderation/report-modals/video-report.component.html
+++ b/client/src/app/shared/shared-moderation/report-modals/video-report.component.html
@@ -8,38 +8,32 @@
8 <form novalidate [formGroup]="form" (ngSubmit)="report()"> 8 <form novalidate [formGroup]="form" (ngSubmit)="report()">
9 9
10 <div class="row"> 10 <div class="row">
11 <div class="col-5 form-group"> 11 <div class="col-12 col-md-5">
12 <label i18n for="reportPredefinedReasons">What is the issue?</label>
12 13
13 <label i18n for="reportPredefinedReasons">What is the issue?</label> 14 <div class="ms-2 mt-2 d-flex flex-column">
15 <ng-container formGroupName="predefinedReasons">
14 16
15 <div class="ml-2 mt-2 d-flex flex-column"> 17 <div class="form-group" *ngFor="let reason of predefinedReasons">
16 <ng-container formGroupName="predefinedReasons"> 18 <my-peertube-checkbox [inputName]="reason.id" [formControlName]="reason.id" [labelText]="reason.label">
19 <ng-template *ngIf="reason.help" ptTemplate="help">
20 <div [innerHTML]="reason.help"></div>
21 </ng-template>
17 22
18 <div class="form-group" *ngFor="let reason of predefinedReasons"> 23 <ng-container *ngIf="reason.description" ngProjectAs="description">
19 <my-peertube-checkbox [inputName]="reason.id" [formControlName]="reason.id" [labelText]="reason.label"> 24 <div [innerHTML]="reason.description"></div>
20 <ng-template *ngIf="reason.help" ptTemplate="help"> 25 </ng-container>
21 <div [innerHTML]="reason.help"></div> 26 </my-peertube-checkbox>
22 </ng-template> 27 </div>
23
24 <ng-container *ngIf="reason.description" ngProjectAs="description">
25 <div [innerHTML]="reason.description"></div>
26 </ng-container>
27 </my-peertube-checkbox>
28 </div>
29
30 </ng-container>
31 </div>
32 28
29 </ng-container>
30 </div>
33 </div> 31 </div>
34 32
35 <div class="col-7"> 33 <div class="col-12 col-md-7">
36 <div class="row justify-content-center"> 34 <my-embed [video]="video"></my-embed>
37 <div class="col-12 col-lg-9 mb-2">
38 <my-embed [video]="video"></my-embed>
39 </div>
40 </div>
41 35
42 <div class="mb-1 start-at" formGroupName="timestamp"> 36 <div class="mb-1 mt-3 start-at" formGroupName="timestamp">
43 <my-peertube-checkbox 37 <my-peertube-checkbox
44 formControlName="hasStart" 38 formControlName="hasStart"
45 i18n-labelText labelText="Start at" 39 i18n-labelText labelText="Start at"
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 1a320e9a4..bc47bf26f 100644
--- a/client/src/app/shared/shared-moderation/server-blocklist.component.html
+++ b/client/src/app/shared/shared-moderation/server-blocklist.component.html
@@ -19,7 +19,7 @@
19 </a> 19 </a>
20 </div> 20 </div>
21 21
22 <div class="ml-auto"> 22 <div class="ms-auto">
23 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> 23 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
24 </div> 24 </div>
25 </div> 25 </div>
@@ -41,7 +41,7 @@
41 <td> 41 <td>
42 <a [href]="'https://' + serverBlock.blockedServer.host" i18n-title title="Open instance in a new tab" target="_blank" rel="noopener noreferrer"> 42 <a [href]="'https://' + serverBlock.blockedServer.host" i18n-title title="Open instance in a new tab" target="_blank" rel="noopener noreferrer">
43 {{ serverBlock.blockedServer.host }} 43 {{ serverBlock.blockedServer.host }}
44 <span class="glyphicon glyphicon-new-window"></span> 44 <my-global-icon iconName="external-link"></my-global-icon>
45 </a> 45 </a>
46 </td> 46 </td>
47 <td>{{ serverBlock.createdAt | date: 'short' }}</td> 47 <td>{{ serverBlock.createdAt | date: 'short' }}</td>
@@ -50,7 +50,7 @@
50 50
51 <ng-template pTemplate="emptymessage"> 51 <ng-template pTemplate="emptymessage">
52 <tr> 52 <tr>
53 <td colspan="6"> 53 <td colspan="3">
54 <div class="no-results"> 54 <div class="no-results">
55 <ng-container *ngIf="search" i18n>No server found matching current filters.</ng-container> 55 <ng-container *ngIf="search" i18n>No server found matching current filters.</ng-container>
56 <ng-container *ngIf="!search" i18n>No server found.</ng-container> 56 <ng-container *ngIf="!search" i18n>No server found.</ng-container>
diff --git a/client/src/app/shared/shared-moderation/user-ban-modal.component.scss b/client/src/app/shared/shared-moderation/user-ban-modal.component.scss
index 2c46c3d03..376fb1693 100644
--- a/client/src/app/shared/shared-moderation/user-ban-modal.component.scss
+++ b/client/src/app/shared/shared-moderation/user-ban-modal.component.scss
@@ -2,7 +2,6 @@
2@use '_mixins' as *; 2@use '_mixins' as *;
3 3
4.description { 4.description {
5 font-size: 15px;
6 margin-bottom: 15px; 5 margin-bottom: 15px;
7} 6}
8 7
diff --git a/client/src/app/shared/shared-moderation/user-ban-modal.component.ts b/client/src/app/shared/shared-moderation/user-ban-modal.component.ts
index 9edfac388..617408f2a 100644
--- a/client/src/app/shared/shared-moderation/user-ban-modal.component.ts
+++ b/client/src/app/shared/shared-moderation/user-ban-modal.component.ts
@@ -1,6 +1,7 @@
1import { forkJoin } from 'rxjs' 1import { forkJoin } from 'rxjs'
2import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' 2import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
3import { Notifier } from '@app/core' 3import { Notifier } from '@app/core'
4import { prepareIcu } from '@app/helpers'
4import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 5import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
5import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 6import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
6import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 7import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
@@ -63,9 +64,16 @@ export class UserBanModalComponent extends FormReactive implements OnInit {
63 forkJoin(observables) 64 forkJoin(observables)
64 .subscribe({ 65 .subscribe({
65 next: () => { 66 next: () => {
66 const message = Array.isArray(this.usersToBan) 67 let message: string
67 ? $localize`${this.usersToBan.length} users banned.` 68
68 : $localize`User ${this.usersToBan.username} banned.` 69 if (Array.isArray(this.usersToBan)) {
70 message = prepareIcu($localize`{count, plural, =1 {1 user banned.} other {{count} users banned.}}`)(
71 { count: this.usersToBan.length },
72 $localize`${this.usersToBan.length} users banned.`
73 )
74 } else {
75 message = $localize`User ${this.usersToBan.username} banned.`
76 }
69 77
70 this.notifier.success(message) 78 this.notifier.success(message)
71 79
@@ -79,7 +87,12 @@ export class UserBanModalComponent extends FormReactive implements OnInit {
79 } 87 }
80 88
81 getModalTitle () { 89 getModalTitle () {
82 if (Array.isArray(this.usersToBan)) return $localize`Ban ${this.usersToBan.length} users` 90 if (Array.isArray(this.usersToBan)) {
91 return prepareIcu($localize`Ban {count, plural, =1 {1 user} other {{count} users}}`)(
92 { count: this.usersToBan.length },
93 $localize`Ban ${this.usersToBan.length} users`
94 )
95 }
83 96
84 return $localize`Ban "${this.usersToBan.username}"` 97 return $localize`Ban "${this.usersToBan.username}"`
85 } 98 }
diff --git a/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts b/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts
index 787318c2c..c69a45c25 100644
--- a/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts
+++ b/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts
@@ -100,7 +100,8 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
100 return 100 return
101 } 101 }
102 102
103 const message = $localize`If you remove user ${user.username}, you won't be able to create another with the same username!` 103 // eslint-disable-next-line max-len
104 const message = $localize`If you remove this user, you won't be able to create another user or channel with <strong>${user.username}</strong> username!`
104 const res = await this.confirmService.confirm(message, $localize`Delete ${user.username}`) 105 const res = await this.confirmService.confirm(message, $localize`Delete ${user.username}`)
105 if (res === false) return 106 if (res === false) return
106 107
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 3061bbf15..7726eca11 100644
--- a/client/src/app/shared/shared-moderation/video-block.component.scss
+++ b/client/src/app/shared/shared-moderation/video-block.component.scss
@@ -6,6 +6,5 @@ textarea {
6} 6}
7 7
8.live-info { 8.live-info {
9 font-size: 15px;
10 margin: 40px 0 20px; 9 margin: 40px 0 20px;
11} 10}
diff --git a/client/src/app/shared/shared-moderation/video-block.component.ts b/client/src/app/shared/shared-moderation/video-block.component.ts
index 400913f02..f8b22a3f6 100644
--- a/client/src/app/shared/shared-moderation/video-block.component.ts
+++ b/client/src/app/shared/shared-moderation/video-block.component.ts
@@ -1,5 +1,6 @@
1import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' 1import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
2import { Notifier } from '@app/core' 2import { Notifier } from '@app/core'
3import { prepareIcu } from '@app/helpers'
3import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 4import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
4import { Video } from '@app/shared/shared-main' 5import { Video } from '@app/shared/shared-main'
5import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 6import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
@@ -80,9 +81,10 @@ export class VideoBlockComponent extends FormReactive implements OnInit {
80 this.videoBlocklistService.blockVideo(options) 81 this.videoBlocklistService.blockVideo(options)
81 .subscribe({ 82 .subscribe({
82 next: () => { 83 next: () => {
83 const message = this.isMultiple 84 const message = prepareIcu($localize`{count, plural, =1 {Blocked {videoName}.} other {Blocked {count} videos.}}`)(
84 ? $localize`Blocked ${this.videos.length} videos.` 85 { count: this.videos.length, videoName: this.getSingleVideo().name },
85 : $localize`Blocked ${this.getSingleVideo().name}` 86 $localize`Blocked ${this.videos.length} videos.`
87 )
86 88
87 this.notifier.success(message) 89 this.notifier.success(message)
88 this.hide() 90 this.hide()
diff --git a/client/src/app/shared/shared-share-modal/video-share.component.html b/client/src/app/shared/shared-share-modal/video-share.component.html
index 67ca56516..b163d3581 100644
--- a/client/src/app/shared/shared-share-modal/video-share.component.html
+++ b/client/src/app/shared/shared-share-modal/video-share.component.html
@@ -8,7 +8,7 @@
8 <div class="modal-body"> 8 <div class="modal-body">
9 9
10 <div class="playlist" *ngIf="playlist"> 10 <div class="playlist" *ngIf="playlist">
11 <div class="title-page title-page-single" i18n *ngIf="video">Share the playlist</div> 11 <h5 i18n *ngIf="video">Share the playlist</h5>
12 12
13 <div *ngIf="isPrivatePlaylist()" class="alert-private alert alert-warning"> 13 <div *ngIf="isPrivatePlaylist()" class="alert-private alert alert-warning">
14 <div i18n>This playlist is private so you won't be able to share it with external users</div> 14 <div i18n>This playlist is private so you won't be able to share it with external users</div>
@@ -25,8 +25,7 @@
25 25
26 <ng-template ngbNavContent> 26 <ng-template ngbNavContent>
27 <div class="nav-content"> 27 <div class="nav-content">
28 28 <my-input-text [value]="getPlaylistUrl()" [withToggle]="false" [withCopy]="true" [show]="true" [readonly]="true"></my-input-text>
29 <my-input-toggle-hidden [value]="getPlaylistUrl()" [withToggle]="false" [withCopy]="true" [show]="true" [readonly]="true"></my-input-toggle-hidden>
30 </div> 29 </div>
31 </ng-template> 30 </ng-template>
32 </ng-container> 31 </ng-container>
@@ -46,10 +45,10 @@
46 45
47 <ng-template ngbNavContent> 46 <ng-template ngbNavContent>
48 <div class="nav-content"> 47 <div class="nav-content">
49 <my-input-toggle-hidden 48 <my-input-text
50 [value]="customizations.onlyEmbedUrl ? getPlaylistEmbedUrl() : getPlaylistIframeCode()" (change)="updateEmbedCode()" 49 [value]="customizations.onlyEmbedUrl ? getPlaylistEmbedUrl() : getPlaylistIframeCode()" (change)="updateEmbedCode()"
51 [withToggle]="false" [withCopy]="true" [show]="true" [readonly]="true" 50 [withToggle]="false" [withCopy]="true" [show]="true" [readonly]="true"
52 ></my-input-toggle-hidden> 51 ></my-input-text>
53 52
54 <div i18n *ngIf="notSecure()" class="alert alert-warning"> 53 <div i18n *ngIf="notSecure()" class="alert alert-warning">
55 The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites). 54 The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites).
@@ -86,7 +85,7 @@
86 85
87 86
88 <div class="video" *ngIf="video"> 87 <div class="video" *ngIf="video">
89 <div class="title-page title-page-single" *ngIf="playlist" i18n>Share the video</div> 88 <h5 *ngIf="playlist" i18n>Share the video</h5>
90 89
91 <div *ngIf="isPrivateVideo()" class="alert-private alert alert-warning"> 90 <div *ngIf="isPrivateVideo()" class="alert-private alert alert-warning">
92 <div i18n>This video is private so you won't be able to share it with external users</div> 91 <div i18n>This video is private so you won't be able to share it with external users</div>
@@ -103,7 +102,7 @@
103 102
104 <ng-template ngbNavContent> 103 <ng-template ngbNavContent>
105 <div class="nav-content"> 104 <div class="nav-content">
106 <my-input-toggle-hidden [value]="getVideoUrl()" [withToggle]="false" [withCopy]="true" [show]="true" [readonly]="true"></my-input-toggle-hidden> 105 <my-input-text [value]="getVideoUrl()" [withToggle]="false" [withCopy]="true" [show]="true" [readonly]="true"></my-input-text>
107 </div> 106 </div>
108 </ng-template> 107 </ng-template>
109 </ng-container> 108 </ng-container>
@@ -123,10 +122,10 @@
123 122
124 <ng-template ngbNavContent> 123 <ng-template ngbNavContent>
125 <div class="nav-content"> 124 <div class="nav-content">
126 <my-input-toggle-hidden 125 <my-input-text
127 [value]="customizations.onlyEmbedUrl ? getVideoEmbedUrl() : getVideoIframeCode()" (ngModelChange)="updateEmbedCode()" 126 [value]="customizations.onlyEmbedUrl ? getVideoEmbedUrl() : getVideoIframeCode()" (ngModelChange)="updateEmbedCode()"
128 [withToggle]="false" [withCopy]="true" [show]="true" [readonly]="true" 127 [withToggle]="false" [withCopy]="true" [show]="true" [readonly]="true"
129 ></my-input-toggle-hidden> 128 ></my-input-text>
130 129
131 <div i18n *ngIf="notSecure()" class="alert alert-warning"> 130 <div i18n *ngIf="notSecure()" class="alert alert-warning">
132 The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites). 131 The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites).
@@ -171,9 +170,8 @@
171 </div> 170 </div>
172 </div> 171 </div>
173 172
174 <div class="form-group"> 173 <div class="form-group" *ngIf="isInVideoEmbedTab()">
175 <my-peertube-checkbox 174 <my-peertube-checkbox
176 *ngIf="isInVideoEmbedTab()"
177 inputName="onlyEmbedUrl" [(ngModel)]="customizations.onlyEmbedUrl" 175 inputName="onlyEmbedUrl" [(ngModel)]="customizations.onlyEmbedUrl"
178 i18n-labelText labelText="Only display embed URL" 176 i18n-labelText labelText="Only display embed URL"
179 ></my-peertube-checkbox> 177 ></my-peertube-checkbox>
@@ -268,7 +266,7 @@
268 [attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic"> 266 [attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic">
269 267
270 <ng-container *ngIf="isAdvancedCustomizationCollapsed"> 268 <ng-container *ngIf="isAdvancedCustomizationCollapsed">
271 <span class="glyphicon glyphicon-menu-down"></span> 269 <span class="chevron-down"></span>
272 270
273 <ng-container i18n> 271 <ng-container i18n>
274 More customization 272 More customization
@@ -276,7 +274,7 @@
276 </ng-container> 274 </ng-container>
277 275
278 <ng-container *ngIf="!isAdvancedCustomizationCollapsed"> 276 <ng-container *ngIf="!isAdvancedCustomizationCollapsed">
279 <span class="glyphicon glyphicon-menu-up"></span> 277 <span class="chevron-up"></span>
280 278
281 <ng-container i18n> 279 <ng-container i18n>
282 Less customization 280 Less customization
diff --git a/client/src/app/shared/shared-share-modal/video-share.component.scss b/client/src/app/shared/shared-share-modal/video-share.component.scss
index 44ebb13f4..6e80f8c76 100644
--- a/client/src/app/shared/shared-share-modal/video-share.component.scss
+++ b/client/src/app/shared/shared-share-modal/video-share.component.scss
@@ -1,14 +1,10 @@
1@use '_mixins' as *; 1@use '_mixins' as *;
2@use '_variables' as *; 2@use '_variables' as *;
3 3
4my-input-toggle-hidden { 4my-input-text {
5 width: 100%; 5 width: 100%;
6} 6}
7 7
8.title-page.title-page-single {
9 margin-top: 0;
10}
11
12.playlist { 8.playlist {
13 margin-bottom: 50px; 9 margin-bottom: 50px;
14} 10}
@@ -34,6 +30,10 @@ my-input-toggle-hidden {
34 margin-top: 20px; 30 margin-top: 20px;
35} 31}
36 32
33.alert-private {
34 margin-top: 0;
35}
36
37.filters { 37.filters {
38 margin-top: 30px; 38 margin-top: 30px;
39 39
@@ -46,20 +46,8 @@ my-input-toggle-hidden {
46 justify-content: center; 46 justify-content: center;
47 align-items: center; 47 align-items: center;
48 margin-top: 20px; 48 margin-top: 20px;
49 font-size: 16px;
50 font-weight: $font-semibold; 49 font-weight: $font-semibold;
51 cursor: pointer; 50 cursor: pointer;
52
53 .glyphicon {
54 @include margin-right(5px);
55 }
56 }
57
58 .form-group {
59 margin-bottom: 0;
60 height: 34px;
61 display: flex;
62 align-items: center;
63 } 51 }
64 52
65 .video-caption-block { 53 .video-caption-block {
@@ -88,3 +76,7 @@ my-input-toggle-hidden {
88 align-items: center; 76 align-items: center;
89 justify-content: space-between; 77 justify-content: space-between;
90} 78}
79
80h5 {
81 font-size: 1.15rem;
82}
diff --git a/client/src/app/shared/shared-tables/table-expander-icon.component.ts b/client/src/app/shared/shared-tables/table-expander-icon.component.ts
index 3756b475a..66bbfe6fb 100644
--- a/client/src/app/shared/shared-tables/table-expander-icon.component.ts
+++ b/client/src/app/shared/shared-tables/table-expander-icon.component.ts
@@ -4,7 +4,7 @@ import { Component, Input } from '@angular/core'
4 selector: 'my-table-expander-icon', 4 selector: 'my-table-expander-icon',
5 template: ` 5 template: `
6<span class="expander"> 6<span class="expander">
7 <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i> 7 <i [ngClass]="expanded ? 'chevron-down' : 'chevron-right'"></i>
8</span>` 8</span>`
9}) 9})
10export class TableExpanderIconComponent { 10export class TableExpanderIconComponent {
diff --git a/client/src/app/shared/shared-tables/video-cell.component.scss b/client/src/app/shared/shared-tables/video-cell.component.scss
index 7efb61502..5d26b02ef 100644
--- a/client/src/app/shared/shared-tables/video-cell.component.scss
+++ b/client/src/app/shared/shared-tables/video-cell.component.scss
@@ -59,13 +59,6 @@
59 color: pvar(--mainForegroundColor); 59 color: pvar(--mainForegroundColor);
60 line-height: 1rem; 60 line-height: 1rem;
61 61
62 div .glyphicon {
63 @include margin-left(0.1rem);
64
65 font-size: 80%;
66 color: #808080;
67 }
68
69 div + div { 62 div + div {
70 color: var(--greyForegroundColor); 63 color: var(--greyForegroundColor);
71 font-size: 11px; 64 font-size: 11px;
diff --git a/client/src/app/shared/shared-user-settings/user-interface-settings.component.html b/client/src/app/shared/shared-user-settings/user-interface-settings.component.html
index b739e881b..1a24641a2 100644
--- a/client/src/app/shared/shared-user-settings/user-interface-settings.component.html
+++ b/client/src/app/shared/shared-user-settings/user-interface-settings.component.html
@@ -5,10 +5,10 @@
5 5
6 <div class="peertube-select-container"> 6 <div class="peertube-select-container">
7 <select formControlName="theme" id="theme" class="form-control"> 7 <select formControlName="theme" id="theme" class="form-control">
8 <option i18n value="instance-default">Instance default theme ({{ getDefaultThemeLabel() }})</option> 8 <option i18n value="instance-default">{{ instanceName }} default theme ({{ getDefaultInstanceThemeLabel() }})</option>
9 <option i18n value="default">{{ defaultThemeLabel }}</option> 9 <option i18n value="default">{{ getDefaultThemeLabel() }}</option>
10 10
11 <option *ngFor="let theme of availableThemes" [value]="theme">{{ capitalizeFirstLetter(theme) }}</option> 11 <option *ngFor="let theme of getAvailableThemes()" [value]="theme">{{ theme }}</option>
12 </select> 12 </select>
13 </div> 13 </div>
14 </div> 14 </div>
diff --git a/client/src/app/shared/shared-user-settings/user-interface-settings.component.scss b/client/src/app/shared/shared-user-settings/user-interface-settings.component.scss
index 2fc245ace..da8202594 100644
--- a/client/src/app/shared/shared-user-settings/user-interface-settings.component.scss
+++ b/client/src/app/shared/shared-user-settings/user-interface-settings.component.scss
@@ -1,11 +1,6 @@
1@use '_variables' as *; 1@use '_variables' as *;
2@use '_mixins' as *; 2@use '_mixins' as *;
3 3
4label {
5 font-weight: $font-regular;
6 font-size: 100%;
7}
8
9input[type=submit] { 4input[type=submit] {
10 @include peertube-button; 5 @include peertube-button;
11 @include orange-button; 6 @include orange-button;
diff --git a/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts b/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts
index 932db498a..24ad54b1d 100644
--- a/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts
+++ b/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts
@@ -1,8 +1,7 @@
1import { Subject, Subscription } from 'rxjs' 1import { Subject, Subscription } from 'rxjs'
2import { Component, Input, OnDestroy, OnInit } from '@angular/core' 2import { Component, Input, OnDestroy, OnInit } from '@angular/core'
3import { AuthService, Notifier, ServerService, UserService } from '@app/core' 3import { AuthService, Notifier, ServerService, ThemeService, UserService } from '@app/core'
4import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 4import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
5import { capitalizeFirstLetter } from '@root-helpers/string'
6import { HTMLServerConfig, User, UserUpdateMe } from '@shared/models' 5import { HTMLServerConfig, User, UserUpdateMe } from '@shared/models'
7 6
8@Component({ 7@Component({
@@ -18,8 +17,6 @@ export class UserInterfaceSettingsComponent extends FormReactive implements OnIn
18 17
19 formValuesWatcher: Subscription 18 formValuesWatcher: Subscription
20 19
21 defaultThemeLabel = $localize`Light/Orange`
22
23 private serverConfig: HTMLServerConfig 20 private serverConfig: HTMLServerConfig
24 21
25 constructor ( 22 constructor (
@@ -27,14 +24,14 @@ export class UserInterfaceSettingsComponent extends FormReactive implements OnIn
27 private authService: AuthService, 24 private authService: AuthService,
28 private notifier: Notifier, 25 private notifier: Notifier,
29 private userService: UserService, 26 private userService: UserService,
27 private themeService: ThemeService,
30 private serverService: ServerService 28 private serverService: ServerService
31 ) { 29 ) {
32 super() 30 super()
33 } 31 }
34 32
35 get availableThemes () { 33 get instanceName () {
36 return this.serverConfig.theme.registered 34 return this.serverConfig.instance.name
37 .map(t => t.name)
38 } 35 }
39 36
40 ngOnInit () { 37 ngOnInit () {
@@ -61,17 +58,23 @@ export class UserInterfaceSettingsComponent extends FormReactive implements OnIn
61 } 58 }
62 59
63 getDefaultThemeLabel () { 60 getDefaultThemeLabel () {
61 return this.themeService.getDefaultThemeLabel()
62 }
63
64 getAvailableThemes () {
65 return this.themeService.getAvailableThemeLabels()
66 }
67
68 getDefaultInstanceThemeLabel () {
64 const theme = this.serverConfig.theme.default 69 const theme = this.serverConfig.theme.default
65 70
66 if (theme === 'default') return this.defaultThemeLabel 71 if (theme === 'default') {
72 return this.getDefaultThemeLabel()
73 }
67 74
68 return theme 75 return theme
69 } 76 }
70 77
71 capitalizeFirstLetter (str: string) {
72 return capitalizeFirstLetter(str)
73 }
74
75 updateInterfaceSettings () { 78 updateInterfaceSettings () {
76 const theme = this.form.value['theme'] 79 const theme = this.form.value['theme']
77 80
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 446ade445..85b27a4ff 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
@@ -1,5 +1,5 @@
1<form role="form" (ngSubmit)="updateDetails()" [formGroup]="form"> 1<form role="form" (ngSubmit)="updateDetails()" [formGroup]="form">
2 <div class="form-group form-group-select"> 2 <div class="form-group">
3 <div class="anchor" id="video-sensitive-content-policy"></div> <!-- video-sensitive-content-policy anchor --> 3 <div class="anchor" id="video-sensitive-content-policy"></div> <!-- video-sensitive-content-policy anchor -->
4 <label i18n for="nsfwPolicy">Default policy on videos containing sensitive content</label> 4 <label i18n for="nsfwPolicy">Default policy on videos containing sensitive content</label>
5 <my-help> 5 <my-help>
@@ -20,7 +20,7 @@
20 </div> 20 </div>
21 </div> 21 </div>
22 22
23 <div class="form-group form-group-select"> 23 <div class="form-group">
24 <div class="anchor" id="video-languages-subtitles"></div> <!-- video-languages-subtitles anchor --> 24 <div class="anchor" id="video-languages-subtitles"></div> <!-- video-languages-subtitles anchor -->
25 <label i18n for="videoLanguages">Only display videos in the following languages/subtitles</label> 25 <label i18n for="videoLanguages">Only display videos in the following languages/subtitles</label>
26 <my-help> 26 <my-help>
@@ -30,7 +30,7 @@
30 </my-help> 30 </my-help>
31 31
32 <div> 32 <div>
33 <my-select-languages formControlName="videoLanguages"></my-select-languages> 33 <my-select-languages [maxLanguages]="20" formControlName="videoLanguages"></my-select-languages>
34 </div> 34 </div>
35 </div> 35 </div>
36 36
@@ -42,7 +42,7 @@
42 i18n-labelText labelText="Help share videos being played" 42 i18n-labelText labelText="Help share videos being played"
43 > 43 >
44 <ng-container ngProjectAs="description"> 44 <ng-container ngProjectAs="description">
45 <span i18n>The <a routerLink="/about/peertube" fragment="privacy" target="_blank">sharing system</a> implies that some technical information about your system (such as a public IP address) can be sent to other peers, but greatly helps to reduce server load.</span> 45 <span i18n>The <a class="link-orange" routerLink="/about/peertube" fragment="privacy" target="_blank">sharing system</a> implies that some technical information about your system (such as a public IP address) can be sent to other peers, but greatly helps to reduce server load.</span>
46 </ng-container> 46 </ng-container>
47 </my-peertube-checkbox> 47 </my-peertube-checkbox>
48 </div> 48 </div>
diff --git a/client/src/app/shared/shared-user-settings/user-video-settings.component.scss b/client/src/app/shared/shared-user-settings/user-video-settings.component.scss
index c4f6020d4..163c899d3 100644
--- a/client/src/app/shared/shared-user-settings/user-video-settings.component.scss
+++ b/client/src/app/shared/shared-user-settings/user-video-settings.component.scss
@@ -1,11 +1,6 @@
1@use '_variables' as *; 1@use '_variables' as *;
2@use '_mixins' as *; 2@use '_mixins' as *;
3 3
4label {
5 font-weight: $font-regular;
6 font-size: 100%;
7}
8
9input[type=submit] { 4input[type=submit] {
10 @include peertube-button; 5 @include peertube-button;
11 @include orange-button; 6 @include orange-button;
@@ -15,8 +10,6 @@ input[type=submit] {
15 10
16.peertube-select-container { 11.peertube-select-container {
17 @include peertube-select-container(340px); 12 @include peertube-select-container(340px);
18
19 margin-bottom: 30px;
20} 13}
21 14
22my-select-languages { 15my-select-languages {
@@ -24,7 +17,3 @@ my-select-languages {
24 17
25 display: block; 18 display: block;
26} 19}
27
28.form-group-select {
29 margin-bottom: 30px;
30}
diff --git a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.html b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.html
index a00c3d1c7..656d1beb3 100644
--- a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.html
+++ b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.html
@@ -1,5 +1,5 @@
1<form novalidate [formGroup]="form" (ngSubmit)="formValidated()"> 1<form novalidate [formGroup]="form" (ngSubmit)="formValidated()">
2 <div class="form-group mb-2"> 2 <div class="form-group">
3 <input type="email" 3 <input type="email"
4 formControlName="text" 4 formControlName="text"
5 class="form-control" 5 class="form-control"
diff --git a/client/src/app/shared/shared-user-subscription/subscribe-button.component.html b/client/src/app/shared/shared-user-subscription/subscribe-button.component.html
index a6d851315..0e09c2697 100644
--- a/client/src/app/shared/shared-user-subscription/subscribe-button.component.html
+++ b/client/src/app/shared/shared-user-subscription/subscribe-button.component.html
@@ -1,5 +1,5 @@
1<div 1<div
2 class="btn-group-subscribe btn-group" 2 class="btn-group-subscribe btn-group" role="group"
3 [ngClass]="{'subscribe-button': !isAllChannelsSubscribed, 'unsubscribe-button': isAllChannelsSubscribed, 'big': isBigButton }" 3 [ngClass]="{'subscribe-button': !isAllChannelsSubscribed, 'unsubscribe-button': isAllChannelsSubscribed, 'big': isBigButton }"
4> 4>
5 5
@@ -20,17 +20,11 @@
20 </ng-template> 20 </ng-template>
21 21
22 <ng-template #userLoggedIn> 22 <ng-template #userLoggedIn>
23 <button 23 <button *ngIf="!isAllChannelsSubscribed" type="button" class="btn" (click)="subscribe()">
24 *ngIf="!isAllChannelsSubscribed" type="button"
25 class="btn btn-sm" (click)="subscribe()"
26 >
27 <ng-template [ngTemplateOutlet]="userLoggedOut"></ng-template> 24 <ng-template [ngTemplateOutlet]="userLoggedOut"></ng-template>
28 </button> 25 </button>
29 26
30 <button 27 <button *ngIf="isAllChannelsSubscribed" type="button" class="btn" role="button" (click)="unsubscribe()">
31 *ngIf="isAllChannelsSubscribed" type="button"
32 class="btn btn-sm" role="button"
33 (click)="unsubscribe()">
34 <ng-container i18n>{account + "", select, undefined {Unsubscribe} other {Unsubscribe from all channels}}</ng-container> 28 <ng-container i18n>{account + "", select, undefined {Unsubscribe} other {Unsubscribe from all channels}}</ng-container>
35 </button> 29 </button>
36 </ng-template> 30 </ng-template>
@@ -43,7 +37,7 @@
43 class="btn-group" ngbDropdown autoClose="outside" placement="bottom-right bottom-left bottom auto" 37 class="btn-group" ngbDropdown autoClose="outside" placement="bottom-right bottom-left bottom auto"
44 role="group" aria-label="Multiple ways to subscribe to the current channel" i18n-aria-label 38 role="group" aria-label="Multiple ways to subscribe to the current channel" i18n-aria-label
45 > 39 >
46 <button class="btn btn-sm dropdown-toggle-split" ngbDropdownToggle aria-label="Open subscription dropdown" i18n-aria-label> 40 <button class="btn dropdown-toggle-split" ngbDropdownToggle aria-label="Open subscription dropdown" i18n-aria-label>
47 <ng-container 41 <ng-container
48 *ngIf="!isUserLoggedIn(); then userLoggedOut"> 42 *ngIf="!isUserLoggedIn(); then userLoggedOut">
49 </ng-container> 43 </ng-container>
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 da8eaf646..889596b62 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
@@ -3,14 +3,10 @@
3 3
4.btn-group-subscribe { 4.btn-group-subscribe {
5 @include peertube-button; 5 @include peertube-button;
6 @include disable-default-a-behaviour;
7 6
8 float: right; 7 button.dropdown-toggle {
9 padding: 0; 8 font-size: $button-font-size;
10 9 line-height: 1.2;
11 > .btn,
12 > .dropdown > .dropdown-toggle {
13 font-size: 15px;
14 } 10 }
15 11
16 &:not(.big) { 12 &:not(.big) {
@@ -38,7 +34,7 @@
38 34
39 // Unlogged 35 // Unlogged
40 > .dropdown > .dropdown-toggle span { 36 > .dropdown > .dropdown-toggle span {
41 @include padding-right(3px); 37 @include padding-right(5px);
42 } 38 }
43 39
44 // Logged 40 // Logged
@@ -65,9 +61,11 @@
65 @include padding-left(5px); 61 @include padding-left(5px);
66 } 62 }
67 } 63 }
64
68 &.unsubscribe-button { 65 &.unsubscribe-button {
69 .btn { 66 .btn {
70 @include grey-button; 67 @include grey-button;
68
71 font-weight: 600; 69 font-weight: 600;
72 } 70 }
73 } 71 }
diff --git a/client/src/app/shared/shared-video-live/live-documentation-link.component.html b/client/src/app/shared/shared-video-live/live-documentation-link.component.html
index acf8a71eb..27248645f 100644
--- a/client/src/app/shared/shared-video-live/live-documentation-link.component.html
+++ b/client/src/app/shared/shared-video-live/live-documentation-link.component.html
@@ -1,4 +1,4 @@
1<div i18n> 1<p i18n>
2 See <a href="https://docs.joinpeertube.org/use-create-upload-video?id=publish-a-live-in-peertube-gt-v3" target="_blank" rel="noopener noreferrer">the documentation</a> 2 See <a class="link-orange" href="https://docs.joinpeertube.org/use-create-upload-video?id=publish-a-live-in-peertube-gt-v3" target="_blank" rel="noopener noreferrer">the documentation</a>
3 to learn how to use the PeerTube live streaming feature. 3 to learn how to use the PeerTube live streaming feature.
4</div> 4</p>
diff --git a/client/src/app/shared/shared-video-live/live-stream-information.component.html b/client/src/app/shared/shared-video-live/live-stream-information.component.html
index 01e305938..99c7dbd4c 100644
--- a/client/src/app/shared/shared-video-live/live-stream-information.component.html
+++ b/client/src/app/shared/shared-video-live/live-stream-information.component.html
@@ -7,27 +7,27 @@
7 7
8 <div class="modal-body" *ngIf="live"> 8 <div class="modal-body" *ngIf="live">
9 <div> 9 <div>
10 <div class="badge badge-info" *ngIf="live.permanentLive" i18n>Permanent/Recurring live</div> 10 <div class="pt-badge badge-blue" *ngIf="live.permanentLive" i18n>Permanent/Recurring live</div>
11 <div class="badge badge-info" *ngIf="live.saveReplay" i18n>Replay will be saved</div> 11 <div class="pt-badge badge-blue" *ngIf="live.saveReplay" i18n>Replay will be saved</div>
12 </div> 12 </div>
13 13
14 <div class="alert alert-info"> 14 <div class="alert pt-alert-primary">
15 <my-live-documentation-link></my-live-documentation-link> 15 <my-live-documentation-link></my-live-documentation-link>
16 </div> 16 </div>
17 17
18 <div *ngIf="live.rtmpUrl" class="form-group"> 18 <div *ngIf="live.rtmpUrl" class="form-group">
19 <label for="liveVideoRTMPUrl" i18n>Live RTMP Url</label> 19 <label for="liveVideoRTMPUrl" i18n>Live RTMP Url</label>
20 <my-input-toggle-hidden inputId="liveVideoRTMPUrl" [value]="live.rtmpUrl" [withToggle]="false" [withCopy]="true" [show]="true" [readonly]="true"></my-input-toggle-hidden> 20 <my-input-text inputId="liveVideoRTMPUrl" [value]="live.rtmpUrl" [withToggle]="false" [withCopy]="true" [show]="true" [readonly]="true"></my-input-text>
21 </div> 21 </div>
22 22
23 <div *ngIf="live.rtmpsUrl" class="form-group"> 23 <div *ngIf="live.rtmpsUrl" class="form-group">
24 <label for="liveVideoRTMPSUrl" i18n>Live RTMPS Url</label> 24 <label for="liveVideoRTMPSUrl" i18n>Live RTMPS Url</label>
25 <my-input-toggle-hidden inputId="liveVideoRTMPSUrl" [value]="live.rtmpsUrl" [withToggle]="false" [withCopy]="true" [show]="true" [readonly]="true"></my-input-toggle-hidden> 25 <my-input-text inputId="liveVideoRTMPSUrl" [value]="live.rtmpsUrl" [withToggle]="false" [withCopy]="true" [show]="true" [readonly]="true"></my-input-text>
26 </div> 26 </div>
27 27
28 <div class="form-group"> 28 <div class="form-group">
29 <label for="liveVideoStreamKey" i18n>Live stream key</label> 29 <label for="liveVideoStreamKey" i18n>Live stream key</label>
30 <my-input-toggle-hidden inputId="liveVideoStreamKey" [value]="live.streamKey" [withCopy]="true" [readonly]="true"></my-input-toggle-hidden> 30 <my-input-text inputId="liveVideoStreamKey" [value]="live.streamKey" [withCopy]="true" [readonly]="true"></my-input-text>
31 31
32 <div class="form-group-description" i18n>⚠️ Never share your stream key with anyone.</div> 32 <div class="form-group-description" i18n>⚠️ Never share your stream key with anyone.</div>
33 </div> 33 </div>
@@ -36,8 +36,8 @@
36 <label i18n>Latest live sessions</label> 36 <label i18n>Latest live sessions</label>
37 37
38 <div class="journal-session" *ngFor="let session of latestLiveSessions"> 38 <div class="journal-session" *ngFor="let session of latestLiveSessions">
39 <span i18n class="badge badge-success" *ngIf="!getErrorLabel(session)">Success</span> 39 <span i18n class="pt-badge badge-success" *ngIf="!getErrorLabel(session)">Success</span>
40 <span class="badge badge-danger" *ngIf="getErrorLabel(session)">{{ getErrorLabel(session) }}</span> 40 <span class="pt-badge badge-danger" *ngIf="getErrorLabel(session)">{{ getErrorLabel(session) }}</span>
41 41
42 <span i18n>Started on {{ session.startDate | date:'medium' }}</span> 42 <span i18n>Started on {{ session.startDate | date:'medium' }}</span>
43 <span i18n *ngIf="session.endDate">Ended on {{ session.endDate | date:'medium' }}</span> 43 <span i18n *ngIf="session.endDate">Ended on {{ session.endDate | date:'medium' }}</span>
diff --git a/client/src/app/shared/shared-video-live/live-stream-information.component.scss b/client/src/app/shared/shared-video-live/live-stream-information.component.scss
index 9c8ad12bd..a0cb30897 100644
--- a/client/src/app/shared/shared-video-live/live-stream-information.component.scss
+++ b/client/src/app/shared/shared-video-live/live-stream-information.component.scss
@@ -9,19 +9,20 @@ p-autocomplete {
9 margin: 20px 0; 9 margin: 20px 0;
10} 10}
11 11
12.alert-info { 12.pt-alert-primary {
13 margin: 1rem 0; 13 margin: 1rem 0;
14} 14}
15 15
16.badge { 16.pt-badge {
17 @include margin-right(5px);
18
17 font-size: 13px; 19 font-size: 13px;
18 margin-right: 5px;
19} 20}
20 21
21.journal-session { 22.journal-session {
22 margin-bottom: 5px; 23 margin-bottom: 5px;
23 24
24 span:not(.badge, :last-child)::after { 25 span:not(.pt-badge, :last-child)::after {
25 margin: 3px; 26 margin: 3px;
26 content: '•'; 27 content: '•';
27 } 28 }
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.html b/client/src/app/shared/shared-video-miniature/video-download.component.html
index b50544057..1c7458b4b 100644
--- a/client/src/app/shared/shared-video-miniature/video-download.component.html
+++ b/client/src/app/shared/shared-video-miniature/video-download.component.html
@@ -28,15 +28,10 @@
28 28
29 <ng-template ngbNavContent> 29 <ng-template ngbNavContent>
30 <div class="nav-content"> 30 <div class="nav-content">
31 <div class="input-group input-group-sm"> 31 <my-input-text
32 <input #urlInput (click)="urlInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getLink()" /> 32 *ngIf="!isConfidentialVideo()"
33 33 [show]="true" [readonly]="true" [withCopy]="true" [withToggle]="false" [value]="getLink()"
34 <div class="input-group-append" *ngIf="!isConfidentialVideo()"> 34 ></my-input-text>
35 <button [cdkCopyToClipboard]="urlInput.value" (click)="activateCopiedMessage()" type="button" class="btn btn-outline-secondary">
36 <span class="glyphicon glyphicon-duplicate"></span>
37 </button>
38 </div>
39 </div>
40 </div> 35 </div>
41 </ng-template> 36 </ng-template>
42 </ng-container> 37 </ng-container>
@@ -53,14 +48,10 @@
53 48
54 <ng-template ngbNavContent> 49 <ng-template ngbNavContent>
55 <div class="nav-content"> 50 <div class="nav-content">
56 <div class="input-group input-group-sm"> 51 <my-input-text
57 <input #urlInput (click)="urlInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getLink()" /> 52 *ngIf="!isConfidentialVideo()"
58 <div class="input-group-append" *ngIf="!isConfidentialVideo()"> 53 [show]="true" [readonly]="true" [withCopy]="true" [withToggle]="false" [value]="getLink()"
59 <button [cdkCopyToClipboard]="urlInput.value" (click)="activateCopiedMessage()" type="button" class="btn btn-outline-secondary"> 54 ></my-input-text>
60 <span class="glyphicon glyphicon-duplicate"></span>
61 </button>
62 </div>
63 </div>
64 </div> 55 </div>
65 </ng-template> 56 </ng-template>
66 </ng-container> 57 </ng-container>
@@ -129,7 +120,7 @@
129 [attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic" 120 [attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic"
130 > 121 >
131 <ng-container *ngIf="isAdvancedCustomizationCollapsed"> 122 <ng-container *ngIf="isAdvancedCustomizationCollapsed">
132 <span class="glyphicon glyphicon-menu-down"></span> 123 <span class="chevron-down"></span>
133 124
134 <ng-container i18n> 125 <ng-container i18n>
135 Advanced 126 Advanced
@@ -137,7 +128,7 @@
137 </ng-container> 128 </ng-container>
138 129
139 <ng-container *ngIf="!isAdvancedCustomizationCollapsed"> 130 <ng-container *ngIf="!isAdvancedCustomizationCollapsed">
140 <span class="glyphicon glyphicon-menu-up"></span> 131 <span class="chevron-up"></span>
141 132
142 <ng-container i18n> 133 <ng-container i18n>
143 Simple 134 Simple
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 bd42f4813..407bdadf2 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
@@ -10,17 +10,12 @@
10 justify-content: center; 10 justify-content: center;
11 align-items: center; 11 align-items: center;
12 margin-top: 20px; 12 margin-top: 20px;
13 font-size: 16px;
14 font-weight: 600; 13 font-weight: 600;
15 cursor: pointer; 14 cursor: pointer;
16 15
17 .nav-tabs { 16 .nav-tabs {
18 margin-top: 10x; 17 margin-top: 10x;
19 } 18 }
20
21 .glyphicon {
22 @include margin-right(5px);
23 }
24} 19}
25 20
26.peertube-select-container.title-select { 21.peertube-select-container.title-select {
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.ts b/client/src/app/shared/shared-video-miniature/video-download.component.ts
index 5328f5170..bbda39c2d 100644
--- a/client/src/app/shared/shared-video-miniature/video-download.component.ts
+++ b/client/src/app/shared/shared-video-miniature/video-download.component.ts
@@ -193,10 +193,6 @@ export class VideoDownloadComponent {
193 return this.video.privacy.id === VideoPrivacy.PRIVATE || this.video.privacy.id === VideoPrivacy.INTERNAL 193 return this.video.privacy.id === VideoPrivacy.PRIVATE || this.video.privacy.id === VideoPrivacy.INTERNAL
194 } 194 }
195 195
196 activateCopiedMessage () {
197 this.notifier.success($localize`Copied`)
198 }
199
200 switchToType (type: DownloadType) { 196 switchToType (type: DownloadType) {
201 this.type = type 197 this.type = type
202 } 198 }
diff --git a/client/src/app/shared/shared-video-miniature/video-filters-header.component.html b/client/src/app/shared/shared-video-miniature/video-filters-header.component.html
index a07b8b5ee..fe7a59bdb 100644
--- a/client/src/app/shared/shared-video-miniature/video-filters-header.component.html
+++ b/client/src/app/shared/shared-video-miniature/video-filters-header.component.html
@@ -44,6 +44,7 @@
44 [searchable]="false" 44 [searchable]="false"
45 > 45 >
46 <ng-option i18n value="-publishedAt">Sort by <strong>"Recently Added"</strong></ng-option> 46 <ng-option i18n value="-publishedAt">Sort by <strong>"Recently Added"</strong></ng-option>
47 <ng-option i18n value="-originallyPublishedAt">Sort by <strong>"Original Publication Date"</strong></ng-option>
47 48
48 <ng-option i18n *ngIf="isTrendingSortEnabled('most-viewed')" value="-trending">Sort by <strong>"Recent Views"</strong></ng-option> 49 <ng-option i18n *ngIf="isTrendingSortEnabled('most-viewed')" value="-trending">Sort by <strong>"Recent Views"</strong></ng-option>
49 <ng-option i18n *ngIf="isTrendingSortEnabled('hot')" value="-hot">Sort by <strong>"Hot"</strong></ng-option> 50 <ng-option i18n *ngIf="isTrendingSortEnabled('hot')" value="-hot">Sort by <strong>"Hot"</strong></ng-option>
diff --git a/client/src/app/shared/shared-video-miniature/video-filters-header.component.scss b/client/src/app/shared/shared-video-miniature/video-filters-header.component.scss
index 8cb1ff5b8..a4e51982c 100644
--- a/client/src/app/shared/shared-video-miniature/video-filters-header.component.scss
+++ b/client/src/app/shared/shared-video-miniature/video-filters-header.component.scss
@@ -3,7 +3,6 @@
3 3
4.root { 4.root {
5 margin-bottom: 45px; 5 margin-bottom: 45px;
6 font-size: 15px;
7} 6}
8 7
9.first-row { 8.first-row {
@@ -49,7 +48,6 @@
49 48
50 border-radius: 24px; 49 border-radius: 24px;
51 padding: 4px 15px; 50 padding: 4px 15px;
52 font-size: 16px;
53 margin-bottom: 15px; 51 margin-bottom: 15px;
54 cursor: pointer; 52 cursor: pointer;
55} 53}
@@ -101,7 +99,7 @@
101} 99}
102 100
103.sort { 101.sort {
104 min-width: 200px; 102 min-width: 250px;
105 max-width: 300px; 103 max-width: 300px;
106 height: min-content; 104 height: min-content;
107 105
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 80b418c87..a397efdca 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
@@ -4,6 +4,10 @@
4 4
5$more-button-width: 40px; 5$more-button-width: 40px;
6 6
7.video-miniature {
8 font-size: 14px;
9}
10
7.video-miniature-name { 11.video-miniature-name {
8 @include miniature-name; 12 @include miniature-name;
9} 13}
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 42c472579..534a78b3f 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
@@ -175,7 +175,7 @@ export class VideoMiniatureComponent implements OnInit {
175 175
176 if (video.scheduledUpdate) { 176 if (video.scheduledUpdate) {
177 const updateAt = new Date(video.scheduledUpdate.updateAt.toString()).toLocaleString(this.localeId) 177 const updateAt = new Date(video.scheduledUpdate.updateAt.toString()).toLocaleString(this.localeId)
178 return $localize`Publication scheduled on ` + updateAt 178 return $localize`Publication scheduled on ${updateAt}`
179 } 179 }
180 180
181 if (video.state.id === VideoState.TRANSCODING_FAILED) { 181 if (video.state.id === VideoState.TRANSCODING_FAILED) {
diff --git a/client/src/app/shared/shared-video-miniature/videos-list.component.html b/client/src/app/shared/shared-video-miniature/videos-list.component.html
index 2b554517f..c220f61f1 100644
--- a/client/src/app/shared/shared-video-miniature/videos-list.component.html
+++ b/client/src/app/shared/shared-video-miniature/videos-list.component.html
@@ -12,15 +12,15 @@
12 12
13 <div class="action-block"> 13 <div class="action-block">
14 <ng-container *ngFor="let action of headerActions"> 14 <ng-container *ngFor="let action of headerActions">
15 <a *ngIf="action.routerLink" class="ml-2" [routerLink]="action.routerLink" routerLinkActive="active"> 15 <a *ngIf="action.routerLink" class="ms-2" [routerLink]="action.routerLink" routerLinkActive="active">
16 <ng-container *ngTemplateOutlet="actionContent; context:{ $implicit: action }"></ng-container> 16 <ng-container *ngTemplateOutlet="actionContent; context:{ $implicit: action }"></ng-container>
17 </a> 17 </a>
18 18
19 <a *ngIf="!action.routerLink && !action.href && action.click" class="ml-2" (click)="action.click($event)" (key.enter)="action.click($event)"> 19 <a *ngIf="!action.routerLink && !action.href && action.click" class="ms-2" (click)="action.click($event)" (key.enter)="action.click($event)">
20 <ng-container *ngTemplateOutlet="actionContent; context:{ $implicit: action }"></ng-container> 20 <ng-container *ngTemplateOutlet="actionContent; context:{ $implicit: action }"></ng-container>
21 </a> 21 </a>
22 22
23 <a *ngIf="!action.routerLink && action.href && action.click" class="ml-2" (click)="action.click($event)" (key.enter)="action.click($event)" [href]="action.href"> 23 <a *ngIf="!action.routerLink && action.href && action.click" class="ms-2" (click)="action.click($event)" (key.enter)="action.click($event)" [href]="action.href">
24 <ng-container *ngTemplateOutlet="actionContent; context:{ $implicit: action }"></ng-container> 24 <ng-container *ngTemplateOutlet="actionContent; context:{ $implicit: action }"></ng-container>
25 </a> 25 </a>
26 26
diff --git a/client/src/app/shared/shared-video-miniature/videos-list.component.scss b/client/src/app/shared/shared-video-miniature/videos-list.component.scss
index 209201a5c..fb9dcafb8 100644
--- a/client/src/app/shared/shared-video-miniature/videos-list.component.scss
+++ b/client/src/app/shared/shared-video-miniature/videos-list.component.scss
@@ -54,9 +54,9 @@ $margin-top: 30px;
54} 54}
55 55
56.date-title { 56.date-title {
57 font-size: 16px;
58 font-weight: $font-semibold; 57 font-weight: $font-semibold;
59 margin-bottom: 20px; 58 margin-bottom: 20px;
59 font-size: 1rem;
60 60
61 // Make the element span a full grid row within .videos grid 61 // Make the element span a full grid row within .videos grid
62 grid-column: 1 / -1; 62 grid-column: 1 / -1;
@@ -99,11 +99,5 @@ $margin-top: 30px;
99 align-items: center; 99 align-items: center;
100 height: auto; 100 height: auto;
101 margin-bottom: 10px; 101 margin-bottom: 10px;
102
103 .title-page {
104 @include margin-right(0);
105
106 margin-bottom: 10px;
107 }
108 } 102 }
109} 103}
diff --git a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.html b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.html
index bd5d37196..6d787796a 100644
--- a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.html
+++ b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.html
@@ -30,12 +30,10 @@
30 </div> 30 </div>
31 31
32 <div class="optional-rows" *ngIf="playlist.optionalRowDisplayed"> 32 <div class="optional-rows" *ngIf="playlist.optionalRowDisplayed">
33 <div class="labels"> 33 <div class="header-label" i18n>Start at</div>
34 <div i18n>Start at</div> 34 <div class="header-label" i18n>Stop at</div>
35 <div i18n>Stop at</div>
36 </div>
37 35
38 <div *ngFor="let element of buildOptionalRowElements(playlist)"> 36 <ng-container *ngFor="let element of buildOptionalRowElements(playlist)">
39 <my-peertube-checkbox 37 <my-peertube-checkbox
40 [inputName]="getOptionalInputName(playlist, element)" 38 [inputName]="getOptionalInputName(playlist, element)"
41 [ngModel]="element.enabled" [onPushWorkaround]="true" 39 [ngModel]="element.enabled" [onPushWorkaround]="true"
@@ -55,7 +53,7 @@
55 (inputBlur)="onElementTimestampUpdate(playlist, element)" 53 (inputBlur)="onElementTimestampUpdate(playlist, element)"
56 #stopAt 54 #stopAt
57 ></my-timestamp-input> 55 ></my-timestamp-input>
58 </div> 56 </ng-container>
59 </div> 57 </div>
60 </div> 58 </div>
61 </div> 59 </div>
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 7db469d7c..de2f1032b 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
@@ -1,10 +1,6 @@
1@use '_variables' as *; 1@use '_variables' as *;
2@use '_mixins' as *; 2@use '_mixins' as *;
3 3
4$optional-rows-checkbox-width: 34px;
5$timestamp-width: 50px;
6$timestamp-margin-right: 10px;
7
8.header, 4.header,
9.dropdown-item, 5.dropdown-item,
10.input-container { 6.input-container {
@@ -52,12 +48,12 @@ $timestamp-margin-right: 10px;
52 } 48 }
53} 49}
54 50
55.primary-row, 51.primary-row {
56.optional-rows > div {
57 display: flex; 52 display: flex;
58 53
59 my-peertube-checkbox { 54 my-peertube-checkbox {
60 @include margin-right(10px); 55 @include margin-right(10px);
56
61 align-self: center; 57 align-self: center;
62 } 58 }
63 59
@@ -84,41 +80,30 @@ $timestamp-margin-right: 10px;
84 height: 19px; 80 height: 19px;
85 } 81 }
86 } 82 }
87
88 my-timestamp-input {
89 @include margin-right($timestamp-margin-right);
90
91 ::ng-deep .ui-inputtext {
92 padding: 0;
93 width: $timestamp-width;
94 }
95 }
96} 83}
97 84
98.optional-rows { 85.optional-rows {
99 > div { 86 display: grid;
100 padding: 8px 5px 5px 10px; 87 grid-template-columns: 35px 80px 80px;
101 } 88 row-gap: 3px;
89 column-gap: 10px;
90 align-items: center;
102 91
103 my-peertube-checkbox { 92 my-peertube-checkbox {
104 @include margin-right(0 !important); 93 @include margin-left(auto);
105
106 display: block;
107 width: $optional-rows-checkbox-width;
108 } 94 }
109 95
110 .labels { 96 .header-label {
111 @include margin-left($optional-rows-checkbox-width);
112
113 font-size: 13px; 97 font-size: 13px;
114 color: pvar(--greyForegroundColor); 98 color: pvar(--greyForegroundColor);
115 padding-top: 5px; 99 padding-left: 2px;
116 padding-bottom: 0;
117 100
118 div { 101 &:nth-child(1) {
119 @include margin-right($timestamp-margin-right); 102 grid-column: 2;
103 }
120 104
121 width: $timestamp-width; 105 &:nth-child(2) {
106 grid-column: 3;
122 } 107 }
123 } 108 }
124} 109}
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.html b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.html
index 2400a4c25..f58d5f7f6 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.html
+++ b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.html
@@ -21,7 +21,7 @@
21 [attr.title]="playlistElement.video.name" 21 [attr.title]="playlistElement.video.name"
22 >{{ playlistElement.video.name }}</a> 22 >{{ playlistElement.video.name }}</a>
23 23
24 <span *ngIf="isVideoPrivate()" class="badge badge-yellow">Private</span> 24 <span *ngIf="isVideoPrivate()" class="pt-badge badge-yellow">Private</span>
25 </div> 25 </div>
26 26
27 <span class="video-miniature-created-at-views"> 27 <span class="video-miniature-created-at-views">
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 fbf67e892..e6b01d33d 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
@@ -75,41 +75,6 @@ my-video-thumbnail,
75 left: -2px; 75 left: -2px;
76 } 76 }
77 } 77 }
78
79 .video-info {
80 display: flex;
81 flex-direction: column;
82 align-self: flex-start;
83 min-width: 0;
84
85 .video-info-header {
86 display: flex;
87 align-items: baseline;
88
89 a {
90 width: auto;
91 padding-right: 5px;
92 }
93
94 .badge {
95 @include peertube-badge;
96 margin-right: 5px;
97 }
98 }
99
100 .video-info-account,
101 .video-info-timestamp {
102 color: pvar(--greyForegroundColor);
103 }
104 }
105 }
106
107 .video-info-name {
108 @include ellipsis;
109
110 font-size: 18px;
111 font-weight: $font-semibold;
112 display: inline-block;
113 } 78 }
114 79
115 .more, 80 .more,
@@ -140,6 +105,45 @@ my-video-thumbnail,
140 } 105 }
141} 106}
142 107
108.video-info-name {
109 @include ellipsis;
110
111 font-size: 18px;
112 font-weight: $font-semibold;
113 display: inline-block;
114}
115
116.video-info {
117 display: flex;
118 flex-direction: column;
119 align-self: flex-start;
120 min-width: 0;
121
122 .video-info-header {
123 display: flex;
124 align-items: baseline;
125
126 a {
127 width: auto;
128 padding-right: 5px;
129 }
130
131 .pt-badge {
132 @include margin-right(5px);
133 }
134 }
135
136 .video-info-account,
137 .video-info-timestamp {
138 color: pvar(--greyForegroundColor);
139 }
140}
141
142.video-info-account,
143.video-miniature-created-at-views {
144 font-size: 14px;
145}
146
143.dropdown-menu { 147.dropdown-menu {
144 148
145 .dropdown-item { 149 .dropdown-item {
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 3956d9282..d43afad28 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
@@ -53,7 +53,7 @@
53 53
54 .privacy-date { 54 .privacy-date {
55 margin-top: 5px; 55 margin-top: 5px;
56 font-size: 13px; 56 font-size: 14px;
57 57
58 .privacy { 58 .privacy {
59 font-weight: $font-semibold; 59 font-weight: $font-semibold;