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