aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/.eslintrc.json10
-rw-r--r--client/e2e/wdio.local-test.conf.ts6
-rw-r--r--client/e2e/wdio.local.conf.ts4
-rw-r--r--client/src/app/+about/about-instance/about-instance.resolver.ts2
-rw-r--r--client/src/app/+accounts/accounts.component.html4
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.html4
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html18
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-homepage.component.html2
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html8
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.html6
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.html10
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.html4
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.scss4
-rw-r--r--client/src/app/+admin/overview/users/user-edit/user-edit.component.html6
-rw-r--r--client/src/app/+admin/overview/users/user-list/user-list.component.html2
-rw-r--r--client/src/app/+admin/overview/users/user-list/user-list.component.scss4
-rw-r--r--client/src/app/+login/login.component.html6
-rw-r--r--client/src/app/+login/login.component.ts4
-rw-r--r--client/src/app/+manage/video-channel-edit/video-channel-create.component.ts6
-rw-r--r--client/src/app/+manage/video-channel-edit/video-channel-edit.component.html2
-rw-r--r--client/src/app/+manage/video-channel-edit/video-channel-update.component.ts10
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts2
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts8
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.component.html2
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss4
-rw-r--r--client/src/app/+my-library/my-history/my-history.component.html4
-rw-r--r--client/src/app/+my-library/my-ownership/my-ownership.component.html2
-rw-r--r--client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.html2
-rw-r--r--client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.scss5
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html4
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html2
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlists.component.scss4
-rw-r--r--client/src/app/+my-library/my-videos/my-videos.component.html4
-rw-r--r--client/src/app/+reset-password/reset-password.component.ts2
-rw-r--r--client/src/app/+search/search-filters.component.html14
-rw-r--r--client/src/app/+search/search.component.html6
-rw-r--r--client/src/app/+search/shared/abstract-lazy-load.resolver.ts2
-rw-r--r--client/src/app/+signup/+register/custom-stepper.component.html9
-rw-r--r--client/src/app/+signup/+register/custom-stepper.component.scss5
-rw-r--r--client/src/app/+signup/+register/register.component.html6
-rw-r--r--client/src/app/+signup/+register/register.component.scss26
-rw-r--r--client/src/app/+video-channels/video-channels.component.html4
-rw-r--r--client/src/app/+video-studio/edit/video-studio-edit.component.html4
-rw-r--r--client/src/app/+video-studio/edit/video-studio-edit.component.ts2
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.html16
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.scss4
-rw-r--r--client/src/app/+videos/+video-watch/shared/comment/video-comments.component.html4
-rw-r--r--client/src/app/+videos/+video-watch/shared/comment/video-comments.component.scss5
-rw-r--r--client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.html5
-rw-r--r--client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.scss23
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html14
-rw-r--r--client/src/app/core/hotkeys/hotkeys.component.html4
-rw-r--r--client/src/app/header/search-typeahead.component.html7
-rw-r--r--client/src/app/menu/menu.component.html26
-rw-r--r--client/src/app/menu/menu.component.scss45
-rw-r--r--client/src/app/shared/form-validators/video-validators.ts2
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.html6
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html12
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss4
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html12
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss4
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar.component.ts14
-rw-r--r--client/src/app/shared/shared-forms/input-switch.component.html6
-rw-r--r--client/src/app/shared/shared-forms/preview-upload.component.html2
-rw-r--r--client/src/app/shared/shared-icons/global-icon.component.ts108
-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.ts8
-rw-r--r--client/src/app/shared/shared-main/angular/number-formatter.pipe.ts2
-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.ts2
-rw-r--r--client/src/app/shared/shared-main/date/date-toggle.component.html3
-rw-r--r--client/src/app/shared/shared-main/loaders/loader.component.ts4
-rw-r--r--client/src/app/shared/shared-main/users/user-notifications.component.html1
-rw-r--r--client/src/app/shared/shared-main/users/user-quota.component.html6
-rw-r--r--client/src/app/shared/shared-moderation/server-blocklist.component.html4
-rw-r--r--client/src/app/shared/shared-moderation/server-blocklist.component.scss4
-rw-r--r--client/src/app/shared/shared-tables/video-cell.component.html2
-rw-r--r--client/src/app/shared/shared-thumbnail/video-thumbnail.component.html8
-rw-r--r--client/src/app/shared/shared-video-live/live-stream-information.component.html2
-rw-r--r--client/src/app/shared/shared-video-miniature/video-download.component.html6
-rw-r--r--client/src/app/shared/shared-video-miniature/video-filters-header.component.html8
-rw-r--r--client/src/app/shared/shared-video-miniature/video-filters.model.ts2
-rw-r--r--client/src/app/shared/shared-video-miniature/videos-list.component.html4
-rw-r--r--client/src/app/shared/shared-video-miniature/videos-selection.component.html4
-rw-r--r--client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.html14
-rw-r--r--client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss6
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.html8
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss1
-rw-r--r--client/src/assets/player/shared/settings/settings-dialog.ts2
-rw-r--r--client/src/assets/player/utils.ts2
-rw-r--r--client/src/root-helpers/bytes.ts2
-rw-r--r--client/src/root-helpers/logger.ts2
-rw-r--r--client/src/root-helpers/plugins-manager.ts10
-rw-r--r--client/src/sass/class-helpers/_buttons.scss11
-rw-r--r--client/src/sass/class-helpers/_common.scss6
-rw-r--r--client/src/sass/include/_mixins.scss6
-rw-r--r--client/src/standalone/videos/embed.html1
-rw-r--r--client/src/standalone/videos/test-embed.html2
-rw-r--r--server/tests/api/live/live-save-replay.ts30
100 files changed, 357 insertions, 390 deletions
diff --git a/client/.eslintrc.json b/client/.eslintrc.json
index f7b207b58..a297cdc94 100644
--- a/client/.eslintrc.json
+++ b/client/.eslintrc.json
@@ -18,8 +18,7 @@
18 }, 18 },
19 "extends": [ 19 "extends": [
20 "../.eslintrc.json", 20 "../.eslintrc.json",
21 "plugin:@angular-eslint/ng-cli-compat", 21 "plugin:@angular-eslint/recommended",
22 "plugin:@angular-eslint/ng-cli-compat--formatting-add-on",
23 "plugin:@angular-eslint/template/process-inline-templates" 22 "plugin:@angular-eslint/template/process-inline-templates"
24 ], 23 ],
25 "rules": { 24 "rules": {
@@ -155,6 +154,10 @@
155 "object-shorthand": [ 154 "object-shorthand": [
156 "error", 155 "error",
157 "properties" 156 "properties"
157 ],
158 "quote-props": [
159 "error",
160 "consistent-as-needed"
158 ] 161 ]
159 } 162 }
160 }, 163 },
@@ -163,7 +166,8 @@
163 "*.html" 166 "*.html"
164 ], 167 ],
165 "extends": [ 168 "extends": [
166 "plugin:@angular-eslint/template/recommended" 169 "plugin:@angular-eslint/template/recommended",
170 "plugin:@angular-eslint/template/accessibility"
167 ], 171 ],
168 "rules": {} 172 "rules": {}
169 } 173 }
diff --git a/client/e2e/wdio.local-test.conf.ts b/client/e2e/wdio.local-test.conf.ts
index 3c19e4e9a..96ddc67ca 100644
--- a/client/e2e/wdio.local-test.conf.ts
+++ b/client/e2e/wdio.local-test.conf.ts
@@ -25,15 +25,15 @@ module.exports = {
25 25
26 capabilities: [ 26 capabilities: [
27 { 27 {
28 browserName: 'chrome', 28 'browserName': 'chrome',
29 acceptInsecureCerts: true, 29 'acceptInsecureCerts': true,
30 'goog:chromeOptions': { 30 'goog:chromeOptions': {
31 args: [ '--disable-gpu', windowSizeArg ], 31 args: [ '--disable-gpu', windowSizeArg ],
32 prefs 32 prefs
33 } 33 }
34 }, 34 },
35 { 35 {
36 browserName: 'firefox', 36 'browserName': 'firefox',
37 'moz:firefoxOptions': { 37 'moz:firefoxOptions': {
38 binary: '/usr/bin/firefox-developer-edition', 38 binary: '/usr/bin/firefox-developer-edition',
39 args: [ '--headless', windowSizeArg ], 39 args: [ '--headless', windowSizeArg ],
diff --git a/client/e2e/wdio.local.conf.ts b/client/e2e/wdio.local.conf.ts
index 903235b86..5cdd69290 100644
--- a/client/e2e/wdio.local.conf.ts
+++ b/client/e2e/wdio.local.conf.ts
@@ -20,14 +20,14 @@ module.exports = {
20 20
21 capabilities: [ 21 capabilities: [
22 { 22 {
23 browserName: 'chrome', 23 'browserName': 'chrome',
24 'goog:chromeOptions': { 24 'goog:chromeOptions': {
25 args: [ '--headless', '--disable-gpu', windowSizeArg ], 25 args: [ '--headless', '--disable-gpu', windowSizeArg ],
26 prefs 26 prefs
27 } 27 }
28 }, 28 },
29 { 29 {
30 browserName: 'firefox', 30 'browserName': 'firefox',
31 'moz:firefoxOptions': { 31 'moz:firefoxOptions': {
32 binary: '/usr/bin/firefox-developer-edition', 32 binary: '/usr/bin/firefox-developer-edition',
33 args: [ '--headless', windowSizeArg ], 33 args: [ '--headless', windowSizeArg ],
diff --git a/client/src/app/+about/about-instance/about-instance.resolver.ts b/client/src/app/+about/about-instance/about-instance.resolver.ts
index 5c09b0f46..f52a95b88 100644
--- a/client/src/app/+about/about-instance/about-instance.resolver.ts
+++ b/client/src/app/+about/about-instance/about-instance.resolver.ts
@@ -16,7 +16,7 @@ export type ResolverData = {
16} 16}
17 17
18@Injectable() 18@Injectable()
19export class AboutInstanceResolver { 19export class AboutInstanceResolver {
20 20
21 constructor ( 21 constructor (
22 private instanceService: InstanceService, 22 private instanceService: InstanceService,
diff --git a/client/src/app/+accounts/accounts.component.html b/client/src/app/+accounts/accounts.component.html
index 2a4985964..66d108134 100644
--- a/client/src/app/+accounts/accounts.component.html
+++ b/client/src/app/+accounts/accounts.component.html
@@ -48,12 +48,12 @@
48 <div class="description-html" [innerHTML]="accountDescriptionHTML"></div> 48 <div class="description-html" [innerHTML]="accountDescriptionHTML"></div>
49 </div> 49 </div>
50 50
51 <div *ngIf="hasShowMoreDescription()" class="show-more d-md-none d-block" role="button" 51 <button *ngIf="hasShowMoreDescription()" class="show-more d-md-none d-block button-unstyle"
52 (click)="accountDescriptionExpanded = !accountDescriptionExpanded" 52 (click)="accountDescriptionExpanded = !accountDescriptionExpanded"
53 title="Show the complete description" i18n-title i18n 53 title="Show the complete description" i18n-title i18n
54 > 54 >
55 Show more... 55 Show more...
56 </div> 56 </button>
57 57
58 <div class="buttons"> 58 <div class="buttons">
59 <a *ngIf="isManageable()" routerLink="/my-account" class="peertube-button-link orange-button" i18n> 59 <a *ngIf="isManageable()" routerLink="/my-account" class="peertube-button-link orange-button" i18n>
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.html b/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.html
index a17b13fdf..bbf946df0 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.html
@@ -3,7 +3,7 @@
3 <div class="row mt-5"> <!-- cache grid --> 3 <div class="row mt-5"> <!-- cache grid -->
4 4
5 <div class="col-12 col-lg-4 col-xl-3"> 5 <div class="col-12 col-lg-4 col-xl-3">
6 <div i18n class="inner-form-title">CACHE</div> 6 <h2 i18n class="inner-form-title">CACHE</h2>
7 <div i18n class="inner-form-description"> 7 <div i18n class="inner-form-description">
8 Some files are not federated, and fetched when necessary. Define their caching policies. 8 Some files are not federated, and fetched when necessary. Define their caching policies.
9 </div> 9 </div>
@@ -60,7 +60,7 @@
60 <div class="row mt-4"> <!-- cache grid --> 60 <div class="row mt-4"> <!-- cache grid -->
61 <div class="col-12 col-lg-4 col-xl-3"> 61 <div class="col-12 col-lg-4 col-xl-3">
62 <div class="anchor" id="customizations"></div> <!-- customizations anchor --> 62 <div class="anchor" id="customizations"></div> <!-- customizations anchor -->
63 <div i18n class="inner-form-title">CUSTOMIZATIONS</div> 63 <h2 i18n class="inner-form-title">CUSTOMIZATIONS</h2>
64 <div i18n class="inner-form-description"> 64 <div i18n class="inner-form-description">
65 Slight modifications to your PeerTube instance for when creating a plugin or theme is overkill. 65 Slight modifications to your PeerTube instance for when creating a plugin or theme is overkill.
66 </div> 66 </div>
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html
index 9fc332308..50533ef73 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html
@@ -1,7 +1,7 @@
1<ng-container [formGroup]="form"> 1<ng-container [formGroup]="form">
2 <div class="row mt-5"> <!-- appearance grid --> 2 <div class="row mt-5"> <!-- appearance grid -->
3 <div class="col-12 col-lg-4 col-xl-3"> 3 <div class="col-12 col-lg-4 col-xl-3">
4 <div i18n class="inner-form-title">APPEARANCE</div> 4 <h2 i18n class="inner-form-title">APPEARANCE</h2>
5 <div i18n class="inner-form-description"> 5 <div i18n class="inner-form-description">
6 Use <a class="link-orange" routerLink="/admin/plugins">plugins & themes</a> for more involved changes, or add slight <a class="link-orange" routerLink="/admin/config/edit-custom" fragment="advanced-configuration">customizations</a>. 6 Use <a class="link-orange" routerLink="/admin/plugins">plugins & themes</a> for more involved changes, or add slight <a class="link-orange" routerLink="/admin/config/edit-custom" fragment="advanced-configuration">customizations</a>.
7 </div> 7 </div>
@@ -91,7 +91,7 @@
91 91
92 <div class="row mt-4"> <!-- broadcast grid --> 92 <div class="row mt-4"> <!-- broadcast grid -->
93 <div class="col-12 col-lg-4 col-xl-3"> 93 <div class="col-12 col-lg-4 col-xl-3">
94 <div i18n class="inner-form-title">BROADCAST MESSAGE</div> 94 <h2 i18n class="inner-form-title">BROADCAST MESSAGE</h2>
95 <div i18n class="inner-form-description"> 95 <div i18n class="inner-form-description">
96 Display a message on your instance 96 Display a message on your instance
97 </div> 97 </div>
@@ -147,7 +147,7 @@
147 147
148 <div class="row mt-4"> <!-- new users grid --> 148 <div class="row mt-4"> <!-- new users grid -->
149 <div class="col-12 col-lg-4 col-xl-3"> 149 <div class="col-12 col-lg-4 col-xl-3">
150 <div i18n class="inner-form-title">NEW USERS</div> 150 <h2 i18n class="inner-form-title">NEW USERS</h2>
151 <div i18n class="inner-form-description"> 151 <div i18n class="inner-form-description">
152 Manage <a class="link-orange" routerLink="/admin/users">users</a> to set their quota individually. 152 Manage <a class="link-orange" routerLink="/admin/users">users</a> to set their quota individually.
153 </div> 153 </div>
@@ -264,7 +264,7 @@
264 264
265 <div class="row mt-4"> <!-- videos grid --> 265 <div class="row mt-4"> <!-- videos grid -->
266 <div class="col-12 col-lg-4 col-xl-3"> 266 <div class="col-12 col-lg-4 col-xl-3">
267 <div i18n class="inner-form-title">VIDEOS</div> 267 <h2 i18n class="inner-form-title">VIDEOS</h2>
268 </div> 268 </div>
269 269
270 <div class="col-12 col-lg-8 col-xl-9"> 270 <div class="col-12 col-lg-8 col-xl-9">
@@ -350,7 +350,7 @@
350 350
351 <div class="row mt-4"> <!-- video channels grid --> 351 <div class="row mt-4"> <!-- video channels grid -->
352 <div class="col-12 col-lg-4 col-xl-3"> 352 <div class="col-12 col-lg-4 col-xl-3">
353 <div i18n class="inner-form-title">VIDEO CHANNELS</div> 353 <h2 i18n class="inner-form-title">VIDEO CHANNELS</h2>
354 </div> 354 </div>
355 355
356 <div class="col-12 col-lg-8 col-xl-9"> 356 <div class="col-12 col-lg-8 col-xl-9">
@@ -372,7 +372,7 @@
372 372
373 <div class="row mt-4"> <!-- search grid --> 373 <div class="row mt-4"> <!-- search grid -->
374 <div class="col-12 col-lg-4 col-xl-3"> 374 <div class="col-12 col-lg-4 col-xl-3">
375 <div i18n class="inner-form-title">SEARCH</div> 375 <h2 i18n class="inner-form-title">SEARCH</h2>
376 </div> 376 </div>
377 377
378 <div class="col-12 col-lg-8 col-xl-9"> 378 <div class="col-12 col-lg-8 col-xl-9">
@@ -461,7 +461,7 @@
461 461
462 <div class="row mt-4"> <!-- federation grid --> 462 <div class="row mt-4"> <!-- federation grid -->
463 <div class="col-12 col-lg-4 col-xl-3"> 463 <div class="col-12 col-lg-4 col-xl-3">
464 <div i18n class="inner-form-title">FEDERATION</div> 464 <h2 i18n class="inner-form-title">FEDERATION</h2>
465 <div i18n class="inner-form-description"> 465 <div i18n class="inner-form-description">
466 Manage <a class="link-orange" routerLink="/admin/follows">relations</a> with other instances. 466 Manage <a class="link-orange" routerLink="/admin/follows">relations</a> with other instances.
467 </div> 467 </div>
@@ -540,7 +540,7 @@
540 540
541 <div class="row mt-4"> <!-- administrators grid --> 541 <div class="row mt-4"> <!-- administrators grid -->
542 <div class="col-12 col-lg-4 col-xl-3"> 542 <div class="col-12 col-lg-4 col-xl-3">
543 <div i18n class="inner-form-title">ADMINISTRATORS</div> 543 <h2 i18n class="inner-form-title">ADMINISTRATORS</h2>
544 </div> 544 </div>
545 545
546 <div class="col-12 col-lg-8 col-xl-9"> 546 <div class="col-12 col-lg-8 col-xl-9">
@@ -568,7 +568,7 @@
568 568
569 <div class="row mt-4"> <!-- Twitter grid --> 569 <div class="row mt-4"> <!-- Twitter grid -->
570 <div class="col-12 col-lg-4 col-xl-3"> 570 <div class="col-12 col-lg-4 col-xl-3">
571 <div i18n class="inner-form-title">TWITTER</div> 571 <h2 i18n class="inner-form-title">TWITTER</h2>
572 <div i18n class="inner-form-description"> 572 <div i18n class="inner-form-description">
573 Provide the Twitter account representing your instance to improve link previews. 573 Provide the Twitter account representing your instance to improve link previews.
574 If you don't have a Twitter account, just leave the default value. 574 If you don't have a Twitter account, just leave the default value.
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-homepage.component.html b/client/src/app/+admin/config/edit-custom-config/edit-homepage.component.html
index 3d8414f5c..fd009367f 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-homepage.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-homepage.component.html
@@ -4,7 +4,7 @@
4 4
5 <div class="homepage row mt-5"> <!-- homepage grid --> 5 <div class="homepage row mt-5"> <!-- homepage grid -->
6 <div class="col-12 col-lg-4 col-xl-3"> 6 <div class="col-12 col-lg-4 col-xl-3">
7 <div i18n class="inner-form-title">INSTANCE HOMEPAGE</div> 7 <h2 i18n class="inner-form-title">INSTANCE HOMEPAGE</h2>
8 </div> 8 </div>
9 9
10 <div class="col-12 col-lg-8 col-xl-9"> 10 <div class="col-12 col-lg-8 col-xl-9">
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html b/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html
index ff79ecc88..60f1aee2e 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html
@@ -4,7 +4,7 @@
4 4
5 <div class="row mt-5"> <!-- instance grid --> 5 <div class="row mt-5"> <!-- instance grid -->
6 <div class="col-12 col-lg-4 col-xl-3"> 6 <div class="col-12 col-lg-4 col-xl-3">
7 <div i18n class="inner-form-title">INSTANCE</div> 7 <h2 i18n class="inner-form-title">INSTANCE</h2>
8 </div> 8 </div>
9 9
10 <div class="col-12 col-lg-8 col-xl-9"> 10 <div class="col-12 col-lg-8 col-xl-9">
@@ -76,7 +76,7 @@
76 76
77 <div class="row mt-4"> <!-- moderation & nsfw grid --> 77 <div class="row mt-4"> <!-- moderation & nsfw grid -->
78 <div class="col-12 col-lg-4 col-xl-3"> 78 <div class="col-12 col-lg-4 col-xl-3">
79 <div i18n class="inner-form-title">MODERATION & NSFW</div> 79 <h2 i18n class="inner-form-title">MODERATION & NSFW</h2>
80 <div i18n class="inner-form-description"> 80 <div i18n class="inner-form-description">
81 Manage <a class="link-orange" routerLink="/admin/users">users</a> to build a moderation team. 81 Manage <a class="link-orange" routerLink="/admin/users">users</a> to build a moderation team.
82 </div> 82 </div>
@@ -154,7 +154,7 @@
154 154
155 <div class="row mt-4"> <!-- you and your instance grid --> 155 <div class="row mt-4"> <!-- you and your instance grid -->
156 <div class="col-12 col-lg-4 col-xl-3"> 156 <div class="col-12 col-lg-4 col-xl-3">
157 <div i18n class="inner-form-title">YOU AND YOUR INSTANCE</div> 157 <h2 i18n class="inner-form-title">YOU AND YOUR INSTANCE</h2>
158 </div> 158 </div>
159 159
160 <div class="col-12 col-lg-8 col-xl-9"> 160 <div class="col-12 col-lg-8 col-xl-9">
@@ -204,7 +204,7 @@
204 204
205 <div class="row mt-4"> <!-- other information grid --> 205 <div class="row mt-4"> <!-- other information grid -->
206 <div class="col-12 col-lg-4 col-xl-3"> 206 <div class="col-12 col-lg-4 col-xl-3">
207 <div i18n class="inner-form-title">OTHER INFORMATION</div> 207 <h2 i18n class="inner-form-title">OTHER INFORMATION</h2>
208 </div> 208 </div>
209 209
210 <div class="col-12 col-lg-8 col-xl-9"> 210 <div class="col-12 col-lg-8 col-xl-9">
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.html b/client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.html
index e9919741f..1ba3bdfe0 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.html
@@ -2,7 +2,7 @@
2 2
3 <div class="row mt-5"> 3 <div class="row mt-5">
4 <div class="col-12 col-lg-4 col-xl-3"> 4 <div class="col-12 col-lg-4 col-xl-3">
5 <div i18n class="inner-form-title">LIVE</div> 5 <h2 i18n class="inner-form-title">LIVE</h2>
6 <div i18n class="inner-form-description"> 6 <div i18n class="inner-form-description">
7 Enable users of your instance to stream live. 7 Enable users of your instance to stream live.
8 </div> 8 </div>
@@ -89,7 +89,7 @@
89 89
90 <div class="row"> <!-- transcoding live streams grid --> 90 <div class="row"> <!-- transcoding live streams grid -->
91 <div class="col-12 col-lg-4 col-xl-3"> 91 <div class="col-12 col-lg-4 col-xl-3">
92 <div i18n class="inner-form-title">TRANSCODING</div> 92 <h2 i18n class="inner-form-title">TRANSCODING</h2>
93 <div i18n class="inner-form-description"> 93 <div i18n class="inner-form-description">
94 Same as VOD transcoding, transcoding live streams so that they are in a streamable form that any device can play. Requires a beefy CPU, and then some. 94 Same as VOD transcoding, transcoding live streams so that they are in a streamable form that any device can play. Requires a beefy CPU, and then some.
95 </div> 95 </div>
@@ -111,7 +111,7 @@
111 </div> 111 </div>
112 112
113 <div class="callout callout-light pt-2 mt-2 pb-0"> 113 <div class="callout callout-light pt-2 mt-2 pb-0">
114 <label i18n>Output formats</label> 114 <h3 class="callout-title" i18n>Output formats</h3>
115 115
116 <div class="form-group" [ngClass]="getDisabledLiveTranscodingClass()"> 116 <div class="form-group" [ngClass]="getDisabledLiveTranscodingClass()">
117 <label i18n for="liveTranscodingThreads">Live resolutions to generate</label> 117 <label i18n for="liveTranscodingThreads">Live resolutions to generate</label>
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.html b/client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.html
index d3fc2e481..fb750aca6 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.html
@@ -18,7 +18,7 @@
18 18
19 <div class="row mt-4"> <!-- transcoding grid --> 19 <div class="row mt-4"> <!-- transcoding grid -->
20 <div class="col-12 col-lg-4 col-xl-3"> 20 <div class="col-12 col-lg-4 col-xl-3">
21 <div i18n class="inner-form-title">TRANSCODING</div> 21 <h2 i18n class="inner-form-title">TRANSCODING</h2>
22 <div i18n class="inner-form-description"> 22 <div i18n class="inner-form-description">
23 Process uploaded videos so that they are in a streamable form that any device can play. Though costly in 23 Process uploaded videos so that they are in a streamable form that any device can play. Though costly in
24 resources, this is a critical part of PeerTube, so tread carefully. 24 resources, this is a critical part of PeerTube, so tread carefully.
@@ -38,7 +38,7 @@
38 <ng-container ngProjectAs="extra"> 38 <ng-container ngProjectAs="extra">
39 39
40 <div class="callout callout-light pt-2 pb-0"> 40 <div class="callout callout-light pt-2 pb-0">
41 <label i18n>Input formats</label> 41 <h3 class="callout-title" i18n>Input formats</h3>
42 42
43 <div class="form-group" [ngClass]="getTranscodingDisabledClass()"> 43 <div class="form-group" [ngClass]="getTranscodingDisabledClass()">
44 <my-peertube-checkbox 44 <my-peertube-checkbox
@@ -65,7 +65,7 @@
65 </div> 65 </div>
66 66
67 <div class="callout callout-light pt-2 mt-2 pb-0"> 67 <div class="callout callout-light pt-2 mt-2 pb-0">
68 <label i18n>Output formats</label> 68 <h3 class="callout-title" i18n>Output formats</h3>
69 69
70 <ng-container formGroupName="webtorrent"> 70 <ng-container formGroupName="webtorrent">
71 <div class="form-group" [ngClass]="getTranscodingDisabledClass()"> 71 <div class="form-group" [ngClass]="getTranscodingDisabledClass()">
@@ -108,7 +108,7 @@
108 </ng-container> 108 </ng-container>
109 109
110 <div class="form-group" [ngClass]="getTranscodingDisabledClass()"> 110 <div class="form-group" [ngClass]="getTranscodingDisabledClass()">
111 <label i18n>Resolutions to generate</label> 111 <div class="mb-2 fw-bold" i18n>Resolutions to generate</div>
112 112
113 <div class="ms-2 d-flex flex-column"> 113 <div class="ms-2 d-flex flex-column">
114 <my-peertube-checkbox 114 <my-peertube-checkbox
@@ -211,7 +211,7 @@
211 211
212 <div class="row mt-2"> <!-- video studio grid --> 212 <div class="row mt-2"> <!-- video studio grid -->
213 <div class="col-12 col-lg-4 col-xl-3"> 213 <div class="col-12 col-lg-4 col-xl-3">
214 <div i18n class="inner-form-title">VIDEO STUDIO</div> 214 <h2 i18n class="inner-form-title">VIDEO STUDIO</h2>
215 <div i18n class="inner-form-description"> 215 <div i18n class="inner-form-description">
216 Allows your users to edit their video (cut, add intro/outro, add a watermark etc) 216 Allows your users to edit their video (cut, add intro/outro, add a watermark etc)
217 </div> 217 </div>
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.html b/client/src/app/+admin/follows/following-list/following-list.component.html
index 1605190f6..f46f36375 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.html
+++ b/client/src/app/+admin/follows/following-list/following-list.component.html
@@ -20,10 +20,10 @@
20 > 20 >
21 </my-action-dropdown> 21 </my-action-dropdown>
22 22
23 <a *ngIf="!isInSelectionMode()" class="follow-button" (click)="openFollowModal()" (key.enter)="openFollowModal()"> 23 <button *ngIf="!isInSelectionMode()" class="peertube-create-button" (click)="openFollowModal()">
24 <my-global-icon iconName="following" aria-hidden="true"></my-global-icon> 24 <my-global-icon iconName="following" aria-hidden="true"></my-global-icon>
25 <ng-container i18n>Follow</ng-container> 25 <ng-container i18n>Follow</ng-container>
26 </a> 26 </button>
27 </div> 27 </div>
28 28
29 <div class="ms-auto"> 29 <div class="ms-auto">
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.scss b/client/src/app/+admin/follows/following-list/following-list.component.scss
index 0de80e563..405ddae5c 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.scss
+++ b/client/src/app/+admin/follows/following-list/following-list.component.scss
@@ -16,10 +16,6 @@ a {
16 } 16 }
17} 17}
18 18
19.follow-button {
20 @include create-button;
21}
22
23my-delete-button { 19my-delete-button {
24 max-width: 130px; 20 max-width: 130px;
25} 21}
diff --git a/client/src/app/+admin/overview/users/user-edit/user-edit.component.html b/client/src/app/+admin/overview/users/user-edit/user-edit.component.html
index ce3226857..7b3eadac2 100644
--- a/client/src/app/+admin/overview/users/user-edit/user-edit.component.html
+++ b/client/src/app/+admin/overview/users/user-edit/user-edit.component.html
@@ -219,17 +219,17 @@
219 219
220 <div class="danger-zone"> 220 <div class="danger-zone">
221 <div class="form-group"> 221 <div class="form-group">
222 <label i18n>Send a link to reset the password by email to the user</label> 222 <div class="mb-1 fw-bold" i18n>Send a link to reset the password by email to the user</div>
223 <button (click)="resetPassword()" i18n>Ask for new password</button> 223 <button (click)="resetPassword()" i18n>Ask for new password</button>
224 </div> 224 </div>
225 225
226 <div class="form-group"> 226 <div class="form-group">
227 <label i18n>Manually set the user password</label> 227 <div class="mb-1 fw-bold" i18n>Manually set the user password</div>
228 <my-user-password [userId]="user.id" [username]="user.username"></my-user-password> 228 <my-user-password [userId]="user.id" [username]="user.username"></my-user-password>
229 </div> 229 </div>
230 230
231 <div *ngIf="user.twoFactorEnabled" class="form-group"> 231 <div *ngIf="user.twoFactorEnabled" class="form-group">
232 <label i18n>This user has two factor authentication enabled</label> 232 <div class="mb-1 fw-bold" i18n>This user has two factor authentication enabled</div>
233 <button (click)="disableTwoFactorAuth()" i18n>Disable two factor authentication</button> 233 <button (click)="disableTwoFactorAuth()" i18n>Disable two factor authentication</button>
234 </div> 234 </div>
235 </div> 235 </div>
diff --git a/client/src/app/+admin/overview/users/user-list/user-list.component.html b/client/src/app/+admin/overview/users/user-list/user-list.component.html
index b7467d2cb..8c90f5a45 100644
--- a/client/src/app/+admin/overview/users/user-list/user-list.component.html
+++ b/client/src/app/+admin/overview/users/user-list/user-list.component.html
@@ -20,7 +20,7 @@
20 > 20 >
21 </my-action-dropdown> 21 </my-action-dropdown>
22 22
23 <a *ngIf="!isInSelectionMode()" class="add-button" routerLink="/admin/users/create"> 23 <a *ngIf="!isInSelectionMode()" class="peertube-create-button" routerLink="/admin/users/create">
24 <my-global-icon iconName="user-add" aria-hidden="true"></my-global-icon> 24 <my-global-icon iconName="user-add" aria-hidden="true"></my-global-icon>
25 <ng-container i18n>Create user</ng-container> 25 <ng-container i18n>Create user</ng-container>
26 </a> 26 </a>
diff --git a/client/src/app/+admin/overview/users/user-list/user-list.component.scss b/client/src/app/+admin/overview/users/user-list/user-list.component.scss
index 2a3b955d2..559a00251 100644
--- a/client/src/app/+admin/overview/users/user-list/user-list.component.scss
+++ b/client/src/app/+admin/overview/users/user-list/user-list.component.scss
@@ -2,10 +2,6 @@
2@use '_mixins' as *; 2@use '_mixins' as *;
3@use 'bootstrap/scss/functions' as *; 3@use 'bootstrap/scss/functions' as *;
4 4
5.add-button {
6 @include create-button;
7}
8
9tr.banned > td { 5tr.banned > td {
10 background-color: lighten($color: $red, $amount: 40) !important; 6 background-color: lighten($color: $red, $amount: 40) !important;
11} 7}
diff --git a/client/src/app/+login/login.component.html b/client/src/app/+login/login.component.html
index 40049390e..8ccc73617 100644
--- a/client/src/app/+login/login.component.html
+++ b/client/src/app/+login/login.component.html
@@ -81,10 +81,12 @@
81 <input type="submit" class="peertube-button orange-button w-100" i18n-value value="Login" [disabled]="!form.valid"> 81 <input type="submit" class="peertube-button orange-button w-100" i18n-value value="Login" [disabled]="!form.valid">
82 82
83 <div *ngIf="!otpStep" class="additional-links d-flex justify-content-center mt-4 mb-5"> 83 <div *ngIf="!otpStep" class="additional-links d-flex justify-content-center mt-4 mb-5">
84 <a i18n role="button" class="link-orange mx-3" (click)="openForgotPasswordModal()" i18n-title title="Click here to reset your password">I forgot my password</a> 84 <button i18n class="button-unstyle link-orange mx-3" (click)="openForgotPasswordModal()" i18n-title title="Click here to reset your password">
85 I forgot my password
86 </button>
85 87
86 <ng-container *ngIf="signupAllowed"> 88 <ng-container *ngIf="signupAllowed">
87 <span>·</span> 89 <span class="lh-1">·</span>
88 <a i18n routerLink="/signup" class="link-orange mx-3">Create an account</a> 90 <a i18n routerLink="/signup" class="link-orange mx-3">Create an account</a>
89 </ng-container> 91 </ng-container>
90 </div> 92 </div>
diff --git a/client/src/app/+login/login.component.ts b/client/src/app/+login/login.component.ts
index ba0d412d6..1e224db8c 100644
--- a/client/src/app/+login/login.component.ts
+++ b/client/src/app/+login/login.component.ts
@@ -85,8 +85,8 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
85 85
86 // Avoid undefined errors when accessing form error properties 86 // Avoid undefined errors when accessing form error properties
87 this.buildForm({ 87 this.buildForm({
88 username: LOGIN_USERNAME_VALIDATOR, 88 'username': LOGIN_USERNAME_VALIDATOR,
89 password: LOGIN_PASSWORD_VALIDATOR, 89 'password': LOGIN_PASSWORD_VALIDATOR,
90 'otp-token': { 90 'otp-token': {
91 VALIDATORS: [], // Will be set dynamically 91 VALIDATORS: [], // Will be set dynamically
92 MESSAGES: USER_OTP_TOKEN_VALIDATOR.MESSAGES 92 MESSAGES: USER_OTP_TOKEN_VALIDATOR.MESSAGES
diff --git a/client/src/app/+manage/video-channel-edit/video-channel-create.component.ts b/client/src/app/+manage/video-channel-edit/video-channel-create.component.ts
index 372066890..8ca94b0b3 100644
--- a/client/src/app/+manage/video-channel-edit/video-channel-create.component.ts
+++ b/client/src/app/+manage/video-channel-edit/video-channel-create.component.ts
@@ -38,10 +38,10 @@ export class VideoChannelCreateComponent extends VideoChannelEdit implements OnI
38 38
39 ngOnInit () { 39 ngOnInit () {
40 this.buildForm({ 40 this.buildForm({
41 name: VIDEO_CHANNEL_NAME_VALIDATOR, 41 'name': VIDEO_CHANNEL_NAME_VALIDATOR,
42 'display-name': VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR, 42 'display-name': VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR,
43 description: VIDEO_CHANNEL_DESCRIPTION_VALIDATOR, 43 'description': VIDEO_CHANNEL_DESCRIPTION_VALIDATOR,
44 support: VIDEO_CHANNEL_SUPPORT_VALIDATOR 44 'support': VIDEO_CHANNEL_SUPPORT_VALIDATOR
45 }) 45 })
46 } 46 }
47 47
diff --git a/client/src/app/+manage/video-channel-edit/video-channel-edit.component.html b/client/src/app/+manage/video-channel-edit/video-channel-edit.component.html
index 966a350d1..58f65d994 100644
--- a/client/src/app/+manage/video-channel-edit/video-channel-edit.component.html
+++ b/client/src/app/+manage/video-channel-edit/video-channel-edit.component.html
@@ -10,8 +10,6 @@
10 </div> 10 </div>
11 11
12 <div class="col-12 col-lg-8 col-xl-9"> 12 <div class="col-12 col-lg-8 col-xl-9">
13 <label i18n>Banner image of the channel</label>
14
15 <my-actor-banner-edit 13 <my-actor-banner-edit
16 *ngIf="videoChannel" [previewImage]="isCreation()" class="d-block mb-4" 14 *ngIf="videoChannel" [previewImage]="isCreation()" class="d-block mb-4"
17 [actor]="videoChannel" (bannerChange)="onBannerChange($event)" (bannerDelete)="onBannerDelete()" 15 [actor]="videoChannel" (bannerChange)="onBannerChange($event)" (bannerDelete)="onBannerDelete()"
diff --git a/client/src/app/+manage/video-channel-edit/video-channel-update.component.ts b/client/src/app/+manage/video-channel-edit/video-channel-update.component.ts
index 3326a1505..f9045db35 100644
--- a/client/src/app/+manage/video-channel-edit/video-channel-update.component.ts
+++ b/client/src/app/+manage/video-channel-edit/video-channel-update.component.ts
@@ -46,9 +46,9 @@ export class VideoChannelUpdateComponent extends VideoChannelEdit implements OnI
46 46
47 this.buildForm({ 47 this.buildForm({
48 'display-name': VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR, 48 'display-name': VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR,
49 description: VIDEO_CHANNEL_DESCRIPTION_VALIDATOR, 49 'description': VIDEO_CHANNEL_DESCRIPTION_VALIDATOR,
50 support: VIDEO_CHANNEL_SUPPORT_VALIDATOR, 50 'support': VIDEO_CHANNEL_SUPPORT_VALIDATOR,
51 bulkVideosSupportUpdate: null 51 'bulkVideosSupportUpdate': null
52 }) 52 })
53 53
54 this.paramsSub = this.route.params.subscribe(routeParams => { 54 this.paramsSub = this.route.params.subscribe(routeParams => {
@@ -65,8 +65,8 @@ export class VideoChannelUpdateComponent extends VideoChannelEdit implements OnI
65 65
66 this.form.patchValue({ 66 this.form.patchValue({
67 'display-name': videoChannelToUpdate.displayName, 67 'display-name': videoChannelToUpdate.displayName,
68 description: videoChannelToUpdate.description, 68 'description': videoChannelToUpdate.description,
69 support: videoChannelToUpdate.support 69 'support': videoChannelToUpdate.support
70 }) 70 })
71 }, 71 },
72 72
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts
index ebb7ed2da..1e8fa2a56 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts
@@ -29,7 +29,7 @@ export class MyAccountChangeEmailComponent extends FormReactive implements OnIni
29 ngOnInit () { 29 ngOnInit () {
30 this.buildForm({ 30 this.buildForm({
31 'new-email': USER_EMAIL_VALIDATOR, 31 'new-email': USER_EMAIL_VALIDATOR,
32 password: USER_PASSWORD_VALIDATOR 32 'password': USER_PASSWORD_VALIDATOR
33 }) 33 })
34 34
35 this.user = this.authService.getUser() 35 this.user = this.authService.getUser()
diff --git a/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts b/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts
index 8621eb7aa..a2d128190 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts
@@ -25,17 +25,17 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit {
25 25
26 ngOnInit () { 26 ngOnInit () {
27 this.buildForm({ 27 this.buildForm({
28 username: null, 28 'username': null,
29 'display-name': USER_DISPLAY_NAME_REQUIRED_VALIDATOR, 29 'display-name': USER_DISPLAY_NAME_REQUIRED_VALIDATOR,
30 description: USER_DESCRIPTION_VALIDATOR 30 'description': USER_DESCRIPTION_VALIDATOR
31 }) 31 })
32 this.form.controls['username'].disable() 32 this.form.controls['username'].disable()
33 33
34 this.userInformationLoaded.subscribe(() => { 34 this.userInformationLoaded.subscribe(() => {
35 this.form.patchValue({ 35 this.form.patchValue({
36 username: this.user.username, 36 'username': this.user.username,
37 'display-name': this.user.account.displayName, 37 'display-name': this.user.account.displayName,
38 description: this.user.account.description 38 'description': this.user.account.description
39 }) 39 })
40 }) 40 })
41 } 41 }
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html
index 5bef4a6ed..0fe061983 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.html
@@ -18,7 +18,7 @@
18<div class="video-channels-header d-flex justify-content-between gap-2"> 18<div class="video-channels-header d-flex justify-content-between gap-2">
19 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> 19 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
20 20
21 <a class="create-button" routerLink="/manage/create"> 21 <a class="peertube-create-button" routerLink="/manage/create">
22 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> 22 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
23 <ng-container i18n>Create video channel</ng-container> 23 <ng-container i18n>Create video channel</ng-container>
24 </a> 24 </a>
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss
index 6c5be9240..b4907db51 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss
@@ -17,10 +17,6 @@ h1 {
17 } 17 }
18} 18}
19 19
20.create-button {
21 @include create-button;
22}
23
24input[type=text] { 20input[type=text] {
25 @include peertube-input-text(300px); 21 @include peertube-input-text(300px);
26} 22}
diff --git a/client/src/app/+my-library/my-history/my-history.component.html b/client/src/app/+my-library/my-history/my-history.component.html
index 6791dab52..1046a8f8c 100644
--- a/client/src/app/+my-library/my-history/my-history.component.html
+++ b/client/src/app/+my-library/my-history/my-history.component.html
@@ -10,8 +10,8 @@
10 </div> 10 </div>
11 11
12 <div class="history-switch"> 12 <div class="history-switch">
13 <my-input-switch [(ngModel)]="videosHistoryEnabled" (ngModelChange)="onVideosHistoryChange()"></my-input-switch> 13 <my-input-switch inputName="track-watch-history" [(ngModel)]="videosHistoryEnabled" (ngModelChange)="onVideosHistoryChange()"></my-input-switch>
14 <label i18n>Track watch history</label> 14 <label for="track-watch-history" i18n>Track watch history</label>
15 </div> 15 </div>
16 16
17 <button class="delete-history" (click)="clearAllHistory()" i18n> 17 <button class="delete-history" (click)="clearAllHistory()" i18n>
diff --git a/client/src/app/+my-library/my-ownership/my-ownership.component.html b/client/src/app/+my-library/my-ownership/my-ownership.component.html
index d9a4f32bd..5db1a9bb2 100644
--- a/client/src/app/+my-library/my-ownership/my-ownership.component.html
+++ b/client/src/app/+my-library/my-ownership/my-ownership.component.html
@@ -44,7 +44,7 @@
44 <a [href]="videoChangeOwnership.video.url" class="video-table-video-link" [title]="videoChangeOwnership.video.name" target="_blank" rel="noopener noreferrer"> 44 <a [href]="videoChangeOwnership.video.url" class="video-table-video-link" [title]="videoChangeOwnership.video.name" target="_blank" rel="noopener noreferrer">
45 <div class="video-table-video"> 45 <div class="video-table-video">
46 <div class="video-table-video-image"> 46 <div class="video-table-video-image">
47 <img [src]="videoChangeOwnership.video.thumbnailPath"> 47 <img [src]="videoChangeOwnership.video.thumbnailPath" alt="">
48 </div> 48 </div>
49 <div class="video-table-video-text"> 49 <div class="video-table-video-text">
50 <div> 50 <div>
diff --git a/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.html b/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.html
index 538bbd178..5275fb517 100644
--- a/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.html
+++ b/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.html
@@ -20,7 +20,7 @@
20 <ng-template pTemplate="caption"> 20 <ng-template pTemplate="caption">
21 <div class="caption"> 21 <div class="caption">
22 <div class="left-buttons"> 22 <div class="left-buttons">
23 <a class="add-sync" routerLink="{{ getSyncCreateLink() }}"> 23 <a class="peertube-create-button" routerLink="{{ getSyncCreateLink() }}">
24 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> 24 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
25 <ng-container i18n>Add synchronization</ng-container> 25 <ng-container i18n>Add synchronization</ng-container>
26 </a> 26 </a>
diff --git a/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.scss b/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.scss
index 6c09cc9ef..153bfc6b1 100644
--- a/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.scss
+++ b/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.scss
@@ -2,12 +2,9 @@
2@use '_mixins' as *; 2@use '_mixins' as *;
3@use '_actor' as *; 3@use '_actor' as *;
4 4
5.add-sync {
6 @include create-button;
7}
8
9.actor { 5.actor {
10 @include actor-row($min-height: auto, $separator: true); 6 @include actor-row($min-height: auto, $separator: true);
7
11 margin-bottom: 0; 8 margin-bottom: 0;
12 padding-bottom: 0; 9 padding-bottom: 0;
13 border: 0; 10 border: 0;
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html b/client/src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html
index e867f63b8..a3c2aab44 100644
--- a/client/src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html
+++ b/client/src/app/+my-library/my-video-playlists/my-video-playlist-edit.component.html
@@ -61,7 +61,7 @@
61 </div> 61 </div>
62 62
63 <div class="form-group"> 63 <div class="form-group">
64 <label i18n>Channel</label> 64 <label for="videoChannelIdl" i18n>Channel</label>
65 65
66 <my-select-channel 66 <my-select-channel
67 labelForId="videoChannelIdl" [items]="userVideoChannels" formControlName="videoChannelId" 67 labelForId="videoChannelIdl" [items]="userVideoChannels" formControlName="videoChannelId"
@@ -73,7 +73,7 @@
73 </div> 73 </div>
74 74
75 <div class="form-group"> 75 <div class="form-group">
76 <label i18n>Playlist thumbnail</label> 76 <label for="thumbnailfile" i18n>Playlist thumbnail</label>
77 77
78 <my-preview-upload 78 <my-preview-upload
79 i18n-inputLabel inputLabel="Edit" inputName="thumbnailfile" formControlName="thumbnailfile" 79 i18n-inputLabel inputLabel="Edit" inputName="thumbnailfile" formControlName="thumbnailfile"
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html
index 3bab20a22..ebcb0b1fd 100644
--- a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html
+++ b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html
@@ -9,7 +9,7 @@
9<div class="video-playlists-header d-flex justify-content-between gap-2"> 9<div class="video-playlists-header d-flex justify-content-between gap-2">
10 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> 10 <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
11 11
12 <a class="create-button" routerLink="create"> 12 <a class="peertube-create-button" routerLink="create">
13 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> 13 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
14 <ng-container i18n>Create playlist</ng-container> 14 <ng-container i18n>Create playlist</ng-container>
15 </a> 15 </a>
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.scss b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.scss
index 07e50e53f..f22feaa48 100644
--- a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.scss
+++ b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.scss
@@ -5,10 +5,6 @@ h1 {
5 display: flex; 5 display: flex;
6} 6}
7 7
8.create-button {
9 @include create-button;
10}
11
12input[type=text] { 8input[type=text] {
13 @include peertube-input-text(300px); 9 @include peertube-input-text(300px);
14} 10}
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.html b/client/src/app/+my-library/my-videos/my-videos.component.html
index 9bb609ae8..b95287151 100644
--- a/client/src/app/+my-library/my-videos/my-videos.component.html
+++ b/client/src/app/+my-library/my-videos/my-videos.component.html
@@ -46,10 +46,10 @@
46 #videosSelection 46 #videosSelection
47> 47>
48 <ng-template ptTemplate="globalButtons"> 48 <ng-template ptTemplate="globalButtons">
49 <span class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()"> 49 <button class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()">
50 <my-global-icon iconName="delete" aria-hidden="true"></my-global-icon> 50 <my-global-icon iconName="delete" aria-hidden="true"></my-global-icon>
51 <ng-container i18n>Delete</ng-container> 51 <ng-container i18n>Delete</ng-container>
52 </span> 52 </button>
53 </ng-template> 53 </ng-template>
54 54
55 <ng-template ptTemplate="rowButtons" let-video> 55 <ng-template ptTemplate="rowButtons" let-video>
diff --git a/client/src/app/+reset-password/reset-password.component.ts b/client/src/app/+reset-password/reset-password.component.ts
index a6c05bbfb..a99894458 100644
--- a/client/src/app/+reset-password/reset-password.component.ts
+++ b/client/src/app/+reset-password/reset-password.component.ts
@@ -27,7 +27,7 @@ export class ResetPasswordComponent extends FormReactive implements OnInit {
27 27
28 ngOnInit () { 28 ngOnInit () {
29 this.buildForm({ 29 this.buildForm({
30 password: USER_PASSWORD_VALIDATOR, 30 'password': USER_PASSWORD_VALIDATOR,
31 'password-confirm': RESET_PASSWORD_CONFIRM_VALIDATOR 31 'password-confirm': RESET_PASSWORD_CONFIRM_VALIDATOR
32 }) 32 })
33 33
diff --git a/client/src/app/+search/search-filters.component.html b/client/src/app/+search/search-filters.component.html
index 5bce009d5..ed0632a4d 100644
--- a/client/src/app/+search/search-filters.component.html
+++ b/client/src/app/+search/search-filters.component.html
@@ -4,7 +4,7 @@
4 <div class="col-lg-4 col-md-6 col-xs-12"> 4 <div class="col-lg-4 col-md-6 col-xs-12">
5 <div class="form-group"> 5 <div class="form-group">
6 <div class="radio-label label-container"> 6 <div class="radio-label label-container">
7 <label i18n>Sort</label> 7 <label for="sort" i18n>Sort</label>
8 <button i18n class="reset-button reset-button-small" (click)="resetField('sort', '-match')" *ngIf="advancedSearch.sort !== '-match'"> 8 <button i18n class="reset-button reset-button-small" (click)="resetField('sort', '-match')" *ngIf="advancedSearch.sort !== '-match'">
9 Reset 9 Reset
10 </button> 10 </button>
@@ -18,7 +18,7 @@
18 18
19 <div class="form-group"> 19 <div class="form-group">
20 <div class="radio-label label-container"> 20 <div class="radio-label label-container">
21 <label i18n>Display only</label> 21 <label for="isLive" i18n>Display only</label>
22 <button i18n class="reset-button reset-button-small" (click)="resetField('isLive')" *ngIf="advancedSearch.isLive !== undefined"> 22 <button i18n class="reset-button reset-button-small" (click)="resetField('isLive')" *ngIf="advancedSearch.isLive !== undefined">
23 Reset 23 Reset
24 </button> 24 </button>
@@ -37,7 +37,7 @@
37 37
38 <div class="form-group"> 38 <div class="form-group">
39 <div class="radio-label label-container"> 39 <div class="radio-label label-container">
40 <label i18n>Display sensitive content</label> 40 <label for="sensitiveContent" i18n>Display sensitive content</label>
41 <button i18n class="reset-button reset-button-small" (click)="resetField('nsfw')" *ngIf="advancedSearch.nsfw !== undefined"> 41 <button i18n class="reset-button reset-button-small" (click)="resetField('nsfw')" *ngIf="advancedSearch.nsfw !== undefined">
42 Reset 42 Reset
43 </button> 43 </button>
@@ -56,7 +56,7 @@
56 56
57 <div class="form-group"> 57 <div class="form-group">
58 <div class="radio-label label-container"> 58 <div class="radio-label label-container">
59 <label i18n>Published date</label> 59 <label for="publishedDateRange" i18n>Published date</label>
60 <button i18n class="reset-button reset-button-small" (click)="resetLocalField('publishedDateRange')" *ngIf="publishedDateRange !== undefined"> 60 <button i18n class="reset-button reset-button-small" (click)="resetLocalField('publishedDateRange')" *ngIf="publishedDateRange !== undefined">
61 Reset 61 Reset
62 </button> 62 </button>
@@ -105,7 +105,7 @@
105 <div class="col-lg-4 col-md-6 col-xs-12"> 105 <div class="col-lg-4 col-md-6 col-xs-12">
106 <div class="form-group"> 106 <div class="form-group">
107 <div class="radio-label label-container"> 107 <div class="radio-label label-container">
108 <label i18n>Duration</label> 108 <label for="durationRange" i18n>Duration</label>
109 <button i18n class="reset-button reset-button-small" (click)="resetLocalField('durationRange')" *ngIf="durationRange !== undefined"> 109 <button i18n class="reset-button reset-button-small" (click)="resetLocalField('durationRange')" *ngIf="durationRange !== undefined">
110 Reset 110 Reset
111 </button> 111 </button>
@@ -184,7 +184,7 @@
184 184
185 <div class="form-group"> 185 <div class="form-group">
186 <div class="radio-label label-container"> 186 <div class="radio-label label-container">
187 <label i18n>Result types</label> 187 <label for="resultType" i18n>Result types</label>
188 <button i18n class="reset-button reset-button-small" (click)="resetField('resultType')" *ngIf="advancedSearch.resultType !== undefined"> 188 <button i18n class="reset-button reset-button-small" (click)="resetField('resultType')" *ngIf="advancedSearch.resultType !== undefined">
189 Reset 189 Reset
190 </button> 190 </button>
@@ -209,7 +209,7 @@
209 209
210 <div class="form-group" *ngIf="isSearchTargetEnabled()"> 210 <div class="form-group" *ngIf="isSearchTargetEnabled()">
211 <div class="radio-label label-container"> 211 <div class="radio-label label-container">
212 <label i18n>Search target</label> 212 <label for="searchTarget" i18n>Search target</label>
213 </div> 213 </div>
214 214
215 <div class="peertube-radio-container"> 215 <div class="peertube-radio-container">
diff --git a/client/src/app/+search/search.component.html b/client/src/app/+search/search.component.html
index 2530c87b7..fef6f8e8c 100644
--- a/client/src/app/+search/search.component.html
+++ b/client/src/app/+search/search.component.html
@@ -10,8 +10,8 @@
10 <span *ngIf="currentSearch" i18n>for <span class="search-value">{{ currentSearch }}</span></span> 10 <span *ngIf="currentSearch" i18n>for <span class="search-value">{{ currentSearch }}</span></span>
11 </div> 11 </div>
12 12
13 <div 13 <button
14 class="results-filter-button ms-auto" (click)="isSearchFilterCollapsed = !isSearchFilterCollapsed" role="button" 14 class="results-filter-button button-unstyle ms-auto" (click)="isSearchFilterCollapsed = !isSearchFilterCollapsed"
15 [attr.aria-expanded]="!isSearchFilterCollapsed" aria-controls="collapseBasic" 15 [attr.aria-expanded]="!isSearchFilterCollapsed" aria-controls="collapseBasic"
16 > 16 >
17 <span class="icon icon-filter"></span> 17 <span class="icon icon-filter"></span>
@@ -19,7 +19,7 @@
19 Filters 19 Filters
20 <span *ngIf="numberOfFilters() > 0" class="pt-badge badge-secondary">{{ numberOfFilters() }}</span> 20 <span *ngIf="numberOfFilters() > 0" class="pt-badge badge-secondary">{{ numberOfFilters() }}</span>
21 </ng-container> 21 </ng-container>
22 </div> 22 </button>
23 </div> 23 </div>
24 24
25 <div class="results-filter" [ngbCollapse]="isSearchFilterCollapsed" [animation]="true"> 25 <div class="results-filter" [ngbCollapse]="isSearchFilterCollapsed" [animation]="true">
diff --git a/client/src/app/+search/shared/abstract-lazy-load.resolver.ts b/client/src/app/+search/shared/abstract-lazy-load.resolver.ts
index 6940a7a9b..6765ba15e 100644
--- a/client/src/app/+search/shared/abstract-lazy-load.resolver.ts
+++ b/client/src/app/+search/shared/abstract-lazy-load.resolver.ts
@@ -4,7 +4,7 @@ import { ActivatedRouteSnapshot, Router } from '@angular/router'
4import { logger } from '@root-helpers/logger' 4import { logger } from '@root-helpers/logger'
5import { ResultList } from '@shared/models' 5import { ResultList } from '@shared/models'
6 6
7export abstract class AbstractLazyLoadResolver <T> { 7export abstract class AbstractLazyLoadResolver <T> {
8 protected router: Router 8 protected router: Router
9 9
10 resolve (route: ActivatedRouteSnapshot) { 10 resolve (route: ActivatedRouteSnapshot) {
diff --git a/client/src/app/+signup/+register/custom-stepper.component.html b/client/src/app/+signup/+register/custom-stepper.component.html
index f43a46842..f2687e520 100644
--- a/client/src/app/+signup/+register/custom-stepper.component.html
+++ b/client/src/app/+signup/+register/custom-stepper.component.html
@@ -2,9 +2,10 @@
2 <header *ngIf="steps.length > 2"> 2 <header *ngIf="steps.length > 2">
3 <div class="header-steps"> 3 <div class="header-steps">
4 <ng-container *ngFor="let step of steps; let i = index; let isLast = last;"> 4 <ng-container *ngFor="let step of steps; let i = index; let isLast = last;">
5 <div 5 <button
6 class="step-info" [ngClass]="{ active: selectedIndex === i, completed: isCompleted(step), 'c-hand': isAccessible(step) }" [attr.aria-current]="selectedIndex === i" 6 class="step-info button-unstyle" [ngClass]="{ active: selectedIndex === i, completed: isCompleted(step) }"
7 (click)="onClick(i)" 7 [disabled]="!isAccessible(step)"
8 [attr.aria-current]="selectedIndex === i" (click)="onClick(i)"
8 > 9 >
9 <div class="step-index"> 10 <div class="step-index">
10 <span class="visually-hidden" i18n>Step</span> {{ i + 1 }} 11 <span class="visually-hidden" i18n>Step</span> {{ i + 1 }}
@@ -15,7 +16,7 @@
15 </div> 16 </div>
16 17
17 <div class="step-label">{{ step.label }}</div> 18 <div class="step-label">{{ step.label }}</div>
18 </div> 19 </button>
19 20
20 <!-- Do no display if this is the last child --> 21 <!-- Do no display if this is the last child -->
21 <div *ngIf="!isLast" class="connector"></div> 22 <div *ngIf="!isLast" class="connector"></div>
diff --git a/client/src/app/+signup/+register/custom-stepper.component.scss b/client/src/app/+signup/+register/custom-stepper.component.scss
index 919799716..264136b06 100644
--- a/client/src/app/+signup/+register/custom-stepper.component.scss
+++ b/client/src/app/+signup/+register/custom-stepper.component.scss
@@ -72,11 +72,6 @@ header {
72 align-items: center; 72 align-items: center;
73 width: $index-block-height; 73 width: $index-block-height;
74 opacity: 0.5; 74 opacity: 0.5;
75 cursor: default;
76
77 &.c-hand {
78 cursor: pointer;
79 }
80 75
81 &.active, 76 &.active,
82 &.completed { 77 &.completed {
diff --git a/client/src/app/+signup/+register/register.component.html b/client/src/app/+signup/+register/register.component.html
index 86763e801..71bca63e3 100644
--- a/client/src/app/+signup/+register/register.component.html
+++ b/client/src/app/+signup/+register/register.component.html
@@ -92,9 +92,9 @@
92 <button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button> 92 <button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button>
93 93
94 <div class="skip-step"> 94 <div class="skip-step">
95 <span class="underline-orange" role="button" (click)="skipChannelCreation()"> 95 <button class="underline-orange button-unstyle" (click)="skipChannelCreation()">
96 <strong i18n>I don't want to create a channel</strong> 96 <strong i18n>I don't want to create a channel</strong>
97 </span> 97 </button>
98 98
99 <div class="skip-step-description" i18n>You will be able to create a channel later</div> 99 <div class="skip-step-description" i18n>You will be able to create a channel later</div>
100 </div> 100 </div>
@@ -120,7 +120,7 @@
120 [requiresEmailVerification]="requiresEmailVerification" [requiresApproval]="requiresApproval" [instanceName]="instanceName" 120 [requiresEmailVerification]="requiresEmailVerification" [requiresApproval]="requiresApproval" [instanceName]="instanceName"
121 ></my-signup-success-before-email> 121 ></my-signup-success-before-email>
122 122
123 <div *ngIf="signupError" class="steps-button"> 123 <div *ngIf="signupError" class="step-buttons">
124 <button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button> 124 <button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button>
125 </div> 125 </div>
126 </cdk-step> 126 </cdk-step>
diff --git a/client/src/app/+signup/+register/register.component.scss b/client/src/app/+signup/+register/register.component.scss
index 9904e4ab4..ae0fdbb6e 100644
--- a/client/src/app/+signup/+register/register.component.scss
+++ b/client/src/app/+signup/+register/register.component.scss
@@ -44,7 +44,19 @@ my-instance-about-accordion {
44 } 44 }
45 } 45 }
46 46
47 button, 47 > button {
48 @include peertube-button-big;
49
50 &[cdkStepperNext] {
51 @include orange-button;
52 }
53
54 &[cdkStepperPrevious] {
55 @include grey-button;
56 }
57 }
58
59 > button,
48 .skip-step { 60 .skip-step {
49 margin-top: 20px; 61 margin-top: 20px;
50 margin-bottom: 20px; 62 margin-bottom: 20px;
@@ -60,18 +72,6 @@ my-instance-about-accordion {
60 } 72 }
61} 73}
62 74
63button {
64 @include peertube-button-big;
65
66 &[cdkStepperNext] {
67 @include orange-button;
68 }
69
70 &[cdkStepperPrevious] {
71 @include grey-button;
72 }
73}
74
75.done-loader { 75.done-loader {
76 display: flex; 76 display: flex;
77 justify-content: center; 77 justify-content: center;
diff --git a/client/src/app/+video-channels/video-channels.component.html b/client/src/app/+video-channels/video-channels.component.html
index d92aa072d..fff160f2e 100644
--- a/client/src/app/+video-channels/video-channels.component.html
+++ b/client/src/app/+video-channels/video-channels.component.html
@@ -91,12 +91,12 @@
91 <div class="description-html" [innerHTML]="channelDescriptionHTML"></div> 91 <div class="description-html" [innerHTML]="channelDescriptionHTML"></div>
92 </div> 92 </div>
93 93
94 <div *ngIf="hasShowMoreDescription()" class="show-more" role="button" 94 <button *ngIf="hasShowMoreDescription()" class="show-more button-unstyle"
95 (click)="channelDescriptionExpanded = !channelDescriptionExpanded" 95 (click)="channelDescriptionExpanded = !channelDescriptionExpanded"
96 title="Show the complete description" i18n-title i18n 96 title="Show the complete description" i18n-title i18n
97 > 97 >
98 Show more... 98 Show more...
99 </div> 99 </button>
100 100
101 <div class="channel-buttons bottom"> 101 <div class="channel-buttons bottom">
102 <ng-template *ngTemplateOutlet="buttonsTemplate"></ng-template> 102 <ng-template *ngTemplateOutlet="buttonsTemplate"></ng-template>
diff --git a/client/src/app/+video-studio/edit/video-studio-edit.component.html b/client/src/app/+video-studio/edit/video-studio-edit.component.html
index fe74062d2..c29b12157 100644
--- a/client/src/app/+video-studio/edit/video-studio-edit.component.html
+++ b/client/src/app/+video-studio/edit/video-studio-edit.component.html
@@ -72,12 +72,12 @@
72 72
73 <div class="information"> 73 <div class="information">
74 <div> 74 <div>
75 <label i18n>Video before edition</label> 75 <div class="mb-1 fw-bold" i18n>Video before edition</div>
76 <my-embed [video]="video"></my-embed> 76 <my-embed [video]="video"></my-embed>
77 </div> 77 </div>
78 78
79 <div *ngIf="!noEdition()"> 79 <div *ngIf="!noEdition()">
80 <label i18n>Edition tasks:</label> 80 <div class="mb-1 fw-bold" i18n>Edition tasks:</div>
81 81
82 <ol> 82 <ol>
83 <li *ngFor="let task of getTasksSummary()">{{ task }}</li> 83 <li *ngFor="let task of getTasksSummary()">{{ task }}</li>
diff --git a/client/src/app/+video-studio/edit/video-studio-edit.component.ts b/client/src/app/+video-studio/edit/video-studio-edit.component.ts
index 94f7b5d0b..3d618fbe1 100644
--- a/client/src/app/+video-studio/edit/video-studio-edit.component.ts
+++ b/client/src/app/+video-studio/edit/video-studio-edit.component.ts
@@ -43,7 +43,7 @@ export class VideoStudioEditComponent extends FormReactive implements OnInit {
43 } 43 }
44 44
45 this.buildForm({ 45 this.buildForm({
46 cut: { 46 'cut': {
47 start: null, 47 start: null,
48 end: null 48 end: null
49 }, 49 },
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.html b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
index b0da84979..b607dabe9 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.html
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
@@ -22,7 +22,7 @@
22 </div> 22 </div>
23 23
24 <div class="form-group"> 24 <div class="form-group">
25 <label i18n class="label-tags">Tags</label> 25 <label for="label-tags" i18n class="label-tags">Tags</label>
26 26
27 <my-help> 27 <my-help>
28 <ng-template ptTemplate="customHtml"> 28 <ng-template ptTemplate="customHtml">
@@ -170,10 +170,10 @@
170 <div class="captions"> 170 <div class="captions">
171 171
172 <div class="captions-header"> 172 <div class="captions-header">
173 <a (click)="openAddCaptionModal()" class="create-caption"> 173 <button (click)="openAddCaptionModal()" class="peertube-create-button">
174 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> 174 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
175 <ng-container i18n>Add another caption</ng-container> 175 <ng-container i18n>Add another caption</ng-container>
176 </a> 176 </button>
177 </div> 177 </div>
178 178
179 <div class="form-group" *ngFor="let videoCaption of videoCaptions"> 179 <div class="form-group" *ngFor="let videoCaption of videoCaptions">
@@ -187,8 +187,8 @@
187 187
188 <div i18n class="caption-entry-state">Already uploaded on {{ videoCaption.updatedAt | date }} &#10004;</div> 188 <div i18n class="caption-entry-state">Already uploaded on {{ videoCaption.updatedAt | date }} &#10004;</div>
189 189
190 <span i18n class="caption-entry-edit" (click)="openEditCaptionModal(videoCaption)">Edit</span> 190 <button i18n class="caption-entry-edit" (click)="openEditCaptionModal(videoCaption)">Edit</button>
191 <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Delete</span> 191 <button i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Delete</button>
192 </ng-container> 192 </ng-container>
193 193
194 <ng-container *ngIf="videoCaption.action === 'CREATE'"> 194 <ng-container *ngIf="videoCaption.action === 'CREATE'">
@@ -196,7 +196,7 @@
196 196
197 <div i18n class="caption-entry-state caption-entry-state-create">Will be created on update</div> 197 <div i18n class="caption-entry-state caption-entry-state-create">Will be created on update</div>
198 198
199 <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel create</span> 199 <button i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel create</button>
200 </ng-container> 200 </ng-container>
201 201
202 <ng-container *ngIf="videoCaption.action === 'UPDATE'"> 202 <ng-container *ngIf="videoCaption.action === 'UPDATE'">
@@ -204,7 +204,7 @@
204 204
205 <div i18n class="caption-entry-state caption-entry-state-create">Will be edited on update</div> 205 <div i18n class="caption-entry-state caption-entry-state-create">Will be edited on update</div>
206 206
207 <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel edition</span> 207 <button i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel edition</button>
208 </ng-container> 208 </ng-container>
209 209
210 <ng-container *ngIf="videoCaption.action === 'REMOVE'"> 210 <ng-container *ngIf="videoCaption.action === 'REMOVE'">
@@ -212,7 +212,7 @@
212 212
213 <div i18n class="caption-entry-state caption-entry-state-delete">Will be deleted on update</div> 213 <div i18n class="caption-entry-state caption-entry-state-delete">Will be deleted on update</div>
214 214
215 <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel deletion</span> 215 <button i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel deletion</button>
216 </ng-container> 216 </ng-container>
217 </div> 217 </div>
218 </div> 218 </div>
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.scss b/client/src/app/+videos/+video-edit/shared/video-edit.component.scss
index a8075cc6d..1c6f7f5ab 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.scss
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.scss
@@ -29,10 +29,6 @@ my-peertube-checkbox {
29 margin-bottom: 1rem; 29 margin-bottom: 1rem;
30} 30}
31 31
32.create-caption {
33 @include create-button;
34}
35
36.caption-entry { 32.caption-entry {
37 display: flex; 33 display: flex;
38 height: 40px; 34 height: 40px;
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.html b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.html
index e27942e66..a003a10eb 100644
--- a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.html
+++ b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.html
@@ -64,7 +64,7 @@
64 (timestampClicked)="handleTimestampClicked($event)" 64 (timestampClicked)="handleTimestampClicked($event)"
65 [redraftValue]="commentReplyRedraftValue" 65 [redraftValue]="commentReplyRedraftValue"
66 > 66 >
67 <div *ngIf="comment.totalReplies !== 0 && !threadComments[comment.id]" (click)="viewReplies(comment.id)" class="view-replies mb-2"> 67 <button *ngIf="comment.totalReplies !== 0 && !threadComments[comment.id]" (click)="viewReplies(comment.id)" class="view-replies mb-2 button-unstyle">
68 <span class="chevron-down"></span> 68 <span class="chevron-down"></span>
69 69
70 <ng-container *ngIf="comment.totalRepliesFromVideoAuthor > 0; then hasAuthorComments; else noAuthorComments"></ng-container> 70 <ng-container *ngIf="comment.totalRepliesFromVideoAuthor > 0; then hasAuthorComments; else noAuthorComments"></ng-container>
@@ -81,7 +81,7 @@
81 <ng-template i18n #noAuthorComments>View {comment.totalReplies, plural, =1 {1 reply} other {{{ comment.totalReplies }} replies}}</ng-template> 81 <ng-template i18n #noAuthorComments>View {comment.totalReplies, plural, =1 {1 reply} other {{{ comment.totalReplies }} replies}}</ng-template>
82 82
83 <my-loader size="sm" class="ms-1" [loading]="threadLoading[comment.id]"></my-loader> 83 <my-loader size="sm" class="ms-1" [loading]="threadLoading[comment.id]"></my-loader>
84 </div> 84 </button>
85 </my-video-comment> 85 </my-video-comment>
86 86
87 </div> 87 </div>
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.scss b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.scss
index 5001ad168..7720fe43b 100644
--- a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.scss
+++ b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.scss
@@ -24,16 +24,11 @@
24 @include margin-left(5px); 24 @include margin-left(5px);
25 25
26 display: inline-block; 26 display: inline-block;
27 opacity: 0;
28 transition: ease-in .2s opacity; 27 transition: ease-in .2s opacity;
29 width: 12px; 28 width: 12px;
30 position: relative; 29 position: relative;
31 top: -3px; 30 top: -3px;
32 } 31 }
33
34 &:hover my-feed {
35 opacity: 1;
36 }
37} 32}
38 33
39#dropdown-sort-comments { 34#dropdown-sort-comments {
diff --git a/client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.html b/client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.html
index 7677ae836..93c4ba7a7 100644
--- a/client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.html
+++ b/client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.html
@@ -6,10 +6,11 @@
6 the sharing system used for this video implies that some technical information about your system (such as a public IP address) can be sent to other peers. 6 the sharing system used for this video implies that some technical information about your system (such as a public IP address) can be sent to other peers.
7 </ng-container> 7 </ng-container>
8 </span> 8 </span>
9
9 <a i18n i18n-title title="Get more information" target="_blank" rel="noopener noreferrer" href="/about/peertube#privacy">More information</a> 10 <a i18n i18n-title title="Get more information" target="_blank" rel="noopener noreferrer" href="/about/peertube#privacy">More information</a>
10 </div> 11 </div>
11 12
12 <div i18n class="privacy-concerns-button privacy-concerns-okay" (click)="acceptedPrivacyConcern()"> 13 <button i18n class="ms-2 peertube-button orange-button" (click)="acceptedPrivacyConcern()">
13 OK 14 OK
14 </div> 15 </button>
15</div> 16</div>
diff --git a/client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.scss b/client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.scss
index a6479c7ec..f7f9dfd2f 100644
--- a/client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.scss
+++ b/client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.scss
@@ -50,31 +50,10 @@ a {
50 transition: color 0.3s; 50 transition: color 0.3s;
51 51
52 &:hover { 52 &:hover {
53 color: #fff; 53 color: pvar(--mainBackgroundColor);
54 } 54 }
55} 55}
56 56
57.privacy-concerns-button {
58 @include margin-left(auto);
59
60 padding: 5px 8px 5px 7px;
61 border-radius: 3px;
62 white-space: nowrap;
63 cursor: pointer;
64 transition: background-color 0.3s;
65 font-weight: $font-semibold;
66
67 &:hover {
68 background-color: #000;
69 }
70}
71
72.privacy-concerns-okay {
73 @include margin-left(10px);
74
75 background-color: pvar(--mainColor);
76}
77
78@media screen and (max-width: 1300px) { 57@media screen and (max-width: 1300px) {
79 .privacy-concerns { 58 .privacy-concerns {
80 font-size: 12px; 59 font-size: 12px;
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html
index d847daff7..9db3018e6 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html
@@ -6,14 +6,20 @@
6 myTimestampRouteTransformer 6 myTimestampRouteTransformer
7 ></div> 7 ></div>
8 8
9 <div class="video-info-description-more" *ngIf="completeDescriptionShown === false && video.description?.length >= 250" (click)="showMoreDescription()"> 9 <button
10 *ngIf="completeDescriptionShown === false && video.description?.length >= 250"
11 (click)="showMoreDescription()" class="video-info-description-more button-unstyle"
12 >
10 <ng-container i18n>Show more</ng-container> 13 <ng-container i18n>Show more</ng-container>
11 <span *ngIf="descriptionLoading === false" class="chevron-down"></span> 14 <span *ngIf="descriptionLoading === false" class="chevron-down"></span>
12 <my-loader size="sm" class="description-loading" [loading]="descriptionLoading"></my-loader> 15 <my-loader size="sm" class="description-loading" [loading]="descriptionLoading"></my-loader>
13 </div> 16 </button>
14 17
15 <div *ngIf="completeDescriptionShown === true" (click)="showLessDescription()" class="video-info-description-more"> 18 <button
19 *ngIf="completeDescriptionShown === true"
20 (click)="showLessDescription()" class="video-info-description-more button-unstyle"
21 >
16 <ng-container i18n>Show less</ng-container> 22 <ng-container i18n>Show less</ng-container>
17 <span *ngIf="descriptionLoading === false" class="chevron-up"></span> 23 <span *ngIf="descriptionLoading === false" class="chevron-up"></span>
18 </div> 24 </button>
19</div> 25</div>
diff --git a/client/src/app/core/hotkeys/hotkeys.component.html b/client/src/app/core/hotkeys/hotkeys.component.html
index 61f03be33..b7a6b376b 100644
--- a/client/src/app/core/hotkeys/hotkeys.component.html
+++ b/client/src/app/core/hotkeys/hotkeys.component.html
@@ -13,6 +13,6 @@
13 </tbody> 13 </tbody>
14 </table> 14 </table>
15 </div> 15 </div>
16 <div class="cfp-hotkeys-close" (click)="toggleCheatSheet()">&#215;</div> 16 <button class="button-unstyle cfp-hotkeys-close" (click)="toggleCheatSheet()">&#215;</button>
17 </div> 17 </div>
18</div> \ No newline at end of file 18</div>
diff --git a/client/src/app/header/search-typeahead.component.html b/client/src/app/header/search-typeahead.component.html
index 783b4b53b..c5ca4b9b9 100644
--- a/client/src/app/header/search-typeahead.component.html
+++ b/client/src/app/header/search-typeahead.component.html
@@ -14,7 +14,8 @@
14 <ul [hidden]="!search || !areSuggestionsOpened" role="listbox" class="p-0 m-0"> 14 <ul [hidden]="!search || !areSuggestionsOpened" role="listbox" class="p-0 m-0">
15 <li 15 <li
16 *ngFor="let result of results; let i = index" class="suggestion d-flex flex-justify-start flex-items-center p-0 f5" 16 *ngFor="let result of results; let i = index" class="suggestion d-flex flex-justify-start flex-items-center p-0 f5"
17 role="option" aria-selected="true" (mouseenter)="onSuggestionHover(i)" (click)="onSuggestionClicked(result)" 17 role="option" aria-selected="true" tabindex="0"
18 (mouseenter)="onSuggestionHover(i)" (click)="onSuggestionClicked(result)" (keyup.enter)="onSuggestionClicked(result)"
18 > 19 >
19 <my-suggestion [result]="result" [highlight]="search"></my-suggestion> 20 <my-suggestion [result]="result" [highlight]="search"></my-suggestion>
20 </li> 21 </li>
@@ -23,7 +24,7 @@
23 <!-- suggestion help, not shown until one of the suggestions is selected and specific to that suggestion --> 24 <!-- suggestion help, not shown until one of the suggestions is selected and specific to that suggestion -->
24 <div *ngIf="showSearchGlobalHelp()" id="typeahead-help" class="overflow-hidden"> 25 <div *ngIf="showSearchGlobalHelp()" id="typeahead-help" class="overflow-hidden">
25 <div class="d-flex justify-content-between"> 26 <div class="d-flex justify-content-between">
26 <label class="small-title" i18n>GLOBAL SEARCH</label> 27 <div class="small-title" i18n>GLOBAL SEARCH</div>
27 <div class="advanced-search-status muted"> 28 <div class="advanced-search-status muted">
28 <span *ngIf="serverConfig" class="me-1" i18n>using {{ serverConfig.search.searchIndex.url }}</span> 29 <span *ngIf="serverConfig" class="me-1" i18n>using {{ serverConfig.search.searchIndex.url }}</span>
29 </div> 30 </div>
@@ -35,7 +36,7 @@
35 <div *ngIf="areInstructionsDisplayed()" id="typeahead-instructions" class="overflow-hidden"> 36 <div *ngIf="areInstructionsDisplayed()" id="typeahead-instructions" class="overflow-hidden">
36 <span class="muted" i18n>Your query will be matched against video names or descriptions, channel names.</span> 37 <span class="muted" i18n>Your query will be matched against video names or descriptions, channel names.</span>
37 <div class="d-flex justify-content-between mt-3"> 38 <div class="d-flex justify-content-between mt-3">
38 <label class="small-title" i18n>ADVANCED SEARCH</label> 39 <div class="small-title" i18n>ADVANCED SEARCH</div>
39 <div class="advanced-search-status c-help"> 40 <div class="advanced-search-status c-help">
40 <span [ngClass]="canSearchAnyURI ? 'text-success' : 'muted'" i18n-title title="Determines whether you can resolve any distant content, or if this instance only allows doing so for instances it follows."> 41 <span [ngClass]="canSearchAnyURI ? 'text-success' : 'muted'" i18n-title title="Determines whether you can resolve any distant content, or if this instance only allows doing so for instances it follows.">
41 <span *ngIf="canSearchAnyURI()" class="me-1" i18n>any instance</span> 42 <span *ngIf="canSearchAnyURI()" class="me-1" i18n>any instance</span>
diff --git a/client/src/app/menu/menu.component.html b/client/src/app/menu/menu.component.html
index 8b90fb78b..10b3f15d9 100644
--- a/client/src/app/menu/menu.component.html
+++ b/client/src/app/menu/menu.component.html
@@ -5,7 +5,7 @@
5 <div> 5 <div>
6 <div 6 <div
7 class="logged-in-more" ngbDropdown #dropdown="ngbDropdown" placement="bottom-left auto" 7 class="logged-in-more" ngbDropdown #dropdown="ngbDropdown" placement="bottom-left auto"
8 container="body" (openChange)="onDropdownOpenChange($event)" autoClose="outside" 8 container="body" (openChange)="onDropdownOpenChange($event)"
9 > 9 >
10 <button class="border-0 text-start" ngbDropdownToggle> 10 <button class="border-0 text-start" ngbDropdownToggle>
11 <my-actor-avatar [actor]="user.account" actorType="account" size="34"></my-actor-avatar> 11 <my-actor-avatar [actor]="user.account" actorType="account" size="34"></my-actor-avatar>
@@ -33,21 +33,21 @@
33 33
34 <button 34 <button
35 myPluginSelector pluginSelectorId="menu-user-dropdown-language-item" 35 myPluginSelector pluginSelectorId="menu-user-dropdown-language-item"
36 ngbDropdownItem ngbDropdownToggle class="dropdown-item" (click)="openLanguageChooser()" 36 ngbDropdownItem class="dropdown-item" (click)="openLanguageChooser()"
37 > 37 >
38 <my-global-icon iconName="language" aria-hidden="true"></my-global-icon> 38 <my-global-icon iconName="language" aria-hidden="true"></my-global-icon>
39 <span i18n>Interface:</span> 39 <span i18n>Interface:</span>
40 <span class="ms-auto muted">{{ currentInterfaceLanguage }}</span> 40 <span class="ms-auto muted">{{ currentInterfaceLanguage }}</span>
41 </button> 41 </button>
42 42
43 <a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings" fragment="video-languages-subtitles" 43 <a ngbDropdownItem class="dropdown-item" routerLink="/my-account/settings" fragment="video-languages-subtitles"
44 #settingsLanguagesSubtitles (click)="onActiveLinkScrollToAnchor(settingsLanguagesSubtitles)"> 44 #settingsLanguagesSubtitles (click)="onActiveLinkScrollToAnchor(settingsLanguagesSubtitles)">
45 <my-global-icon iconName="video-lang" aria-hidden="true"></my-global-icon> 45 <my-global-icon iconName="video-lang" aria-hidden="true"></my-global-icon>
46 <span i18n>Videos:</span> 46 <span i18n>Videos:</span>
47 <span class="ms-auto muted">{{ videoLanguages.join(', ') }}</span> 47 <span class="ms-auto muted">{{ videoLanguages.join(', ') }}</span>
48 </a> 48 </a>
49 49
50 <a ngbDropdownItem ngbDropdownToggle class="dropdown-item settings-sensitive" routerLink="/my-account/settings" 50 <a ngbDropdownItem class="dropdown-item settings-sensitive" routerLink="/my-account/settings"
51 fragment="video-sensitive-content-policy" #settingsSensitiveContentPolicy 51 fragment="video-sensitive-content-policy" #settingsSensitiveContentPolicy
52 (click)="onActiveLinkScrollToAnchor(settingsSensitiveContentPolicy)" 52 (click)="onActiveLinkScrollToAnchor(settingsSensitiveContentPolicy)"
53 > 53 >
@@ -57,7 +57,7 @@
57 <span class="ms-auto muted">{{ nsfwPolicy }}</span> 57 <span class="ms-auto muted">{{ nsfwPolicy }}</span>
58 </a> 58 </a>
59 59
60 <button ngbDropdownItem class="dropdown-item" (click)="toggleUseP2P()"> 60 <button class="dropdown-item" (click)="toggleUseP2P()" (mousedown)="$event.stopPropagation()" ngbDropdownItem>
61 <my-global-icon iconName="p2p" aria-hidden="true"></my-global-icon> 61 <my-global-icon iconName="p2p" aria-hidden="true"></my-global-icon>
62 <ng-container i18n>Help share videos</ng-container> 62 <ng-container i18n>Help share videos</ng-container>
63 63
@@ -66,15 +66,15 @@
66 66
67 <div class="dropdown-divider"></div> 67 <div class="dropdown-divider"></div>
68 68
69 <a *ngIf="!isInMobileView" ngbDropdownItem ngbDropdownToggle class="dropdown-item" (click)="openHotkeysCheatSheet()"> 69 <button *ngIf="!isInMobileView" ngbDropdownItem class="dropdown-item" (click)="openHotkeysCheatSheet()">
70 <my-global-icon iconName="command" aria-hidden="true"></my-global-icon> 70 <my-global-icon iconName="command" aria-hidden="true"></my-global-icon>
71 <ng-container i18n>Keyboard shortcuts</ng-container> 71 <ng-container i18n>Keyboard shortcuts</ng-container>
72 </a> 72 </button>
73 73
74 <a ngbDropdownItem ngbDropdownToggle (click)="logout($event)" class="dropdown-item" href="#"> 74 <button ngbDropdownItem (click)="logout($event)" class="dropdown-item">
75 <my-global-icon iconName="sign-out" aria-hidden="true"></my-global-icon> 75 <my-global-icon iconName="sign-out" aria-hidden="true"></my-global-icon>
76 <ng-container i18n>Log out</ng-container> 76 <ng-container i18n>Log out</ng-container>
77 </a> 77 </button>
78 </div> 78 </div>
79 </div> 79 </div>
80 80
@@ -121,10 +121,10 @@
121 121
122 <div class="footer"> 122 <div class="footer">
123 <div class="footer-block"> 123 <div class="footer-block">
124 <a *ngIf="!isLoggedIn" class="menu-link" (click)="openQuickSettings()"> 124 <button *ngIf="!isLoggedIn" class="menu-link button-unstyle" (click)="openQuickSettings()">
125 <my-global-icon iconName="cog" aria-hidden="true"></my-global-icon> 125 <my-global-icon iconName="cog" aria-hidden="true"></my-global-icon>
126 <ng-container i18n>My settings</ng-container> 126 <ng-container i18n>My settings</ng-container>
127 </a> 127 </button>
128 128
129 <a class="menu-link" routerLink="/about" routerLinkActive="active"> 129 <a class="menu-link" routerLink="/about" routerLinkActive="active">
130 <my-global-icon iconName="help" aria-hidden="true"></my-global-icon> 130 <my-global-icon iconName="help" aria-hidden="true"></my-global-icon>
@@ -135,7 +135,7 @@
135 <div class="footer-bottom"> 135 <div class="footer-bottom">
136 136
137 <div class="footer-links"> 137 <div class="footer-links">
138 <span *ngIf="isLoggedIn === false" role="button" (click)="openLanguageChooser()" class="c-hand" i18n>Interface: {{ currentInterfaceLanguage }}</span> 138 <button *ngIf="isLoggedIn === false" (click)="openLanguageChooser()" class="button-unstyle" i18n>Interface: {{ currentInterfaceLanguage }}</button>
139 139
140 <div> 140 <div>
141 <a i18n routerLink="/about/instance">Contact</a> 141 <a i18n routerLink="/about/instance">Contact</a>
@@ -143,7 +143,7 @@
143 <a i18n href="https://joinpeertube.org/faq" i18n-title title="Frequently asked questions about PeerTube" target="_blank" rel="noopener noreferrer">FAQ</a> 143 <a i18n href="https://joinpeertube.org/faq" i18n-title title="Frequently asked questions about PeerTube" target="_blank" rel="noopener noreferrer">FAQ</a>
144 <a i18n routerLink="/about/instance" fragment="statistics">Stats</a> 144 <a i18n routerLink="/about/instance" fragment="statistics">Stats</a>
145 <a i18n href="https://docs.joinpeertube.org/api/rest-reference.html" i18n-title title="API documentation" target="_blank" rel="noopener noreferrer">API</a> 145 <a i18n href="https://docs.joinpeertube.org/api/rest-reference.html" i18n-title title="API documentation" target="_blank" rel="noopener noreferrer">API</a>
146 <a role="button" (click)="openHotkeysCheatSheet()" class="c-hand" i18n>Keyboard shortcuts</a> 146 <button (click)="openHotkeysCheatSheet()" class="button-unstyle" i18n>Keyboard shortcuts</button>
147 </div> 147 </div>
148 </div> 148 </div>
149 149
diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss
index d88025c33..08d6fb900 100644
--- a/client/src/app/menu/menu.component.scss
+++ b/client/src/app/menu/menu.component.scss
@@ -20,6 +20,7 @@ $footer-links-base-opacity: .8;
20 word-break: break-word; 20 word-break: break-word;
21 transition: background-color .1s ease-in-out; 21 transition: background-color .1s ease-in-out;
22 line-height: $line-height-normal; 22 line-height: $line-height-normal;
23 width: 100%;
23 24
24 &.active { 25 &.active {
25 background-color: rgba(255, 255, 255, 0.15); 26 background-color: rgba(255, 255, 255, 0.15);
@@ -113,16 +114,13 @@ my-notification {
113 .dropdown-toggle-indicator { 114 .dropdown-toggle-indicator {
114 display: inherit !important; 115 display: inherit !important;
115 } 116 }
116
117 &.dropdown-toggle {
118 max-width: 88% !important;
119 }
120 } 117 }
121 } 118 }
122 119
123 @include margin-left(13px); 120 @include margin-left(13px);
124 121
125 flex: 1; 122 flex: 1;
123 min-width: 1px;
126 border-radius: 25px; 124 border-radius: 25px;
127 transition: all .1s ease-in-out; 125 transition: all .1s ease-in-out;
128 cursor: pointer; 126 cursor: pointer;
@@ -140,22 +138,24 @@ my-notification {
140 /* smartphones and touchscreens */ 138 /* smartphones and touchscreens */
141 @media (hover: none) and (pointer: coarse) { 139 @media (hover: none) and (pointer: coarse) {
142 @include display-hints($is-mobile: true); 140 @include display-hints($is-mobile: true);
141 }
143 142
144 /* fill space when on mobile */ 143 > .dropdown-toggle {
145 max-width: calc(100% - 80px); 144 display: flex;
146 145 align-items: center;
147 &.dropdown-toggle { 146 width: 100%;
148 max-width: 100%; 147 padding: 5px 7px;
149 }
150 148
151 .logged-in-info { 149 &::after {
152 max-width: calc(100% - 45px) !important; 150 // Disable bootstrap toggle
151 border: 0;
153 } 152 }
154 } 153 }
155 154
156 .dropdown-toggle-indicator { 155 .dropdown-toggle-indicator {
157 position: relative; 156 position: relative;
158 display: none; 157 display: none;
158 width: 17px;
159 159
160 span { 160 span {
161 position: absolute; 161 position: absolute;
@@ -163,17 +163,6 @@ my-notification {
163 color: #808080; 163 color: #808080;
164 } 164 }
165 } 165 }
166
167 .dropdown-toggle::after {
168 border: 0;
169 }
170
171 > .dropdown-toggle:first-child {
172 display: flex;
173 align-items: center;
174 width: 100%;
175 padding: 5px 25px 5px 7px;
176 }
177} 166}
178 167
179my-actor-avatar { 168my-actor-avatar {
@@ -181,9 +170,8 @@ my-actor-avatar {
181} 170}
182 171
183.logged-in-info { 172.logged-in-info {
184 max-width: 105px; 173 flex-shrink: 1;
185 174 min-width: 1px;
186 flex-grow: 1;
187} 175}
188 176
189.logged-in-display-name, 177.logged-in-display-name,
@@ -286,7 +274,8 @@ my-actor-avatar {
286 margin-bottom: 25px; 274 margin-bottom: 25px;
287 } 275 }
288 276
289 a { 277 a,
278 button {
290 min-height: 40px; 279 min-height: 40px;
291 } 280 }
292} 281}
@@ -306,7 +295,7 @@ my-actor-avatar {
306 } 295 }
307 296
308 a, 297 a,
309 span[role=button] { 298 button {
310 @include margin-right(8px); 299 @include margin-right(8px);
311 @include disable-default-a-behaviour; 300 @include disable-default-a-behaviour;
312 301
diff --git a/client/src/app/shared/form-validators/video-validators.ts b/client/src/app/shared/form-validators/video-validators.ts
index ab6f21a35..a4bda8f16 100644
--- a/client/src/app/shared/form-validators/video-validators.ts
+++ b/client/src/app/shared/form-validators/video-validators.ts
@@ -99,7 +99,7 @@ export const VIDEO_ORIGINALLY_PUBLISHED_AT_VALIDATOR: BuildFormValidator = {
99 99
100function arrayTagLengthValidator (min = 2, max = 30): ValidatorFn { 100function arrayTagLengthValidator (min = 2, max = 30): ValidatorFn {
101 return (control: AbstractControl): ValidationErrors => { 101 return (control: AbstractControl): ValidationErrors => {
102 const array = control.value as Array<string> 102 const array = control.value as string[]
103 103
104 if (array.every(e => e.length >= min && e.length <= max)) { 104 if (array.every(e => e.length >= min && e.length <= max)) {
105 return null 105 return null
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 ef96f8f05..2571cc952 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
@@ -120,12 +120,12 @@
120 <my-global-icon *ngIf="isAbuseRejected(abuse)" [title]="abuse.state.label" iconName="cross"></my-global-icon> 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="abuse-messages">
124 <ng-container *ngIf="isLocalAbuse(abuse)"> 124 <button class="button-unstyle" *ngIf="isLocalAbuse(abuse)" (click)="openAbuseMessagesModal(abuse)">
125 {{ abuse.countMessages }} 125 {{ abuse.countMessages }}
126 126
127 <my-global-icon iconName="message-circle"></my-global-icon> 127 <my-global-icon iconName="message-circle"></my-global-icon>
128 </ng-container> 128 </button>
129 </td> 129 </td>
130 130
131 <td *ngIf="isAdminView()" class="internal-note" container="body" placement="left auto" [ngbTooltip]="abuse.moderationComment"> 131 <td *ngIf="isAdminView()" class="internal-note" container="body" placement="left auto" [ngbTooltip]="abuse.moderationComment">
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 a0f65a3d9..c63b5b361 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
@@ -2,29 +2,29 @@
2 <div class="position-relative me-3"> 2 <div class="position-relative me-3">
3 <my-actor-avatar [actor]="actor" [actorType]="getActorType()" [previewImage]="preview" size="100"></my-actor-avatar> 3 <my-actor-avatar [actor]="actor" [actorType]="getActorType()" [previewImage]="preview" size="100"></my-actor-avatar>
4 4
5 <div *ngIf="editable && !hasAvatar()" class="actor-img-edit-button" [ngbTooltip]="avatarFormat" placement="right" container="body"> 5 <div *ngIf="editable && !hasAvatar()" class="actor-img-edit-button button-focus-within" [ngbTooltip]="avatarFormat" placement="right" container="body">
6 <my-global-icon iconName="upload"></my-global-icon> 6 <my-global-icon iconName="upload"></my-global-icon>
7 <label class="visually-hidden" for="avatarfile" i18n>Upload a new avatar</label> 7 <label class="visually-hidden" for="avatarfile" i18n>Upload a new avatar</label>
8 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/> 8 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
9 </div> 9 </div>
10 10
11 <div *ngIf="editable && hasAvatar()" ngbDropdown placement="right"> 11 <div *ngIf="editable && hasAvatar()" ngbDropdown placement="right">
12 <div class="actor-img-edit-button" ngbDropdownToggle> 12 <button type="button" class="actor-img-edit-button" ngbDropdownToggle>
13 <my-global-icon iconName="edit"></my-global-icon> 13 <my-global-icon iconName="edit"></my-global-icon>
14 <label class="visually-hidden" for="avatarMenu" i18n>Change your avatar</label> 14 <label class="visually-hidden" for="avatarMenu" i18n>Change your avatar</label>
15 </div> 15 </button>
16 16
17 <div ngbDropdownMenu> 17 <div ngbDropdownMenu>
18 <div class="dropdown-item c-hand dropdown-file" [ngbTooltip]="avatarFormat"> 18 <div class="dropdown-item dropdown-file button-focus-within" [ngbTooltip]="avatarFormat">
19 <my-global-icon iconName="upload"></my-global-icon> 19 <my-global-icon iconName="upload"></my-global-icon>
20 <span for="avatarfile" i18n>Upload a new avatar</span> 20 <span for="avatarfile" i18n>Upload a new avatar</span>
21 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/> 21 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
22 </div> 22 </div>
23 23
24 <div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()"> 24 <button type="button" class="dropdown-item" (click)="deleteAvatar()" (key.enter)="deleteAvatar()">
25 <my-global-icon iconName="delete"></my-global-icon> 25 <my-global-icon iconName="delete"></my-global-icon>
26 <span i18n>Remove avatar</span> 26 <span i18n>Remove avatar</span>
27 </div> 27 </button>
28 </div> 28 </div>
29 29
30 </div> 30 </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 01e2131ba..689c5873c 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
@@ -39,6 +39,10 @@
39 right: 5px; 39 right: 5px;
40} 40}
41 41
42.button-focus-within:focus-within {
43 box-shadow: #{$focus-box-shadow-form} pvar(--mainColorLightest);
44}
45
42.dropdown-item { 46.dropdown-item {
43 @include dropdown-with-icon-item; 47 @include dropdown-with-icon-item;
44} 48}
diff --git a/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html
index d6fe37094..d4ddb2deb 100644
--- a/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html
+++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html
@@ -4,25 +4,25 @@
4 <img *ngIf="hasBanner()" [src]="preview || actor.bannerUrl" alt="Banner" /> 4 <img *ngIf="hasBanner()" [src]="preview || actor.bannerUrl" alt="Banner" />
5 </div> 5 </div>
6 6
7 <div *ngIf="!hasBanner()" class="actor-img-edit-button" [ngbTooltip]="bannerFormat" placement="right" container="body"> 7 <div *ngIf="!hasBanner()" class="actor-img-edit-button button-focus-within" [ngbTooltip]="bannerFormat" placement="right" container="body">
8 <ng-container *ngTemplateOutlet="uploadNewBanner"></ng-container> 8 <ng-container *ngTemplateOutlet="uploadNewBanner"></ng-container>
9 </div> 9 </div>
10 10
11 <div *ngIf="hasBanner()" ngbDropdown placement="right"> 11 <div *ngIf="hasBanner()" ngbDropdown placement="right">
12 <div class="actor-img-edit-button" ngbDropdownToggle> 12 <button type="button" class="actor-img-edit-button" ngbDropdownToggle>
13 <my-global-icon iconName="edit"></my-global-icon> 13 <my-global-icon iconName="edit"></my-global-icon>
14 <label for="bannerMenu" i18n>Change your banner</label> 14 <label for="bannerMenu" i18n>Change your banner</label>
15 </div> 15 </button>
16 16
17 <div ngbDropdownMenu> 17 <div ngbDropdownMenu>
18 <div class="dropdown-item c-hand dropdown-file" [ngbTooltip]="bannerFormat"> 18 <div class="dropdown-item dropdown-file button-focus-within" [ngbTooltip]="bannerFormat">
19 <ng-container *ngTemplateOutlet="uploadNewBanner"></ng-container> 19 <ng-container *ngTemplateOutlet="uploadNewBanner"></ng-container>
20 </div> 20 </div>
21 21
22 <div class="dropdown-item c-hand" (click)="deleteBanner()" (key.enter)="deleteBanner()"> 22 <button type="button" class="dropdown-item" (click)="deleteBanner()">
23 <my-global-icon iconName="delete"></my-global-icon> 23 <my-global-icon iconName="delete"></my-global-icon>
24 <span i18n>Remove banner</span> 24 <span i18n>Remove banner</span>
25 </div> 25 </button>
26 </div> 26 </div>
27 </div> 27 </div>
28 </div> 28 </div>
diff --git a/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss
index b2c64fff7..8e5a05603 100644
--- a/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss
+++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss
@@ -34,6 +34,10 @@
34 } 34 }
35} 35}
36 36
37.button-focus-within:focus-within {
38 box-shadow: #{$focus-box-shadow-form} pvar(--mainColorLightest);
39}
40
37.dropdown-item { 41.dropdown-item {
38 @include dropdown-with-icon-item; 42 @include dropdown-with-icon-item;
39} 43}
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 8e6ad4015..f1c1aa03f 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
@@ -145,13 +145,13 @@ export class ActorAvatarComponent implements OnInit, OnChanges {
145 // Keep consistency with CSS 145 // Keep consistency with CSS
146 const themes = { 146 const themes = {
147 '0123456789abc': 'blue', 147 '0123456789abc': 'blue',
148 def: 'green', 148 'def': 'green',
149 ghi: 'purple', 149 'ghi': 'purple',
150 jkl: 'gray', 150 'jkl': 'gray',
151 mno: 'yellow', 151 'mno': 'yellow',
152 pqr: 'orange', 152 'pqr': 'orange',
153 stvu: 'red', 153 'stvu': 'red',
154 wxyz: 'dark-blue' 154 'wxyz': 'dark-blue'
155 } 155 }
156 156
157 const theme = Object.keys(themes) 157 const theme = Object.keys(themes)
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 9a466055a..4017d4aa5 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,2 @@
1<div (click)="update()"> 1<input type="checkbox" [checked]="checked" [name]="inputName" [id]="inputName" (change)="update()" />
2 <input type="checkbox" [checked]="checked"/> 2<label [for]="inputName" class="ms-auto">Toggle</label>
3 <label class="ms-auto">Toggle</label>
4</div>
diff --git a/client/src/app/shared/shared-forms/preview-upload.component.html b/client/src/app/shared/shared-forms/preview-upload.component.html
index 11e7bdf7f..8a10a11b0 100644
--- a/client/src/app/shared/shared-forms/preview-upload.component.html
+++ b/client/src/app/shared/shared-forms/preview-upload.component.html
@@ -5,7 +5,7 @@
5 icon="edit" (fileChanged)="onFileChanged($event)" [buttonTooltip]="getReactiveFileButtonTooltip()" 5 icon="edit" (fileChanged)="onFileChanged($event)" [buttonTooltip]="getReactiveFileButtonTooltip()"
6 ></my-reactive-file> 6 ></my-reactive-file>
7 7
8 <img *ngIf="imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" [src]="imageSrc" class="preview" /> 8 <img *ngIf="imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" [src]="imageSrc" class="preview" alt="Preview" i18n-alt />
9 <div *ngIf="!imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" class="preview no-image"></div> 9 <div *ngIf="!imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" class="preview no-image"></div>
10 </div> 10 </div>
11</div> 11</div>
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 55eb45a75..96179cbe6 100644
--- a/client/src/app/shared/shared-icons/global-icon.component.ts
+++ b/client/src/app/shared/shared-icons/global-icon.component.ts
@@ -3,81 +3,81 @@ import { HooksService } from '@app/core/plugins/hooks.service'
3 3
4const icons = { 4const icons = {
5 // misc icons 5 // misc icons
6 npm: require('!!raw-loader?!../../../assets/images/misc/npm.svg').default, 6 'npm': require('!!raw-loader?!../../../assets/images/misc/npm.svg').default,
7 markdown: require('!!raw-loader?!../../../assets/images/misc/markdown.svg').default, 7 'markdown': require('!!raw-loader?!../../../assets/images/misc/markdown.svg').default,
8 language: require('!!raw-loader?!../../../assets/images/misc/language.svg').default, 8 'language': require('!!raw-loader?!../../../assets/images/misc/language.svg').default,
9 'video-lang': require('!!raw-loader?!../../../assets/images/misc/video-lang.svg').default, 9 'video-lang': require('!!raw-loader?!../../../assets/images/misc/video-lang.svg').default,
10 support: require('!!raw-loader?!../../../assets/images/misc/support.svg').default, 10 'support': require('!!raw-loader?!../../../assets/images/misc/support.svg').default,
11 'peertube-x': require('!!raw-loader?!../../../assets/images/misc/peertube-x.svg').default, 11 'peertube-x': require('!!raw-loader?!../../../assets/images/misc/peertube-x.svg').default,
12 robot: require('!!raw-loader?!../../../assets/images/misc/miscellaneous-services.svg').default, // material ui 12 'robot': require('!!raw-loader?!../../../assets/images/misc/miscellaneous-services.svg').default, // material ui
13 videos: require('!!raw-loader?!../../../assets/images/misc/video-library.svg').default, // material ui 13 'videos': require('!!raw-loader?!../../../assets/images/misc/video-library.svg').default, // material ui
14 history: require('!!raw-loader?!../../../assets/images/misc/history.svg').default, // material ui 14 'history': require('!!raw-loader?!../../../assets/images/misc/history.svg').default, // material ui
15 subscriptions: require('!!raw-loader?!../../../assets/images/misc/subscriptions.svg').default, // material ui 15 'subscriptions': require('!!raw-loader?!../../../assets/images/misc/subscriptions.svg').default, // material ui
16 'playlist-add': require('!!raw-loader?!../../../assets/images/misc/playlist-add.svg').default, // material ui 16 'playlist-add': require('!!raw-loader?!../../../assets/images/misc/playlist-add.svg').default, // material ui
17 follower: require('!!raw-loader?!../../../assets/images/misc/account-arrow-left.svg').default, // material ui 17 'follower': require('!!raw-loader?!../../../assets/images/misc/account-arrow-left.svg').default, // material ui
18 following: require('!!raw-loader?!../../../assets/images/misc/account-arrow-right.svg').default, // material ui 18 'following': require('!!raw-loader?!../../../assets/images/misc/account-arrow-right.svg').default, // material ui
19 tip: require('!!raw-loader?!../../../assets/images/misc/tip.svg').default, // material ui 19 'tip': require('!!raw-loader?!../../../assets/images/misc/tip.svg').default, // material ui
20 flame: require('!!raw-loader?!../../../assets/images/misc/flame.svg').default, 20 'flame': require('!!raw-loader?!../../../assets/images/misc/flame.svg').default,
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 'copy': require('!!raw-loader?!../../../assets/images/feather/copy.svg').default,
25 flag: require('!!raw-loader?!../../../assets/images/feather/flag.svg').default, 25 'flag': require('!!raw-loader?!../../../assets/images/feather/flag.svg').default,
26 playlists: require('!!raw-loader?!../../../assets/images/feather/list.svg').default, 26 'playlists': require('!!raw-loader?!../../../assets/images/feather/list.svg').default,
27 syndication: require('!!raw-loader?!../../../assets/images/feather/syndication.svg').default, 27 'syndication': require('!!raw-loader?!../../../assets/images/feather/syndication.svg').default,
28 help: require('!!raw-loader?!../../../assets/images/feather/help.svg').default, 28 'help': require('!!raw-loader?!../../../assets/images/feather/help.svg').default,
29 alert: require('!!raw-loader?!../../../assets/images/feather/alert.svg').default, 29 'alert': require('!!raw-loader?!../../../assets/images/feather/alert.svg').default,
30 globe: require('!!raw-loader?!../../../assets/images/feather/globe.svg').default, 30 'globe': require('!!raw-loader?!../../../assets/images/feather/globe.svg').default,
31 home: require('!!raw-loader?!../../../assets/images/feather/home.svg').default, 31 'home': require('!!raw-loader?!../../../assets/images/feather/home.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,
35 dislike: require('!!raw-loader?!../../../assets/images/feather/dislike.svg').default, 35 'dislike': require('!!raw-loader?!../../../assets/images/feather/dislike.svg').default,
36 like: require('!!raw-loader?!../../../assets/images/feather/like.svg').default, 36 'like': require('!!raw-loader?!../../../assets/images/feather/like.svg').default,
37 no: require('!!raw-loader?!../../../assets/images/feather/no.svg').default, 37 'no': require('!!raw-loader?!../../../assets/images/feather/no.svg').default,
38 'cloud-download': require('!!raw-loader?!../../../assets/images/feather/cloud-download.svg').default, 38 'cloud-download': require('!!raw-loader?!../../../assets/images/feather/cloud-download.svg').default,
39 clock: require('!!raw-loader?!../../../assets/images/feather/clock.svg').default, 39 'clock': require('!!raw-loader?!../../../assets/images/feather/clock.svg').default,
40 cog: require('!!raw-loader?!../../../assets/images/feather/cog.svg').default, 40 'cog': require('!!raw-loader?!../../../assets/images/feather/cog.svg').default,
41 delete: require('!!raw-loader?!../../../assets/images/feather/delete.svg').default, 41 'delete': require('!!raw-loader?!../../../assets/images/feather/delete.svg').default,
42 bell: require('!!raw-loader?!../../../assets/images/feather/bell.svg').default, 42 'bell': require('!!raw-loader?!../../../assets/images/feather/bell.svg').default,
43 'sign-out': require('!!raw-loader?!../../../assets/images/feather/log-out.svg').default, 43 'sign-out': require('!!raw-loader?!../../../assets/images/feather/log-out.svg').default,
44 'sign-in': require('!!raw-loader?!../../../assets/images/feather/log-in.svg').default, 44 'sign-in': require('!!raw-loader?!../../../assets/images/feather/log-in.svg').default,
45 download: require('!!raw-loader?!../../../assets/images/feather/download.svg').default, 45 'download': require('!!raw-loader?!../../../assets/images/feather/download.svg').default,
46 'ownership-change': require('!!raw-loader?!../../../assets/images/feather/share.svg').default, 46 'ownership-change': require('!!raw-loader?!../../../assets/images/feather/share.svg').default,
47 share: require('!!raw-loader?!../../../assets/images/feather/share-2.svg').default, 47 'share': require('!!raw-loader?!../../../assets/images/feather/share-2.svg').default,
48 channel: require('!!raw-loader?!../../../assets/images/feather/tv.svg').default, 48 'channel': require('!!raw-loader?!../../../assets/images/feather/tv.svg').default,
49 user: require('!!raw-loader?!../../../assets/images/feather/user.svg').default, 49 'user': require('!!raw-loader?!../../../assets/images/feather/user.svg').default,
50 'user-x': require('!!raw-loader?!../../../assets/images/feather/user-x.svg').default, 50 'user-x': require('!!raw-loader?!../../../assets/images/feather/user-x.svg').default,
51 users: require('!!raw-loader?!../../../assets/images/feather/users.svg').default, 51 'users': require('!!raw-loader?!../../../assets/images/feather/users.svg').default,
52 'user-add': require('!!raw-loader?!../../../assets/images/feather/user-plus.svg').default, 52 'user-add': require('!!raw-loader?!../../../assets/images/feather/user-plus.svg').default,
53 add: require('!!raw-loader?!../../../assets/images/feather/plus-circle.svg').default, 53 'add': require('!!raw-loader?!../../../assets/images/feather/plus-circle.svg').default,
54 'cloud-error': require('!!raw-loader?!../../../assets/images/feather/cloud-off.svg').default, 54 'cloud-error': require('!!raw-loader?!../../../assets/images/feather/cloud-off.svg').default,
55 undo: require('!!raw-loader?!../../../assets/images/feather/corner-up-left.svg').default, 55 'undo': require('!!raw-loader?!../../../assets/images/feather/corner-up-left.svg').default,
56 'circle-tick': require('!!raw-loader?!../../../assets/images/feather/check-circle.svg').default, 56 'circle-tick': require('!!raw-loader?!../../../assets/images/feather/check-circle.svg').default,
57 'more-horizontal': require('!!raw-loader?!../../../assets/images/feather/more-horizontal.svg').default, 57 'more-horizontal': require('!!raw-loader?!../../../assets/images/feather/more-horizontal.svg').default,
58 'more-vertical': require('!!raw-loader?!../../../assets/images/feather/more-vertical.svg').default, 58 'more-vertical': require('!!raw-loader?!../../../assets/images/feather/more-vertical.svg').default,
59 play: require('!!raw-loader?!../../../assets/images/feather/play.svg').default, 59 'play': require('!!raw-loader?!../../../assets/images/feather/play.svg').default,
60 p2p: require('!!raw-loader?!../../../assets/images/feather/airplay.svg').default, 60 'p2p': require('!!raw-loader?!../../../assets/images/feather/airplay.svg').default,
61 fullscreen: require('!!raw-loader?!../../../assets/images/feather/maximize.svg').default, 61 'fullscreen': require('!!raw-loader?!../../../assets/images/feather/maximize.svg').default,
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 'external-link': require('!!raw-loader?!../../../assets/images/feather/external-link.svg').default, 65 'external-link': require('!!raw-loader?!../../../assets/images/feather/external-link.svg').default,
66 'eye-open': require('!!raw-loader?!../../../assets/images/feather/eye.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 'eye-close': require('!!raw-loader?!../../../assets/images/feather/eye-off.svg').default,
68 refresh: require('!!raw-loader?!../../../assets/images/feather/refresh-cw.svg').default, 68 'refresh': require('!!raw-loader?!../../../assets/images/feather/refresh-cw.svg').default,
69 command: require('!!raw-loader?!../../../assets/images/feather/command.svg').default, 69 'command': require('!!raw-loader?!../../../assets/images/feather/command.svg').default,
70 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,
71 cross: require('!!raw-loader?!../../../assets/images/feather/x.svg').default, 71 'cross': require('!!raw-loader?!../../../assets/images/feather/x.svg').default,
72 tick: require('!!raw-loader?!../../../assets/images/feather/check.svg').default, 72 'tick': require('!!raw-loader?!../../../assets/images/feather/check.svg').default,
73 columns: require('!!raw-loader?!../../../assets/images/feather/columns.svg').default, 73 'columns': require('!!raw-loader?!../../../assets/images/feather/columns.svg').default,
74 live: require('!!raw-loader?!../../../assets/images/feather/live.svg').default, 74 'live': require('!!raw-loader?!../../../assets/images/feather/live.svg').default,
75 repeat: require('!!raw-loader?!../../../assets/images/feather/repeat.svg').default, 75 'repeat': require('!!raw-loader?!../../../assets/images/feather/repeat.svg').default,
76 'chevrons-up': require('!!raw-loader?!../../../assets/images/feather/chevrons-up.svg').default, 76 'chevrons-up': require('!!raw-loader?!../../../assets/images/feather/chevrons-up.svg').default,
77 'message-circle': require('!!raw-loader?!../../../assets/images/feather/message-circle.svg').default, 77 'message-circle': require('!!raw-loader?!../../../assets/images/feather/message-circle.svg').default,
78 codesandbox: require('!!raw-loader?!../../../assets/images/feather/codesandbox.svg').default, 78 'codesandbox': require('!!raw-loader?!../../../assets/images/feather/codesandbox.svg').default,
79 award: require('!!raw-loader?!../../../assets/images/feather/award.svg').default, 79 'award': require('!!raw-loader?!../../../assets/images/feather/award.svg').default,
80 stats: require('!!raw-loader?!../../../assets/images/feather/stats.svg').default 80 'stats': require('!!raw-loader?!../../../assets/images/feather/stats.svg').default
81} 81}
82 82
83export type GlobalIconName = keyof typeof icons 83export type GlobalIconName = keyof typeof icons
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 205f2bc97..37f53b7c6 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
@@ -11,7 +11,7 @@
11 <tr> 11 <tr>
12 <th i18n class="label" scope="row"> 12 <th i18n class="label" scope="row">
13 <div>Default NSFW/sensitive videos policy</div> 13 <div>Default NSFW/sensitive videos policy</div>
14 <div class="c-hand more-info" (click)="openQuickSettingsHighlight()">can be redefined by the users</div> 14 <span class="fs-7 fw-normal fst-italic">can be redefined by the users</span>
15 </th> 15 </th>
16 16
17 <td class="value">{{ buildNSFWLabel() }}</td> 17 <td class="value">{{ buildNSFWLabel() }}</td>
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 c3df7c594..2e63f6c17 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
@@ -2,7 +2,6 @@ import { Component, OnInit } from '@angular/core'
2import { ServerService } from '@app/core' 2import { ServerService } from '@app/core'
3import { prepareIcu } from '@app/helpers' 3import { prepareIcu } from '@app/helpers'
4import { ServerConfig } from '@shared/models' 4import { ServerConfig } from '@shared/models'
5import { PeertubeModalService } from '../shared-main/peertube-modal/peertube-modal.service'
6 5
7@Component({ 6@Component({
8 selector: 'my-instance-features-table', 7 selector: 'my-instance-features-table',
@@ -14,8 +13,7 @@ export class InstanceFeaturesTableComponent implements OnInit {
14 serverConfig: ServerConfig 13 serverConfig: ServerConfig
15 14
16 constructor ( 15 constructor (
17 private serverService: ServerService, 16 private serverService: ServerService
18 private modalService: PeertubeModalService
19 ) { } 17 ) { }
20 18
21 get initialUserVideoQuota () { 19 get initialUserVideoQuota () {
@@ -69,10 +67,6 @@ export class InstanceFeaturesTableComponent implements OnInit {
69 return this.serverService.getServerVersionAndCommit() 67 return this.serverService.getServerVersionAndCommit()
70 } 68 }
71 69
72 openQuickSettingsHighlight () {
73 this.modalService.openQuickSettingsSubject.next()
74 }
75
76 private getApproximateTime (seconds: number) { 70 private getApproximateTime (seconds: number) {
77 const hours = Math.floor(seconds / 3600) 71 const hours = Math.floor(seconds / 3600)
78 72
diff --git a/client/src/app/shared/shared-main/angular/number-formatter.pipe.ts b/client/src/app/shared/shared-main/angular/number-formatter.pipe.ts
index e0cb475fc..00cfbc692 100644
--- a/client/src/app/shared/shared-main/angular/number-formatter.pipe.ts
+++ b/client/src/app/shared/shared-main/angular/number-formatter.pipe.ts
@@ -17,7 +17,7 @@ export class NumberFormatterPipe implements PipeTransform {
17 return +f 17 return +f
18 } 18 }
19 19
20 private dictionary: Array<{ max: number, type: string }> = [ 20 private dictionary: { max: number, type: string }[] = [
21 { max: 1000, type: '' }, 21 { max: 1000, type: '' },
22 { max: 1000000, type: 'K' }, 22 { max: 1000000, type: 'K' },
23 { max: 1000000000, type: 'M' } 23 { max: 1000000000, type: 'M' }
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 bbfab7b37..d4aa9f380 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
@@ -40,7 +40,7 @@
40 40
41 <h6 41 <h6
42 *ngIf="!action.linkBuilder && action.isHeader && areActionsDisplayed(actions, entry)" [ngClass]="{ 'with-icon': !!action.iconName }" 42 *ngIf="!action.linkBuilder && action.isHeader && areActionsDisplayed(actions, entry)" [ngClass]="{ 'with-icon': !!action.iconName }"
43 class="dropdown-header" [title]="action.title || ''" (click)="action.handler(entry)" (keyup.enter)="action.handler(entry)" 43 class="dropdown-header" [title]="action.title || ''"
44 > 44 >
45 <ng-container *ngTemplateOutlet="templateActionLabel; context:{ $implicit: action }"></ng-container> 45 <ng-container *ngTemplateOutlet="templateActionLabel; context:{ $implicit: action }"></ng-container>
46 </h6> 46 </h6>
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 e39fbd66d..7d24ff4ae 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
@@ -55,7 +55,7 @@ export class ActionDropdownComponent<T> {
55 return {} 55 return {}
56 } 56 }
57 57
58 areActionsDisplayed (actions: Array<DropdownAction<T> | DropdownAction<T>[]>, entry: T): boolean { 58 areActionsDisplayed (actions: (DropdownAction<T> | DropdownAction<T>[])[], entry: T): boolean {
59 return actions.some(a => { 59 return actions.some(a => {
60 if (Array.isArray(a)) return this.areActionsDisplayed(a, entry) 60 if (Array.isArray(a)) return this.areActionsDisplayed(a, entry)
61 61
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 63a59cbe1..e3a830891 100644
--- a/client/src/app/shared/shared-main/buttons/button.component.ts
+++ b/client/src/app/shared/shared-main/buttons/button.component.ts
@@ -31,7 +31,7 @@ export class ButtonComponent implements OnInit, OnChanges {
31 private buildClasses () { 31 private buildClasses () {
32 this.classes = { 32 this.classes = {
33 [this.className]: true, 33 [this.className]: true,
34 disabled: this.disabled, 34 'disabled': this.disabled,
35 'icon-only': !this.label, 35 'icon-only': !this.label,
36 'has-icon': !!this.icon, 36 'has-icon': !!this.icon,
37 'responsive-label': this.responsiveLabel 37 'responsive-label': this.responsiveLabel
diff --git a/client/src/app/shared/shared-main/date/date-toggle.component.html b/client/src/app/shared/shared-main/date/date-toggle.component.html
index 14b6e7d7a..5277ae0e8 100644
--- a/client/src/app/shared/shared-main/date/date-toggle.component.html
+++ b/client/src/app/shared/shared-main/date/date-toggle.component.html
@@ -1,7 +1,10 @@
1<span 1<span
2 class="date-toggle" 2 class="date-toggle"
3 [title]="getTitle()" 3 [title]="getTitle()"
4 role="button"
5 tabindex="0"
4 (click)="toggle()" 6 (click)="toggle()"
7 (keyup.enter)="toggle()"
5> 8>
6 {{ getContent() }} 9 {{ getContent() }}
7</span> 10</span>
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 bd038f8b5..be9e130e7 100644
--- a/client/src/app/shared/shared-main/loaders/loader.component.ts
+++ b/client/src/app/shared/shared-main/loaders/loader.component.ts
@@ -10,8 +10,8 @@ export class LoaderComponent {
10 10
11 private readonly sizes = { 11 private readonly sizes = {
12 sm: { 12 sm: {
13 width: '1rem', 13 'width': '1rem',
14 height: '1rem', 14 'height': '1rem',
15 'border-width': '0.15rem' 15 'border-width': '0.15rem'
16 }, 16 },
17 xl: { 17 xl: {
diff --git a/client/src/app/shared/shared-main/users/user-notifications.component.html b/client/src/app/shared/shared-main/users/user-notifications.component.html
index a51e08292..351e4dc3f 100644
--- a/client/src/app/shared/shared-main/users/user-notifications.component.html
+++ b/client/src/app/shared/shared-main/users/user-notifications.component.html
@@ -1,6 +1,7 @@
1<div *ngIf="componentPagination.totalItems === 0" class="no-notification" i18n>You don't have notifications.</div> 1<div *ngIf="componentPagination.totalItems === 0" class="no-notification" i18n>You don't have notifications.</div>
2 2
3<div class="notifications" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()"> 3<div class="notifications" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
4 <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->
4 <div *ngFor="let notification of notifications" class="notification" [ngClass]="{ unread: !notification.read }" (click)="markAsRead(notification)"> 5 <div *ngFor="let notification of notifications" class="notification" [ngClass]="{ unread: !notification.read }" (click)="markAsRead(notification)">
5 6
6 <ng-container [ngSwitch]="notification.type"> 7 <ng-container [ngSwitch]="notification.type">
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 0e0d38c2a..ac9085bb3 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
@@ -1,6 +1,7 @@
1<div class="user-quota mb-3"> 1<div class="user-quota mb-3">
2 <div> 2 <div>
3 <label class="user-quota-title" tabindex="0" i18n>Total video quota</label> 3 <div class="mb-2 fw-bold" i18n>Total video quota</div>
4
4 <div class="progress" tabindex="0" [ngbTooltip]="titleVideoQuota()"> 5 <div class="progress" tabindex="0" [ngbTooltip]="titleVideoQuota()">
5 <div class="progress-bar" tabindex="0" role="progressbar" [style]="{ width: userVideoQuotaPercentage + '%' }" 6 <div class="progress-bar" tabindex="0" role="progressbar" [style]="{ width: userVideoQuotaPercentage + '%' }"
6 [attr.aria-valuenow]="userVideoQuotaUsed" aria-valuemin="0" [attr.aria-valuemax]="user.videoQuota"></div> 7 [attr.aria-valuenow]="userVideoQuotaUsed" aria-valuemin="0" [attr.aria-valuemax]="user.videoQuota"></div>
@@ -10,7 +11,8 @@
10 </div> 11 </div>
11 12
12 <div *ngIf="hasDailyQuota()" class="mt-3"> 13 <div *ngIf="hasDailyQuota()" class="mt-3">
13 <label class="user-quota-title" tabindex="0" i18n>Daily video quota</label> 14 <div class="mb-2 fw-bold" i18n>Daily video quota</div>
15
14 <div class="progress" tabindex="0" [ngbTooltip]="titleVideoQuotaDaily()"> 16 <div class="progress" tabindex="0" [ngbTooltip]="titleVideoQuotaDaily()">
15 <div class="progress-bar" role="progressbar" [style]="{ width: userVideoQuotaDailyPercentage + '%' }" 17 <div class="progress-bar" role="progressbar" [style]="{ width: userVideoQuotaDailyPercentage + '%' }"
16 [attr.aria-valuenow]="userVideoQuotaUsedDaily" aria-valuemin="0" [attr.aria-valuemax]="user.videoQuotaDaily"></div> 18 [attr.aria-valuenow]="userVideoQuotaUsedDaily" aria-valuemin="0" [attr.aria-valuemax]="user.videoQuotaDaily"></div>
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 e07703cf4..1a13e0207 100644
--- a/client/src/app/shared/shared-moderation/server-blocklist.component.html
+++ b/client/src/app/shared/shared-moderation/server-blocklist.component.html
@@ -14,10 +14,10 @@
14 <ng-template pTemplate="caption"> 14 <ng-template pTemplate="caption">
15 <div class="caption"> 15 <div class="caption">
16 <div class="left-buttons"> 16 <div class="left-buttons">
17 <a class="block-button" (click)="addServersToBlock()" (key.enter)="addServersToBlock()"> 17 <button class="peertube-create-button" (click)="addServersToBlock()" (key.enter)="addServersToBlock()">
18 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> 18 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
19 <ng-container i18n>Mute domain</ng-container> 19 <ng-container i18n>Mute domain</ng-container>
20 </a> 20 </button>
21 </div> 21 </div>
22 22
23 <div class="ms-auto"> 23 <div class="ms-auto">
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.scss b/client/src/app/shared/shared-moderation/server-blocklist.component.scss
index 1a6b0435f..10b9a40a1 100644
--- a/client/src/app/shared/shared-moderation/server-blocklist.component.scss
+++ b/client/src/app/shared/shared-moderation/server-blocklist.component.scss
@@ -20,7 +20,3 @@ a {
20 @include peertube-button; 20 @include peertube-button;
21 @include grey-button; 21 @include grey-button;
22} 22}
23
24.block-button {
25 @include create-button;
26}
diff --git a/client/src/app/shared/shared-tables/video-cell.component.html b/client/src/app/shared/shared-tables/video-cell.component.html
index ea432b70e..38e7dea17 100644
--- a/client/src/app/shared/shared-tables/video-cell.component.html
+++ b/client/src/app/shared/shared-tables/video-cell.component.html
@@ -1,7 +1,7 @@
1<a [href]="getVideoUrl()" class="table-video-link" [title]="video.name" target="_blank" rel="noopener noreferrer"> 1<a [href]="getVideoUrl()" class="table-video-link" [title]="video.name" target="_blank" rel="noopener noreferrer">
2 <div class="table-video"> 2 <div class="table-video">
3 <div class="table-video-image"> 3 <div class="table-video-image">
4 <img [src]="video.thumbnailPath"> 4 <img [src]="video.thumbnailPath" alt="">
5 5
6 <ng-content select="[image]"></ng-content> 6 <ng-content select="[image]"></ng-content>
7 </div> 7 </div>
diff --git a/client/src/app/shared/shared-thumbnail/video-thumbnail.component.html b/client/src/app/shared/shared-thumbnail/video-thumbnail.component.html
index 57fcdd899..6c9a8b91c 100644
--- a/client/src/app/shared/shared-thumbnail/video-thumbnail.component.html
+++ b/client/src/app/shared/shared-thumbnail/video-thumbnail.component.html
@@ -11,15 +11,15 @@
11 11
12 <div *ngIf="displayWatchLaterPlaylist" class="video-thumbnail-actions-overlay"> 12 <div *ngIf="displayWatchLaterPlaylist" class="video-thumbnail-actions-overlay">
13 <ng-container *ngIf="inWatchLaterPlaylist !== true"> 13 <ng-container *ngIf="inWatchLaterPlaylist !== true">
14 <div class="video-thumbnail-watch-later-overlay" placement="left" [ngbTooltip]="addToWatchLaterText" container="body" (click)="onWatchLaterClick($event)"> 14 <button class="video-thumbnail-watch-later-overlay button-unstyle" placement="left" [ngbTooltip]="addToWatchLaterText" container="body" (click)="onWatchLaterClick($event)">
15 <my-global-icon iconName="clock" [attr.aria-label]="addToWatchLaterText" role="button"></my-global-icon> 15 <my-global-icon iconName="clock" [attr.aria-label]="addToWatchLaterText" role="button"></my-global-icon>
16 </div> 16 </button>
17 </ng-container> 17 </ng-container>
18 18
19 <ng-container *ngIf="inWatchLaterPlaylist === true"> 19 <ng-container *ngIf="inWatchLaterPlaylist === true">
20 <div class="video-thumbnail-watch-later-overlay" placement="left" [ngbTooltip]="addedToWatchLaterText" container="body" (click)="onWatchLaterClick($event)"> 20 <button class="video-thumbnail-watch-later-overlay button-unstyle" placement="left" [ngbTooltip]="addedToWatchLaterText" container="body" (click)="onWatchLaterClick($event)">
21 <my-global-icon iconName="tick" [attr.aria-label]="addedToWatchLaterText" role="button"></my-global-icon> 21 <my-global-icon iconName="tick" [attr.aria-label]="addedToWatchLaterText" role="button"></my-global-icon>
22 </div> 22 </button>
23 </ng-container> 23 </ng-container>
24 </div> 24 </div>
25 25
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 8e61bdbb3..e385b429c 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
@@ -33,7 +33,7 @@
33 </div> 33 </div>
34 34
35 <div class="journal" *ngIf="latestLiveSessions.length !== 0"> 35 <div class="journal" *ngIf="latestLiveSessions.length !== 0">
36 <label i18n>Latest live sessions</label> 36 <div class="mb-2 fw-bold" i18n>Latest live sessions</div>
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="pt-badge badge-success" *ngIf="!getErrorLabel(session)">Success</span> 39 <span i18n class="pt-badge badge-success" *ngIf="!getErrorLabel(session)">Success</span>
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 3d8ce22de..a3676e159 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
@@ -111,9 +111,9 @@
111 </div> 111 </div>
112 </div> 112 </div>
113 113
114 <div 114 <button
115 (click)="isAdvancedCustomizationCollapsed = !isAdvancedCustomizationCollapsed" 115 (click)="isAdvancedCustomizationCollapsed = !isAdvancedCustomizationCollapsed"
116 role="button" class="advanced-filters-button" 116 class="advanced-filters-button button-unstyle"
117 [attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic" 117 [attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic"
118 > 118 >
119 <ng-container *ngIf="isAdvancedCustomizationCollapsed"> 119 <ng-container *ngIf="isAdvancedCustomizationCollapsed">
@@ -131,7 +131,7 @@
131 Simple 131 Simple
132 </ng-container> 132 </ng-container>
133 </ng-container> 133 </ng-container>
134 </div> 134 </button>
135 </ng-container> 135 </ng-container>
136 </div> 136 </div>
137 137
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 48bb0d812..3d39c6fdc 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
@@ -2,7 +2,7 @@
2 <div class="label-description muted" i18n> 2 <div class="label-description muted" i18n>
3 Update 3 Update
4 <a routerLink="/my-account/settings" [fragment]="fragment"> 4 <a routerLink="/my-account/settings" [fragment]="fragment">
5 <span (click)="onAccountSettingsClick($event)">your settings</span> 5 <button class="button-unstyle" (click)="onAccountSettingsClick($event)">your settings</button>
6 </a 6 </a
7 ></div> 7 ></div>
8</ng-template> 8</ng-template>
@@ -23,9 +23,9 @@
23 <my-global-icon iconName="chevrons-up"></my-global-icon> 23 <my-global-icon iconName="chevrons-up"></my-global-icon>
24 </button> 24 </button>
25 25
26 <div 26 <button
27 *ngFor="let activeFilter of filters.getActiveFilters()" (click)="resetFilter(activeFilter.key, activeFilter.canRemove)" 27 *ngFor="let activeFilter of filters.getActiveFilters()" (click)="resetFilter(activeFilter.key, activeFilter.canRemove)"
28 class="active-filter pastille" [ngClass]="{ 'can-remove': activeFilter.canRemove }" [title]="getFilterTitle(activeFilter.canRemove)" 28 class="active-filter pastille button-unstyle" [ngClass]="{ 'can-remove': activeFilter.canRemove }" [title]="getFilterTitle(activeFilter.canRemove)"
29 > 29 >
30 <span> 30 <span>
31 {{ activeFilter.label }} 31 {{ activeFilter.label }}
@@ -34,7 +34,7 @@
34 </span> 34 </span>
35 35
36 <my-global-icon *ngIf="activeFilter.canRemove" iconName="cross"></my-global-icon> 36 <my-global-icon *ngIf="activeFilter.canRemove" iconName="cross"></my-global-icon>
37 </div> 37 </button>
38 </div> 38 </div>
39 39
40 <ng-select 40 <ng-select
diff --git a/client/src/app/shared/shared-video-miniature/video-filters.model.ts b/client/src/app/shared/shared-video-miniature/video-filters.model.ts
index 4069ab4b5..6b4b72c75 100644
--- a/client/src/app/shared/shared-video-miniature/video-filters.model.ts
+++ b/client/src/app/shared/shared-video-miniature/video-filters.model.ts
@@ -37,7 +37,7 @@ export class VideoFilters {
37 private activeFilters: { key: string, canRemove: boolean, label: string, value?: string }[] = [] 37 private activeFilters: { key: string, canRemove: boolean, label: string, value?: string }[] = []
38 private defaultNSFWPolicy: NSFWPolicyType 38 private defaultNSFWPolicy: NSFWPolicyType
39 39
40 private onChangeCallbacks: Array<() => void> = [] 40 private onChangeCallbacks: (() => void)[] = []
41 private oldFormObjectString: string 41 private oldFormObjectString: string
42 42
43 private readonly hiddenFields: string[] = [] 43 private readonly hiddenFields: string[] = []
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 8c8bf80d9..909c86d3d 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
@@ -16,9 +16,9 @@
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="ms-2" (click)="action.click($event)" (key.enter)="action.click($event)"> 19 <button *ngIf="!action.routerLink && !action.href && action.click" class="ms-2 button-unstyle" (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 </button>
22 22
23 <a *ngIf="!action.routerLink && action.href && action.click" class="ms-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>
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.html b/client/src/app/shared/shared-video-miniature/videos-selection.component.html
index 6c6db4b96..8931158a9 100644
--- a/client/src/app/shared/shared-video-miniature/videos-selection.component.html
+++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.html
@@ -20,9 +20,9 @@
20 <!-- Display only once --> 20 <!-- Display only once -->
21 <div class="action-selection-mode" *ngIf="isInSelectionMode() === true && i === 0"> 21 <div class="action-selection-mode" *ngIf="isInSelectionMode() === true && i === 0">
22 <div class="action-selection-mode-child"> 22 <div class="action-selection-mode-child">
23 <span i18n class="action-button action-button-cancel-selection" (click)="abortSelectionMode()"> 23 <button i18n class="action-button action-button-cancel-selection" (click)="abortSelectionMode()">
24 Cancel 24 Cancel
25 </span> 25 </button>
26 26
27 <ng-container *ngTemplateOutlet="globalButtonsTemplate"></ng-container> 27 <ng-container *ngTemplateOutlet="globalButtonsTemplate"></ng-container>
28 </div> 28 </div>
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 6d787796a..f2f69236c 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
@@ -14,20 +14,20 @@
14 *ngFor="let playlist of videoPlaylists" 14 *ngFor="let playlist of videoPlaylists"
15 class="playlist dropdown-item" [ngClass]="{ 'has-optional-row': playlist.optionalRowDisplayed }" 15 class="playlist dropdown-item" [ngClass]="{ 'has-optional-row': playlist.optionalRowDisplayed }"
16 > 16 >
17 <div class="primary-row" (click)="toggleMainPlaylist($event, playlist)"> 17 <button class="primary-row button-unstyle" (click)="toggleMainPlaylist($event, playlist)">
18 <my-peertube-checkbox 18 <my-peertube-checkbox
19 [disabled]="isPresentMultipleTimes(playlist) || playlist.optionalRowDisplayed" [inputName]="getPrimaryInputName(playlist)" 19 [disabled]="isPresentMultipleTimes(playlist) || playlist.optionalRowDisplayed" [inputName]="getPrimaryInputName(playlist)"
20 [ngModel]="isPrimaryCheckboxChecked(playlist)" [onPushWorkaround]="true" 20 [ngModel]="isPrimaryCheckboxChecked(playlist)" [onPushWorkaround]="true"
21 ></my-peertube-checkbox> 21 ></my-peertube-checkbox>
22 22
23 <label class="display-name"> 23 <label [for]="getPrimaryInputName(playlist)" class="display-name">
24 {{ playlist.displayName }} 24 {{ playlist.displayName }}
25 </label> 25 </label>
26 26
27 <div class="optional-row-icon" *ngIf="isPrimaryCheckboxChecked(playlist)" (click)="$event.stopPropagation(); toggleOptionalRow(playlist)"> 27 <button class="optional-row-icon button-unstyle" *ngIf="isPrimaryCheckboxChecked(playlist)" (click)="$event.stopPropagation(); toggleOptionalRow(playlist)">
28 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> 28 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
29 </div> 29 </button>
30 </div> 30 </button>
31 31
32 <div class="optional-rows" *ngIf="playlist.optionalRowDisplayed"> 32 <div class="optional-rows" *ngIf="playlist.optionalRowDisplayed">
33 <div class="header-label" i18n>Start at</div> 33 <div class="header-label" i18n>Start at</div>
@@ -58,11 +58,11 @@
58 </div> 58 </div>
59 </div> 59 </div>
60 60
61 <div class="new-playlist-button dropdown-item" (click)="openCreateBlock($event)" [hidden]="isNewPlaylistBlockOpened"> 61 <button class="new-playlist-button dropdown-item" (click)="openCreateBlock($event)" [hidden]="isNewPlaylistBlockOpened">
62 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon> 62 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
63 63
64 <span i18n>Create a private playlist</span> 64 <span i18n>Create a private playlist</span>
65 </div> 65 </button>
66 66
67 <form class="new-playlist-block dropdown-item" *ngIf="isNewPlaylistBlockOpened" (ngSubmit)="createPlaylist()" [formGroup]="form"> 67 <form class="new-playlist-block dropdown-item" *ngIf="isNewPlaylistBlockOpened" (ngSubmit)="createPlaylist()" [formGroup]="form">
68 <div class="form-group"> 68 <div class="form-group">
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 de2f1032b..d1aa95266 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
@@ -67,6 +67,8 @@
67 } 67 }
68 68
69 .optional-row-icon { 69 .optional-row-icon {
70 @include margin-left(5px);
71
70 display: flex; 72 display: flex;
71 align-items: center; 73 align-items: center;
72 font-size: 14px; 74 font-size: 14px;
@@ -79,6 +81,10 @@
79 width: 19px; 81 width: 19px;
80 height: 19px; 82 height: 19px;
81 } 83 }
84
85 &:hover {
86 opacity: 0.8;
87 }
82 } 88 }
83} 89}
84 90
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 40c58166d..75afa0709 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
@@ -59,10 +59,10 @@
59 59
60 <div ngbDropdownMenu> 60 <div ngbDropdownMenu>
61 <ng-container *ngIf="playlistElement.video"> 61 <ng-container *ngIf="playlistElement.video">
62 <div class="dropdown-item" (click)="toggleDisplayTimestampsOptions($event, playlistElement)"> 62 <button ngbDropdownItem (click)="toggleDisplayTimestampsOptions($event, playlistElement)">
63 <my-global-icon iconName="edit" aria-hidden="true"></my-global-icon> 63 <my-global-icon iconName="edit" aria-hidden="true"></my-global-icon>
64 <ng-container i18n>Edit starts/stops at</ng-container> 64 <ng-container i18n>Edit starts/stops at</ng-container>
65 </div> 65 </button>
66 66
67 <div class="timestamp-options" *ngIf="displayTimestampOptions"> 67 <div class="timestamp-options" *ngIf="displayTimestampOptions">
68 <div> 68 <div>
@@ -97,10 +97,10 @@
97 </div> 97 </div>
98 </ng-container> 98 </ng-container>
99 99
100 <span class="dropdown-item" (click)="removeFromPlaylist(playlistElement)"> 100 <button ngbDropdownItem (click)="removeFromPlaylist(playlistElement)">
101 <my-global-icon iconName="delete" aria-hidden="true"></my-global-icon> 101 <my-global-icon iconName="delete" aria-hidden="true"></my-global-icon>
102 <ng-container i18n>Delete from {{ playlist?.displayName }}</ng-container> 102 <ng-container i18n>Delete from {{ playlist?.displayName }}</ng-container>
103 </span> 103 </button>
104 </div> 104 </div>
105 </div> 105 </div>
106</div> 106</div>
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 0f085231c..cd9d0ce53 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
@@ -156,6 +156,7 @@ my-video-thumbnail,
156 @include padding-left(35px); 156 @include padding-left(35px);
157 157
158 padding-top: 0; 158 padding-top: 0;
159 margin-top: 5px;
159 margin-bottom: 15px; 160 margin-bottom: 15px;
160 161
161 > div { 162 > div {
diff --git a/client/src/assets/player/shared/settings/settings-dialog.ts b/client/src/assets/player/shared/settings/settings-dialog.ts
index 8cd98967f..f5fbbe7ad 100644
--- a/client/src/assets/player/shared/settings/settings-dialog.ts
+++ b/client/src/assets/player/shared/settings/settings-dialog.ts
@@ -23,7 +23,7 @@ class SettingsDialog extends Component {
23 innerHTML: '', 23 innerHTML: '',
24 tabIndex: -1 24 tabIndex: -1
25 }, { 25 }, {
26 role: 'dialog', 26 'role': 'dialog',
27 'aria-labelledby': dialogLabelId, 27 'aria-labelledby': dialogLabelId,
28 'aria-describedby': dialogDescriptionId 28 'aria-describedby': dialogDescriptionId
29 }) 29 })
diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts
index ffbe64408..cc303b80b 100644
--- a/client/src/assets/player/utils.ts
+++ b/client/src/assets/player/utils.ts
@@ -32,7 +32,7 @@ function isSafari () {
32 32
33// https://github.com/danrevah/ngx-pipes/blob/master/src/pipes/math/bytes.ts 33// https://github.com/danrevah/ngx-pipes/blob/master/src/pipes/math/bytes.ts
34// Don't import all Angular stuff, just copy the code with shame 34// Don't import all Angular stuff, just copy the code with shame
35const dictionaryBytes: Array<{ max: number, type: string }> = [ 35const dictionaryBytes: { max: number, type: string }[] = [
36 { max: 1024, type: 'B' }, 36 { max: 1024, type: 'B' },
37 { max: 1048576, type: 'KB' }, 37 { max: 1048576, type: 'KB' },
38 { max: 1073741824, type: 'MB' }, 38 { max: 1073741824, type: 'MB' },
diff --git a/client/src/root-helpers/bytes.ts b/client/src/root-helpers/bytes.ts
index bda786cc6..7b1920a09 100644
--- a/client/src/root-helpers/bytes.ts
+++ b/client/src/root-helpers/bytes.ts
@@ -1,4 +1,4 @@
1const dictionary: Array<{ max: number, type: string }> = [ 1const dictionary: { max: number, type: string }[] = [
2 { max: 1024, type: 'B' }, 2 { max: 1024, type: 'B' },
3 { max: 1048576, type: 'KB' }, 3 { max: 1048576, type: 'KB' },
4 { max: 1073741824, type: 'MB' }, 4 { max: 1073741824, type: 'MB' },
diff --git a/client/src/root-helpers/logger.ts b/client/src/root-helpers/logger.ts
index 618be62cd..8181c13f3 100644
--- a/client/src/root-helpers/logger.ts
+++ b/client/src/root-helpers/logger.ts
@@ -59,7 +59,7 @@ class Logger {
59 if (!payload) return 59 if (!payload) return
60 60
61 const headers = new Headers({ 61 const headers = new Headers({
62 Accept: 'application/json', 62 'Accept': 'application/json',
63 'Content-Type': 'application/json' 63 'Content-Type': 'application/json'
64 }) 64 })
65 65
diff --git a/client/src/root-helpers/plugins-manager.ts b/client/src/root-helpers/plugins-manager.ts
index f3416e44a..f99c7598d 100644
--- a/client/src/root-helpers/plugins-manager.ts
+++ b/client/src/root-helpers/plugins-manager.ts
@@ -60,14 +60,14 @@ class PluginsManager {
60 private loadingScopes: { [id in PluginClientScope]?: boolean } = {} 60 private loadingScopes: { [id in PluginClientScope]?: boolean } = {}
61 61
62 private pluginsLoaded: { [ scope in PluginClientScope ]: ReplaySubject<boolean> } = { 62 private pluginsLoaded: { [ scope in PluginClientScope ]: ReplaySubject<boolean> } = {
63 common: new ReplaySubject<boolean>(1), 63 'common': new ReplaySubject<boolean>(1),
64 'admin-plugin': new ReplaySubject<boolean>(1), 64 'admin-plugin': new ReplaySubject<boolean>(1),
65 search: new ReplaySubject<boolean>(1), 65 'search': new ReplaySubject<boolean>(1),
66 'video-watch': new ReplaySubject<boolean>(1), 66 'video-watch': new ReplaySubject<boolean>(1),
67 signup: new ReplaySubject<boolean>(1), 67 'signup': new ReplaySubject<boolean>(1),
68 login: new ReplaySubject<boolean>(1), 68 'login': new ReplaySubject<boolean>(1),
69 'video-edit': new ReplaySubject<boolean>(1), 69 'video-edit': new ReplaySubject<boolean>(1),
70 embed: new ReplaySubject<boolean>(1), 70 'embed': new ReplaySubject<boolean>(1),
71 'my-library': new ReplaySubject<boolean>(1), 71 'my-library': new ReplaySubject<boolean>(1),
72 'video-channel': new ReplaySubject<boolean>(1) 72 'video-channel': new ReplaySubject<boolean>(1)
73 } 73 }
diff --git a/client/src/sass/class-helpers/_buttons.scss b/client/src/sass/class-helpers/_buttons.scss
index fdbf6f9d2..5079492d6 100644
--- a/client/src/sass/class-helpers/_buttons.scss
+++ b/client/src/sass/class-helpers/_buttons.scss
@@ -39,3 +39,14 @@
39.peertube-button-icon { 39.peertube-button-icon {
40 @include button-with-icon(18px, 3px, -1px); 40 @include button-with-icon(18px, 3px, -1px);
41} 41}
42
43.peertube-create-button {
44 @include peertube-button-link;
45 @include orange-button;
46 @include button-with-icon(20px, 5px, -1px);
47}
48
49.button-unstyle {
50 padding: 0;
51 border: 0;
52}
diff --git a/client/src/sass/class-helpers/_common.scss b/client/src/sass/class-helpers/_common.scss
index e42d7d587..90fc7e299 100644
--- a/client/src/sass/class-helpers/_common.scss
+++ b/client/src/sass/class-helpers/_common.scss
@@ -48,11 +48,15 @@
48 border-radius: 0.25rem; 48 border-radius: 0.25rem;
49 position: relative; 49 position: relative;
50 50
51 >label { 51 > .callout-title {
52 position: relative; 52 position: relative;
53 top: -5px; 53 top: -5px;
54 left: -10px; 54 left: -10px;
55 color: #6c757d !important; 55 color: #6c757d !important;
56 font-size: 1rem;
57 font-weight: $font-bold;
58 margin-bottom: 0.5rem;
59 line-height: inherit;
56 } 60 }
57 61
58 &:not(.callout-light) { 62 &:not(.callout-light) {
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss
index 1ce584f9b..7a100a53d 100644
--- a/client/src/sass/include/_mixins.scss
+++ b/client/src/sass/include/_mixins.scss
@@ -604,12 +604,6 @@
604 margin-bottom: 10px; 604 margin-bottom: 10px;
605} 605}
606 606
607@mixin create-button {
608 @include peertube-button-link;
609 @include orange-button;
610 @include button-with-icon(20px, 5px, -1px);
611}
612
613@mixin row-blocks ($column-responsive: true, $min-height: 130px, $separator: true) { 607@mixin row-blocks ($column-responsive: true, $min-height: 130px, $separator: true) {
614 display: flex; 608 display: flex;
615 min-height: $min-height; 609 min-height: $min-height;
diff --git a/client/src/standalone/videos/embed.html b/client/src/standalone/videos/embed.html
index eab6f81a6..32bf5f655 100644
--- a/client/src/standalone/videos/embed.html
+++ b/client/src/standalone/videos/embed.html
@@ -35,6 +35,7 @@
35 <body id="custom-css" class="standalone-video-embed"> 35 <body id="custom-css" class="standalone-video-embed">
36 36
37 <div id="error-block"> 37 <div id="error-block">
38 <!-- eslint-disable-next-line @angular-eslint/template/elements-content -->
38 <h1 id="error-title"></h1> 39 <h1 id="error-title"></h1>
39 40
40 <div id="error-content"></div> 41 <div id="error-content"></div>
diff --git a/client/src/standalone/videos/test-embed.html b/client/src/standalone/videos/test-embed.html
index 2b4918681..d818a45ee 100644
--- a/client/src/standalone/videos/test-embed.html
+++ b/client/src/standalone/videos/test-embed.html
@@ -6,7 +6,7 @@
6 <header> 6 <header>
7 <div class="logo"> 7 <div class="logo">
8 <div class="icon"> 8 <div class="icon">
9 <img src="../../assets/images/logo.svg"> 9 <img src="../../assets/images/logo.svg" alt="">
10 </div> 10 </div>
11 <div> 11 <div>
12 PeerTube 12 PeerTube
diff --git a/server/tests/api/live/live-save-replay.ts b/server/tests/api/live/live-save-replay.ts
index 05ffe8af9..8985c9b39 100644
--- a/server/tests/api/live/live-save-replay.ts
+++ b/server/tests/api/live/live-save-replay.ts
@@ -164,7 +164,7 @@ describe('Save replay setting', function () {
164 }) 164 })
165 165
166 it('Should correctly have updated the live and federated it when streaming in the live', async function () { 166 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
167 this.timeout(60000) 167 this.timeout(120000)
168 168
169 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID }) 169 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
170 170
@@ -179,7 +179,7 @@ describe('Save replay setting', function () {
179 }) 179 })
180 180
181 it('Should correctly delete the video files after the stream ended', async function () { 181 it('Should correctly delete the video files after the stream ended', async function () {
182 this.timeout(60000) 182 this.timeout(120000)
183 183
184 sessionEndDateMin = new Date() 184 sessionEndDateMin = new Date()
185 await stopFfmpeg(ffmpegCommand) 185 await stopFfmpeg(ffmpegCommand)
@@ -217,7 +217,7 @@ describe('Save replay setting', function () {
217 }) 217 })
218 218
219 it('Should correctly terminate the stream on blacklist and delete the live', async function () { 219 it('Should correctly terminate the stream on blacklist and delete the live', async function () {
220 this.timeout(60000) 220 this.timeout(120000)
221 221
222 await publishLiveAndBlacklist({ permanent: false, replay: false }) 222 await publishLiveAndBlacklist({ permanent: false, replay: false })
223 223
@@ -241,7 +241,7 @@ describe('Save replay setting', function () {
241 }) 241 })
242 242
243 it('Should correctly terminate the stream on delete and delete the video', async function () { 243 it('Should correctly terminate the stream on delete and delete the video', async function () {
244 this.timeout(60000) 244 this.timeout(120000)
245 245
246 await publishLiveAndDelete({ permanent: false, replay: false }) 246 await publishLiveAndDelete({ permanent: false, replay: false })
247 247
@@ -253,7 +253,7 @@ describe('Save replay setting', function () {
253 describe('With save replay enabled on non permanent live', function () { 253 describe('With save replay enabled on non permanent live', function () {
254 254
255 it('Should correctly create and federate the "waiting for stream" live', async function () { 255 it('Should correctly create and federate the "waiting for stream" live', async function () {
256 this.timeout(60000) 256 this.timeout(120000)
257 257
258 liveVideoUUID = await createLiveWrapper({ permanent: false, replay: true, replaySettings: { privacy: VideoPrivacy.UNLISTED } }) 258 liveVideoUUID = await createLiveWrapper({ permanent: false, replay: true, replaySettings: { privacy: VideoPrivacy.UNLISTED } })
259 259
@@ -265,7 +265,7 @@ describe('Save replay setting', function () {
265 }) 265 })
266 266
267 it('Should correctly have updated the live and federated it when streaming in the live', async function () { 267 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
268 this.timeout(60000) 268 this.timeout(120000)
269 269
270 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID }) 270 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
271 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID) 271 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
@@ -278,7 +278,7 @@ describe('Save replay setting', function () {
278 }) 278 })
279 279
280 it('Should correctly have saved the live and federated it after the streaming', async function () { 280 it('Should correctly have saved the live and federated it after the streaming', async function () {
281 this.timeout(60000) 281 this.timeout(120000)
282 282
283 const session = await servers[0].live.findLatestSession({ videoId: liveVideoUUID }) 283 const session = await servers[0].live.findLatestSession({ videoId: liveVideoUUID })
284 expect(session.endDate).to.not.exist 284 expect(session.endDate).to.not.exist
@@ -319,7 +319,7 @@ describe('Save replay setting', function () {
319 }) 319 })
320 320
321 it('Should update the saved live and correctly federate the updated attributes', async function () { 321 it('Should update the saved live and correctly federate the updated attributes', async function () {
322 this.timeout(60000) 322 this.timeout(120000)
323 323
324 await servers[0].videos.update({ id: liveVideoUUID, attributes: { name: 'video updated', privacy: VideoPrivacy.PUBLIC } }) 324 await servers[0].videos.update({ id: liveVideoUUID, attributes: { name: 'video updated', privacy: VideoPrivacy.PUBLIC } })
325 await waitJobs(servers) 325 await waitJobs(servers)
@@ -352,7 +352,7 @@ describe('Save replay setting', function () {
352 }) 352 })
353 353
354 it('Should correctly terminate the stream on delete and delete the video', async function () { 354 it('Should correctly terminate the stream on delete and delete the video', async function () {
355 this.timeout(60000) 355 this.timeout(120000)
356 356
357 await publishLiveAndDelete({ permanent: false, replay: true, replaySettings: { privacy: VideoPrivacy.PUBLIC } }) 357 await publishLiveAndDelete({ permanent: false, replay: true, replaySettings: { privacy: VideoPrivacy.PUBLIC } })
358 358
@@ -367,7 +367,7 @@ describe('Save replay setting', function () {
367 describe('With a first live and its replay', function () { 367 describe('With a first live and its replay', function () {
368 368
369 it('Should correctly create and federate the "waiting for stream" live', async function () { 369 it('Should correctly create and federate the "waiting for stream" live', async function () {
370 this.timeout(60000) 370 this.timeout(120000)
371 371
372 liveVideoUUID = await createLiveWrapper({ permanent: true, replay: true, replaySettings: { privacy: VideoPrivacy.UNLISTED } }) 372 liveVideoUUID = await createLiveWrapper({ permanent: true, replay: true, replaySettings: { privacy: VideoPrivacy.UNLISTED } })
373 373
@@ -379,7 +379,7 @@ describe('Save replay setting', function () {
379 }) 379 })
380 380
381 it('Should correctly have updated the live and federated it when streaming in the live', async function () { 381 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
382 this.timeout(60000) 382 this.timeout(120000)
383 383
384 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID }) 384 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
385 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID) 385 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
@@ -392,7 +392,7 @@ describe('Save replay setting', function () {
392 }) 392 })
393 393
394 it('Should correctly have saved the live and federated it after the streaming', async function () { 394 it('Should correctly have saved the live and federated it after the streaming', async function () {
395 this.timeout(60000) 395 this.timeout(120000)
396 396
397 const liveDetails = await servers[0].videos.get({ id: liveVideoUUID }) 397 const liveDetails = await servers[0].videos.get({ id: liveVideoUUID })
398 398
@@ -457,7 +457,7 @@ describe('Save replay setting', function () {
457 }) 457 })
458 458
459 it('Should correctly have updated the live and federated it when streaming in the live', async function () { 459 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
460 this.timeout(60000) 460 this.timeout(120000)
461 461
462 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID }) 462 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
463 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID) 463 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
@@ -470,7 +470,7 @@ describe('Save replay setting', function () {
470 }) 470 })
471 471
472 it('Should correctly have saved the live and federated it after the streaming', async function () { 472 it('Should correctly have saved the live and federated it after the streaming', async function () {
473 this.timeout(60000) 473 this.timeout(120000)
474 474
475 const liveDetails = await servers[0].videos.get({ id: liveVideoUUID }) 475 const liveDetails = await servers[0].videos.get({ id: liveVideoUUID })
476 476
@@ -547,7 +547,7 @@ describe('Save replay setting', function () {
547 }) 547 })
548 548
549 it('Should correctly terminate the stream on delete and not save the video', async function () { 549 it('Should correctly terminate the stream on delete and not save the video', async function () {
550 this.timeout(60000) 550 this.timeout(120000)
551 551
552 const { liveDetails } = await publishLiveAndDelete({ 552 const { liveDetails } = await publishLiveAndDelete({
553 permanent: true, 553 permanent: true,