diff options
Diffstat (limited to 'client/src/app/shared/shared-main')
30 files changed, 161 insertions, 193 deletions
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-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 | } |