diff options
Diffstat (limited to 'client/src/app/shared')
86 files changed, 728 insertions, 451 deletions
diff --git a/client/src/app/shared/shared-abuse-list/abuse-details.component.html b/client/src/app/shared/shared-abuse-list/abuse-details.component.html index 658d42537..ca68de4b1 100644 --- a/client/src/app/shared/shared-abuse-list/abuse-details.component.html +++ b/client/src/app/shared/shared-abuse-list/abuse-details.component.html | |||
@@ -7,16 +7,16 @@ | |||
7 | <span class="col-3 moderation-expanded-label" i18n>Reporter</span> | 7 | <span class="col-3 moderation-expanded-label" i18n>Reporter</span> |
8 | 8 | ||
9 | <span class="col-9 moderation-expanded-text"> | 9 | <span class="col-9 moderation-expanded-text"> |
10 | <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reporter:"' + abuse.reporterAccount.displayName + '"' }" | 10 | <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reporter:"' + abuse.reporterAccount.displayName + '"' }" |
11 | class="chip" | 11 | class="chip" |
12 | > | 12 | > |
13 | <my-account-avatar [account]="abuse.reporterAccount"></my-account-avatar> | 13 | <my-actor-avatar size="18" [account]="abuse.reporterAccount"></my-actor-avatar> |
14 | <div> | 14 | <div> |
15 | <span class="text-muted">{{ abuse.reporterAccount.nameWithHost }}</span> | 15 | <span class="text-muted">{{ abuse.reporterAccount.nameWithHost }}</span> |
16 | </div> | 16 | </div> |
17 | </a> | 17 | </a> |
18 | 18 | ||
19 | <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reporter:"' + abuse.reporterAccount.displayName + '"' }" | 19 | <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reporter:"' + abuse.reporterAccount.displayName + '"' }" |
20 | class="ml-auto text-muted abuse-details-links" i18n | 20 | class="ml-auto text-muted abuse-details-links" i18n |
21 | > | 21 | > |
22 | {abuse.countReportsForReporter, plural, =1 {1 report} other {{{ abuse.countReportsForReporter }} reports}}<span class="ml-1Â glyphicon glyphicon-flag"></span> | 22 | {abuse.countReportsForReporter, plural, =1 {1 report} other {{{ abuse.countReportsForReporter }} reports}}<span class="ml-1Â glyphicon glyphicon-flag"></span> |
@@ -27,16 +27,16 @@ | |||
27 | <div class="d-flex" *ngIf="abuse.flaggedAccount"> | 27 | <div class="d-flex" *ngIf="abuse.flaggedAccount"> |
28 | <span class="col-3 moderation-expanded-label" i18n>Reportee</span> | 28 | <span class="col-3 moderation-expanded-label" i18n>Reportee</span> |
29 | <span class="col-9 moderation-expanded-text"> | 29 | <span class="col-9 moderation-expanded-text"> |
30 | <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reportee:"' +abuse.flaggedAccount.displayName + '"' }" | 30 | <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reportee:"' +abuse.flaggedAccount.displayName + '"' }" |
31 | class="chip" | 31 | class="chip" |
32 | > | 32 | > |
33 | <my-account-avatar [account]="abuse.flaggedAccount"></my-account-avatar> | 33 | <my-actor-avatar size="18" [account]="abuse.flaggedAccount"></my-actor-avatar> |
34 | <div> | 34 | <div> |
35 | <span class="text-muted">{{ abuse.flaggedAccount ? abuse.flaggedAccount.nameWithHost : '' }}</span> | 35 | <span class="text-muted">{{ abuse.flaggedAccount ? abuse.flaggedAccount.nameWithHost : '' }}</span> |
36 | </div> | 36 | </div> |
37 | </a> | 37 | </a> |
38 | 38 | ||
39 | <a *ngIf="isAdminView" [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'reportee:"' +abuse.flaggedAccount.displayName + '"' }" | 39 | <a *ngIf="isAdminView" [routerLink]="[ '.' ]" [queryParams]="{ 'search': 'reportee:"' +abuse.flaggedAccount.displayName + '"' }" |
40 | class="ml-auto text-muted abuse-details-links" i18n | 40 | class="ml-auto text-muted abuse-details-links" i18n |
41 | > | 41 | > |
42 | {abuse.countReportsForReportee, plural, =1 {1 report} other {{{ abuse.countReportsForReportee }} reports}}<span class="ml-1Â glyphicon glyphicon-flag"></span> | 42 | {abuse.countReportsForReportee, plural, =1 {1 report} other {{{ abuse.countReportsForReportee }} reports}}<span class="ml-1Â glyphicon glyphicon-flag"></span> |
@@ -53,7 +53,7 @@ | |||
53 | <div class="mt-3 d-flex"> | 53 | <div class="mt-3 d-flex"> |
54 | <span class="col-3 moderation-expanded-label"> | 54 | <span class="col-3 moderation-expanded-label"> |
55 | <ng-container i18n>Report</ng-container> | 55 | <ng-container i18n>Report</ng-container> |
56 | <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': '#' + abuse.id }" class="ml-1 text-muted">#{{ abuse.id }}</a> | 56 | <a [routerLink]="[ '.' ]" [queryParams]="{ 'search': '#' + abuse.id }" class="ml-1 text-muted">#{{ abuse.id }}</a> |
57 | </span> | 57 | </span> |
58 | <span class="col-9 moderation-expanded-text" [innerHTML]="abuse.reasonHtml"></span> | 58 | <span class="col-9 moderation-expanded-text" [innerHTML]="abuse.reasonHtml"></span> |
59 | </div> | 59 | </div> |
@@ -61,7 +61,7 @@ | |||
61 | <div *ngIf="getPredefinedReasons()" class="mt-2 d-flex"> | 61 | <div *ngIf="getPredefinedReasons()" class="mt-2 d-flex"> |
62 | <span class="col-3"></span> | 62 | <span class="col-3"></span> |
63 | <span class="col-9"> | 63 | <span class="col-9"> |
64 | <a *ngFor="let reason of getPredefinedReasons()" [routerLink]="[ baseRoute ]" | 64 | <a *ngFor="let reason of getPredefinedReasons()" [routerLink]="[ '.' ]" |
65 | [queryParams]="{ 'search': 'tag:' + reason.id }" class="chip rectangular bg-secondary text-light" | 65 | [queryParams]="{ 'search': 'tag:' + reason.id }" class="chip rectangular bg-secondary text-light" |
66 | > | 66 | > |
67 | <div>{{ reason.label }}</div> | 67 | <div>{{ reason.label }}</div> |
diff --git a/client/src/app/shared/shared-abuse-list/abuse-details.component.ts b/client/src/app/shared/shared-abuse-list/abuse-details.component.ts index e8ce7e678..14674c5f0 100644 --- a/client/src/app/shared/shared-abuse-list/abuse-details.component.ts +++ b/client/src/app/shared/shared-abuse-list/abuse-details.component.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import { Component, Input } from '@angular/core' | 1 | import { Component, Input } from '@angular/core' |
2 | import { durationToString } from '@app/helpers' | 2 | import { durationToString } from '@app/helpers' |
3 | import { Account } from '@app/shared/shared-main' | ||
4 | import { AbusePredefinedReasonsString } from '@shared/models' | 3 | import { AbusePredefinedReasonsString } from '@shared/models' |
5 | import { ProcessedAbuse } from './processed-abuse.model' | 4 | import { ProcessedAbuse } from './processed-abuse.model' |
6 | 5 | ||
@@ -12,7 +11,6 @@ import { ProcessedAbuse } from './processed-abuse.model' | |||
12 | export class AbuseDetailsComponent { | 11 | export class AbuseDetailsComponent { |
13 | @Input() abuse: ProcessedAbuse | 12 | @Input() abuse: ProcessedAbuse |
14 | @Input() isAdminView: boolean | 13 | @Input() isAdminView: boolean |
15 | @Input() baseRoute: string | ||
16 | 14 | ||
17 | private predefinedReasonsTranslations: { [key in AbusePredefinedReasonsString]: string } | 15 | private predefinedReasonsTranslations: { [key in AbusePredefinedReasonsString]: string } |
18 | 16 | ||
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html index 29b51f09c..b1c065c7a 100644 --- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html +++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html | |||
@@ -1,6 +1,7 @@ | |||
1 | <p-table | 1 | <p-table |
2 | [value]="abuses" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" | 2 | [value]="abuses" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" |
3 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" [resizableColumns]="true" | 3 | [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true" |
4 | [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" | ||
4 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate | 5 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate |
5 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} reports" | 6 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} reports" |
6 | (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" | 7 | (onPage)="onPage($event)" [expandedRowKeys]="expandedRows" |
@@ -8,28 +9,7 @@ | |||
8 | <ng-template pTemplate="caption"> | 9 | <ng-template pTemplate="caption"> |
9 | <div class="caption"> | 10 | <div class="caption"> |
10 | <div class="ml-auto"> | 11 | <div class="ml-auto"> |
11 | <div class="input-group has-feedback has-clear"> | 12 | <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter> |
12 | <div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body"> | ||
13 | <div class="input-group-text" ngbDropdownToggle> | ||
14 | <span class="caret" aria-haspopup="menu" role="button"></span> | ||
15 | </div> | ||
16 | |||
17 | <div role="menu" ngbDropdownMenu> | ||
18 | <h6 class="dropdown-header" i18n>Advanced report filters</h6> | ||
19 | <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'state:pending' }" class="dropdown-item" i18n>Unsolved reports</a> | ||
20 | <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'state:accepted' }" class="dropdown-item" i18n>Accepted reports</a> | ||
21 | <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'state:rejected' }" class="dropdown-item" i18n>Refused reports</a> | ||
22 | <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'videoIs:blacklisted' }" class="dropdown-item" i18n>Reports with blocked videos</a> | ||
23 | <a [routerLink]="[ baseRoute ]" [queryParams]="{ 'search': 'videoIs:deleted' }" class="dropdown-item" i18n>Reports with deleted videos</a> | ||
24 | </div> | ||
25 | </div> | ||
26 | <input | ||
27 | type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..." | ||
28 | (keyup)="onSearch($event)" | ||
29 | > | ||
30 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetTableFilter()"></a> | ||
31 | <span class="sr-only" i18n>Clear filters</span> | ||
32 | </div> | ||
33 | </div> | 13 | </div> |
34 | </div> | 14 | </div> |
35 | </ng-template> | 15 | </ng-template> |
@@ -65,7 +45,7 @@ | |||
65 | <td *ngIf="isAdminView()"> | 45 | <td *ngIf="isAdminView()"> |
66 | <a *ngIf="abuse.reporterAccount" [href]="abuse.reporterAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> | 46 | <a *ngIf="abuse.reporterAccount" [href]="abuse.reporterAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> |
67 | <div class="chip two-lines"> | 47 | <div class="chip two-lines"> |
68 | <my-account-avatar [account]="abuse.reporterAccount"></my-account-avatar> | 48 | <my-actor-avatar [account]="abuse.reporterAccount"></my-actor-avatar> |
69 | <div> | 49 | <div> |
70 | {{ abuse.reporterAccount.displayName }} | 50 | {{ abuse.reporterAccount.displayName }} |
71 | <span>{{ abuse.reporterAccount.nameWithHost }}</span> | 51 | <span>{{ abuse.reporterAccount.nameWithHost }}</span> |
@@ -171,7 +151,7 @@ | |||
171 | <ng-template pTemplate="rowexpansion" let-abuse> | 151 | <ng-template pTemplate="rowexpansion" let-abuse> |
172 | <tr> | 152 | <tr> |
173 | <td class="expand-cell" colspan="8"> | 153 | <td class="expand-cell" colspan="8"> |
174 | <my-abuse-details [abuse]="abuse" [baseRoute]="baseRoute" [isAdminView]="isAdminView()"></my-abuse-details> | 154 | <my-abuse-details [abuse]="abuse" [isAdminView]="isAdminView()"></my-abuse-details> |
175 | </td> | 155 | </td> |
176 | </tr> | 156 | </tr> |
177 | </ng-template> | 157 | </ng-template> |
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts index 8b5771237..4dc2b4f10 100644 --- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts +++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts | |||
@@ -3,7 +3,7 @@ import truncate from 'lodash-es/truncate' | |||
3 | import { SortMeta } from 'primeng/api' | 3 | import { SortMeta } from 'primeng/api' |
4 | import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' | 4 | import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' |
5 | import { environment } from 'src/environments/environment' | 5 | import { environment } from 'src/environments/environment' |
6 | import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core' | 6 | import { Component, Input, OnInit, ViewChild } from '@angular/core' |
7 | import { DomSanitizer } from '@angular/platform-browser' | 7 | import { DomSanitizer } from '@angular/platform-browser' |
8 | import { ActivatedRoute, Router } from '@angular/router' | 8 | import { ActivatedRoute, Router } from '@angular/router' |
9 | import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' | 9 | import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' |
@@ -11,6 +11,7 @@ import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared | |||
11 | import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation' | 11 | import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation' |
12 | import { VideoCommentService } from '@app/shared/shared-video-comment' | 12 | import { VideoCommentService } from '@app/shared/shared-video-comment' |
13 | import { AbuseState, AdminAbuse } from '@shared/models' | 13 | import { AbuseState, AdminAbuse } from '@shared/models' |
14 | import { AdvancedInputFilter } from '../shared-forms' | ||
14 | import { AbuseMessageModalComponent } from './abuse-message-modal.component' | 15 | import { AbuseMessageModalComponent } from './abuse-message-modal.component' |
15 | import { ModerationCommentModalComponent } from './moderation-comment-modal.component' | 16 | import { ModerationCommentModalComponent } from './moderation-comment-modal.component' |
16 | import { ProcessedAbuse } from './processed-abuse.model' | 17 | import { ProcessedAbuse } from './processed-abuse.model' |
@@ -22,9 +23,8 @@ const logger = debug('peertube:moderation:AbuseListTableComponent') | |||
22 | templateUrl: './abuse-list-table.component.html', | 23 | templateUrl: './abuse-list-table.component.html', |
23 | styleUrls: [ '../shared-moderation/moderation.scss', './abuse-list-table.component.scss' ] | 24 | styleUrls: [ '../shared-moderation/moderation.scss', './abuse-list-table.component.scss' ] |
24 | }) | 25 | }) |
25 | export class AbuseListTableComponent extends RestTable implements OnInit, AfterViewInit { | 26 | export class AbuseListTableComponent extends RestTable implements OnInit { |
26 | @Input() viewType: 'admin' | 'user' | 27 | @Input() viewType: 'admin' | 'user' |
27 | @Input() baseRoute: string | ||
28 | 28 | ||
29 | @ViewChild('abuseMessagesModal', { static: true }) abuseMessagesModal: AbuseMessageModalComponent | 29 | @ViewChild('abuseMessagesModal', { static: true }) abuseMessagesModal: AbuseMessageModalComponent |
30 | @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent | 30 | @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent |
@@ -36,6 +36,29 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV | |||
36 | 36 | ||
37 | abuseActions: DropdownAction<ProcessedAbuse>[][] = [] | 37 | abuseActions: DropdownAction<ProcessedAbuse>[][] = [] |
38 | 38 | ||
39 | inputFilters: AdvancedInputFilter[] = [ | ||
40 | { | ||
41 | queryParams: { 'search': 'state:pending' }, | ||
42 | label: $localize`Unsolved reports` | ||
43 | }, | ||
44 | { | ||
45 | queryParams: { 'search': 'state:accepted' }, | ||
46 | label: $localize`Accepted reports` | ||
47 | }, | ||
48 | { | ||
49 | queryParams: { 'search': 'state:rejected' }, | ||
50 | label: $localize`Refused reports` | ||
51 | }, | ||
52 | { | ||
53 | queryParams: { 'search': 'videoIs:blacklisted' }, | ||
54 | label: $localize`Reports with blocked videos` | ||
55 | }, | ||
56 | { | ||
57 | queryParams: { 'search': 'videoIs:deleted' }, | ||
58 | label: $localize`Reports with deleted videos` | ||
59 | } | ||
60 | ] | ||
61 | |||
39 | constructor ( | 62 | constructor ( |
40 | protected route: ActivatedRoute, | 63 | protected route: ActivatedRoute, |
41 | protected router: Router, | 64 | protected router: Router, |
@@ -66,11 +89,6 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV | |||
66 | ] | 89 | ] |
67 | 90 | ||
68 | this.initialize() | 91 | this.initialize() |
69 | this.listenToSearchChange() | ||
70 | } | ||
71 | |||
72 | ngAfterViewInit () { | ||
73 | if (this.search) this.setTableFilter(this.search, false) | ||
74 | } | 92 | } |
75 | 93 | ||
76 | isAdminView () { | 94 | isAdminView () { |
@@ -86,7 +104,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV | |||
86 | } | 104 | } |
87 | 105 | ||
88 | onModerationCommentUpdated () { | 106 | onModerationCommentUpdated () { |
89 | this.loadData() | 107 | this.reloadData() |
90 | } | 108 | } |
91 | 109 | ||
92 | isAbuseAccepted (abuse: AdminAbuse) { | 110 | isAbuseAccepted (abuse: AdminAbuse) { |
@@ -129,7 +147,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV | |||
129 | this.abuseService.removeAbuse(abuse).subscribe( | 147 | this.abuseService.removeAbuse(abuse).subscribe( |
130 | () => { | 148 | () => { |
131 | this.notifier.success($localize`Abuse deleted.`) | 149 | this.notifier.success($localize`Abuse deleted.`) |
132 | this.loadData() | 150 | this.reloadData() |
133 | }, | 151 | }, |
134 | 152 | ||
135 | err => this.notifier.error(err.message) | 153 | err => this.notifier.error(err.message) |
@@ -139,7 +157,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV | |||
139 | updateAbuseState (abuse: AdminAbuse, state: AbuseState) { | 157 | updateAbuseState (abuse: AdminAbuse, state: AbuseState) { |
140 | this.abuseService.updateAbuse(abuse, { state }) | 158 | this.abuseService.updateAbuse(abuse, { state }) |
141 | .subscribe( | 159 | .subscribe( |
142 | () => this.loadData(), | 160 | () => this.reloadData(), |
143 | 161 | ||
144 | err => this.notifier.error(err.message) | 162 | err => this.notifier.error(err.message) |
145 | ) | 163 | ) |
@@ -166,7 +184,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV | |||
166 | return Actor.IS_LOCAL(abuse.reporterAccount.host) | 184 | return Actor.IS_LOCAL(abuse.reporterAccount.host) |
167 | } | 185 | } |
168 | 186 | ||
169 | protected loadData () { | 187 | protected reloadData () { |
170 | logger('Loading data.') | 188 | logger('Loading data.') |
171 | 189 | ||
172 | const options = { | 190 | const options = { |
diff --git a/client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts b/client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts index 19b6d456d..8f3830a17 100644 --- a/client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts +++ b/client/src/app/shared/shared-abuse-list/shared-abuse-list.module.ts | |||
@@ -10,7 +10,7 @@ import { AbuseDetailsComponent } from './abuse-details.component' | |||
10 | import { AbuseListTableComponent } from './abuse-list-table.component' | 10 | import { AbuseListTableComponent } from './abuse-list-table.component' |
11 | import { AbuseMessageModalComponent } from './abuse-message-modal.component' | 11 | import { AbuseMessageModalComponent } from './abuse-message-modal.component' |
12 | import { ModerationCommentModalComponent } from './moderation-comment-modal.component' | 12 | import { ModerationCommentModalComponent } from './moderation-comment-modal.component' |
13 | import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-account-avatar.module' | 13 | import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module' |
14 | 14 | ||
15 | @NgModule({ | 15 | @NgModule({ |
16 | imports: [ | 16 | imports: [ |
@@ -21,7 +21,7 @@ import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-accou | |||
21 | SharedModerationModule, | 21 | SharedModerationModule, |
22 | SharedGlobalIconModule, | 22 | SharedGlobalIconModule, |
23 | SharedVideoCommentModule, | 23 | SharedVideoCommentModule, |
24 | SharedAccountAvatarModule | 24 | SharedActorImageModule |
25 | ], | 25 | ], |
26 | 26 | ||
27 | declarations: [ | 27 | declarations: [ |
diff --git a/client/src/app/shared/shared-account-avatar/account-avatar.component.html b/client/src/app/shared/shared-account-avatar/account-avatar.component.html deleted file mode 100644 index ca4ceb12f..000000000 --- a/client/src/app/shared/shared-account-avatar/account-avatar.component.html +++ /dev/null | |||
@@ -1,15 +0,0 @@ | |||
1 | <ng-template #img> | ||
2 | <img [class]="class" [src]="avatarUrl" i18n-alt alt="Account avatar" /> | ||
3 | </ng-template> | ||
4 | |||
5 | <a *ngIf="account && href" [href]="href" target="_blank" rel="noopener noreferrer" [title]="title"> | ||
6 | <ng-template *ngTemplateOutlet="img"></ng-template> | ||
7 | </a> | ||
8 | |||
9 | <a *ngIf="account && internalHref" [routerLink]="internalHref" [title]="title"> | ||
10 | <ng-template *ngTemplateOutlet="img"></ng-template> | ||
11 | </a> | ||
12 | |||
13 | <ng-container *ngIf="!account || (!href && !internalHref)"> | ||
14 | <ng-template *ngTemplateOutlet="img"></ng-template> | ||
15 | </ng-container> | ||
diff --git a/client/src/app/shared/shared-account-avatar/account-avatar.component.scss b/client/src/app/shared/shared-account-avatar/account-avatar.component.scss deleted file mode 100644 index bb941d712..000000000 --- a/client/src/app/shared/shared-account-avatar/account-avatar.component.scss +++ /dev/null | |||
@@ -1,22 +0,0 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .avatar-25 { | ||
5 | @include avatar(25px); | ||
6 | } | ||
7 | |||
8 | .avatar-34 { | ||
9 | @include avatar(34px); | ||
10 | } | ||
11 | |||
12 | .avatar-36 { | ||
13 | @include avatar(36px); | ||
14 | } | ||
15 | |||
16 | .avatar-40 { | ||
17 | @include avatar(40px); | ||
18 | } | ||
19 | |||
20 | .avatar-120 { | ||
21 | @include avatar(120px); | ||
22 | } \ No newline at end of file | ||
diff --git a/client/src/app/shared/shared-account-avatar/account-avatar.component.ts b/client/src/app/shared/shared-account-avatar/account-avatar.component.ts deleted file mode 100644 index 02a0a18bf..000000000 --- a/client/src/app/shared/shared-account-avatar/account-avatar.component.ts +++ /dev/null | |||
@@ -1,39 +0,0 @@ | |||
1 | import { Component, Input } from '@angular/core' | ||
2 | import { Account } from '../shared-main/account/account.model' | ||
3 | |||
4 | @Component({ | ||
5 | selector: 'my-account-avatar', | ||
6 | styleUrls: [ './account-avatar.component.scss' ], | ||
7 | templateUrl: './account-avatar.component.html' | ||
8 | }) | ||
9 | export class AccountAvatarComponent { | ||
10 | @Input() account: { | ||
11 | name: string | ||
12 | avatar?: { url?: string, path: string } | ||
13 | url: string | ||
14 | } | ||
15 | @Input() size: '25' | '34' | '36' | '40' | '120' = '36' | ||
16 | |||
17 | // Use an external link | ||
18 | @Input() href: string | ||
19 | // Use routerLink | ||
20 | @Input() internalHref: string | string[] | ||
21 | |||
22 | @Input() set title (value) { | ||
23 | this._title = value | ||
24 | } | ||
25 | |||
26 | private _title: string | ||
27 | |||
28 | get title () { | ||
29 | return this._title || $localize`${this.account.name} (account page)` | ||
30 | } | ||
31 | |||
32 | get class () { | ||
33 | return `avatar avatar-${this.size}` | ||
34 | } | ||
35 | |||
36 | get avatarUrl () { | ||
37 | return Account.GET_ACTOR_AVATAR_URL(this.account) | ||
38 | } | ||
39 | } | ||
diff --git a/client/src/app/shared/shared-account-avatar/index.ts b/client/src/app/shared/shared-account-avatar/index.ts deleted file mode 100644 index 40c742ba5..000000000 --- a/client/src/app/shared/shared-account-avatar/index.ts +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | export * from './account-avatar.component' | ||
2 | export * from './shared-account-avatar.module' \ No newline at end of file | ||
diff --git a/client/src/app/shared/shared-account-avatar/shared-account-avatar.module.ts b/client/src/app/shared/shared-account-avatar/shared-account-avatar.module.ts deleted file mode 100644 index 17b27589f..000000000 --- a/client/src/app/shared/shared-account-avatar/shared-account-avatar.module.ts +++ /dev/null | |||
@@ -1,23 +0,0 @@ | |||
1 | |||
2 | import { NgModule } from '@angular/core' | ||
3 | import { SharedGlobalIconModule } from '../shared-icons' | ||
4 | import { SharedMainModule } from '../shared-main/shared-main.module' | ||
5 | import { AccountAvatarComponent } from './account-avatar.component' | ||
6 | |||
7 | @NgModule({ | ||
8 | imports: [ | ||
9 | SharedMainModule, | ||
10 | SharedGlobalIconModule | ||
11 | ], | ||
12 | |||
13 | declarations: [ | ||
14 | AccountAvatarComponent | ||
15 | ], | ||
16 | |||
17 | exports: [ | ||
18 | AccountAvatarComponent | ||
19 | ], | ||
20 | |||
21 | providers: [ ] | ||
22 | }) | ||
23 | export class SharedAccountAvatarModule { } | ||
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html index 0829263f4..e9c5fadcf 100644 --- a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html +++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.html | |||
@@ -1,6 +1,6 @@ | |||
1 | <div class="actor" *ngIf="actor"> | 1 | <div class="actor" *ngIf="actor"> |
2 | <div class="d-flex"> | 2 | <div class="d-flex"> |
3 | <img [ngClass]="{ channel: isChannel() }" [src]="preview || actor.avatarUrl" alt="Avatar" /> | 3 | <my-actor-avatar [channel]="getChannel()" [account]="getAccount()" [previewImage]="preview" size="100"></my-actor-avatar> |
4 | 4 | ||
5 | <div class="actor-img-edit-container"> | 5 | <div class="actor-img-edit-container"> |
6 | 6 | ||
@@ -34,6 +34,7 @@ | |||
34 | <span for="avatarfile" i18n>Upload a new avatar</span> | 34 | <span for="avatarfile" i18n>Upload a new avatar</span> |
35 | <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/> | 35 | <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/> |
36 | </div> | 36 | </div> |
37 | |||
37 | <div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()"> | 38 | <div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()"> |
38 | <my-global-icon iconName="delete"></my-global-icon> | 39 | <my-global-icon iconName="delete"></my-global-icon> |
39 | <span i18n>Remove avatar</span> | 40 | <span i18n>Remove avatar</span> |
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss index 8b0172315..08e80c3b4 100644 --- a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss +++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.scss | |||
@@ -4,16 +4,8 @@ | |||
4 | .actor { | 4 | .actor { |
5 | display: flex; | 5 | display: flex; |
6 | 6 | ||
7 | img { | 7 | my-actor-avatar { |
8 | margin-right: 15px; | 8 | margin-right: 15px; |
9 | |||
10 | &:not(.channel) { | ||
11 | @include avatar(100px); | ||
12 | } | ||
13 | |||
14 | &.channel { | ||
15 | @include channel-avatar(100px); | ||
16 | } | ||
17 | } | 9 | } |
18 | 10 | ||
19 | .actor-info { | 11 | .actor-info { |
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.ts b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts index d0d269489..840946690 100644 --- a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.ts +++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts | |||
@@ -80,4 +80,16 @@ export class ActorAvatarEditComponent implements OnInit { | |||
80 | isChannel () { | 80 | isChannel () { |
81 | return !!(this.actor as VideoChannel).ownerAccount | 81 | return !!(this.actor as VideoChannel).ownerAccount |
82 | } | 82 | } |
83 | |||
84 | getChannel (): VideoChannel { | ||
85 | if (this.isChannel()) return this.actor as VideoChannel | ||
86 | |||
87 | return undefined | ||
88 | } | ||
89 | |||
90 | getAccount (): Account { | ||
91 | if (this.isChannel()) return undefined | ||
92 | |||
93 | return this.actor as Account | ||
94 | } | ||
83 | } | 95 | } |
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.html b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html index 266fc26c5..266fc26c5 100644 --- a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.html +++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.html | |||
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss index 23606f871..23606f871 100644 --- a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss +++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.scss | |||
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts index 8c12d3c4c..8c12d3c4c 100644 --- a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts +++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts | |||
diff --git a/client/src/app/shared/shared-actor-image/actor-image-edit.scss b/client/src/app/shared/shared-actor-image-edit/actor-image-edit.scss index 918955a89..918955a89 100644 --- a/client/src/app/shared/shared-actor-image/actor-image-edit.scss +++ b/client/src/app/shared/shared-actor-image-edit/actor-image-edit.scss | |||
diff --git a/client/src/app/shared/shared-actor-image-edit/index.ts b/client/src/app/shared/shared-actor-image-edit/index.ts new file mode 100644 index 000000000..276b2e2fb --- /dev/null +++ b/client/src/app/shared/shared-actor-image-edit/index.ts | |||
@@ -0,0 +1 @@ | |||
export * from './shared-actor-image-edit.module' | |||
diff --git a/client/src/app/shared/shared-actor-image-edit/shared-actor-image-edit.module.ts b/client/src/app/shared/shared-actor-image-edit/shared-actor-image-edit.module.ts new file mode 100644 index 000000000..f6a397d5c --- /dev/null +++ b/client/src/app/shared/shared-actor-image-edit/shared-actor-image-edit.module.ts | |||
@@ -0,0 +1,31 @@ | |||
1 | |||
2 | import { CommonModule } from '@angular/common' | ||
3 | import { NgModule } from '@angular/core' | ||
4 | import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module' | ||
5 | import { SharedGlobalIconModule } from '../shared-icons' | ||
6 | import { SharedMainModule } from '../shared-main' | ||
7 | import { ActorAvatarEditComponent } from './actor-avatar-edit.component' | ||
8 | import { ActorBannerEditComponent } from './actor-banner-edit.component' | ||
9 | |||
10 | @NgModule({ | ||
11 | imports: [ | ||
12 | CommonModule, | ||
13 | |||
14 | SharedMainModule, | ||
15 | SharedActorImageModule, | ||
16 | SharedGlobalIconModule | ||
17 | ], | ||
18 | |||
19 | declarations: [ | ||
20 | ActorAvatarEditComponent, | ||
21 | ActorBannerEditComponent | ||
22 | ], | ||
23 | |||
24 | exports: [ | ||
25 | ActorAvatarEditComponent, | ||
26 | ActorBannerEditComponent | ||
27 | ], | ||
28 | |||
29 | providers: [ ] | ||
30 | }) | ||
31 | export class SharedActorImageEditModule { } | ||
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.html b/client/src/app/shared/shared-actor-image/actor-avatar.component.html new file mode 100644 index 000000000..13a5385a8 --- /dev/null +++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.html | |||
@@ -0,0 +1,19 @@ | |||
1 | <ng-template #img> | ||
2 | <img *ngIf="previewImage || avatarUrl || !initial" [class]="getClass('avatar')" [src]="previewImage || avatarUrl || defaultAvatarUrl" [alt]="alt" /> | ||
3 | |||
4 | <div *ngIf="!avatarUrl && initial" [class]="getClass('initial')"> | ||
5 | <span>{{ initial }}</span> | ||
6 | </div> | ||
7 | </ng-template> | ||
8 | |||
9 | <a *ngIf="hasActor() && href" [href]="href" target="_blank" rel="noopener noreferrer" [title]="title"> | ||
10 | <ng-template *ngTemplateOutlet="img"></ng-template> | ||
11 | </a> | ||
12 | |||
13 | <a *ngIf="hasActor() && internalHref" [routerLink]="internalHref" [title]="title"> | ||
14 | <ng-template *ngTemplateOutlet="img"></ng-template> | ||
15 | </a> | ||
16 | |||
17 | <ng-container *ngIf="!hasActor() || (!href && !internalHref)"> | ||
18 | <ng-template *ngTemplateOutlet="img"></ng-template> | ||
19 | </ng-container> | ||
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.scss b/client/src/app/shared/shared-actor-image/actor-avatar.component.scss new file mode 100644 index 000000000..bf50de4e9 --- /dev/null +++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.scss | |||
@@ -0,0 +1,101 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .avatar { | ||
5 | --avatarSize: 100%; | ||
6 | --initialFontSize: 22px; | ||
7 | |||
8 | width: var(--avatarSize); | ||
9 | height: var(--avatarSize); | ||
10 | min-width: var(--avatarSize); | ||
11 | min-height: var(--avatarSize); | ||
12 | |||
13 | &.account { | ||
14 | object-fit: cover; | ||
15 | border-radius: 50%; | ||
16 | } | ||
17 | |||
18 | &.channel { | ||
19 | border-radius: 5px; | ||
20 | } | ||
21 | } | ||
22 | |||
23 | .avatar-18 { | ||
24 | --avatarSize: 18px; | ||
25 | --initialFontSize: 13px; | ||
26 | } | ||
27 | |||
28 | .avatar-25 { | ||
29 | --avatarSize: 25px; | ||
30 | } | ||
31 | |||
32 | .avatar-32 { | ||
33 | --avatarSize: 32px; | ||
34 | } | ||
35 | |||
36 | .avatar-34 { | ||
37 | --avatarSize: 34px; | ||
38 | } | ||
39 | |||
40 | .avatar-36 { | ||
41 | --avatarSize: 36px; | ||
42 | } | ||
43 | |||
44 | .avatar-40 { | ||
45 | --avatarSize: 40px; | ||
46 | } | ||
47 | |||
48 | .avatar-100 { | ||
49 | --avatarSize: 100px; | ||
50 | --initialFontSize: 40px; | ||
51 | } | ||
52 | |||
53 | .avatar-120 { | ||
54 | --avatarSize: 120px; | ||
55 | --initialFontSize: 46px; | ||
56 | } | ||
57 | |||
58 | a:hover { | ||
59 | text-decoration: none; | ||
60 | } | ||
61 | |||
62 | .initial { | ||
63 | background-color: #3C2109; | ||
64 | color: #fff; | ||
65 | display: flex; | ||
66 | align-items: center; | ||
67 | justify-content: center; | ||
68 | font-size: var(--initialFontSize); | ||
69 | |||
70 | &.blue { | ||
71 | background-color: #009FD4; | ||
72 | } | ||
73 | |||
74 | &.green { | ||
75 | background-color: #00AA55; | ||
76 | } | ||
77 | |||
78 | &.purple { | ||
79 | background-color: #B381B3; | ||
80 | } | ||
81 | |||
82 | &.gray { | ||
83 | background-color: #939393; | ||
84 | } | ||
85 | |||
86 | &.yellow { | ||
87 | background-color: #AA8F00; | ||
88 | } | ||
89 | |||
90 | &.orange { | ||
91 | background-color: #D47500; | ||
92 | } | ||
93 | |||
94 | &.red { | ||
95 | background-color: #E76E3C; | ||
96 | } | ||
97 | |||
98 | &.dark-blue { | ||
99 | background-color: #0A3055; | ||
100 | } | ||
101 | } | ||
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.ts b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts new file mode 100644 index 000000000..b06c2bae6 --- /dev/null +++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts | |||
@@ -0,0 +1,111 @@ | |||
1 | import { Component, Input } from '@angular/core' | ||
2 | import { SafeResourceUrl } from '@angular/platform-browser' | ||
3 | import { VideoChannel } from '../shared-main' | ||
4 | import { Account } from '../shared-main/account/account.model' | ||
5 | |||
6 | type ActorInput = { | ||
7 | name: string | ||
8 | avatar?: { url?: string, path: string } | ||
9 | url: string | ||
10 | } | ||
11 | |||
12 | export type ActorAvatarSize = '18' | '25' | '32' | '34' | '36' | '40' | '100' | '120' | ||
13 | |||
14 | @Component({ | ||
15 | selector: 'my-actor-avatar', | ||
16 | styleUrls: [ './actor-avatar.component.scss' ], | ||
17 | templateUrl: './actor-avatar.component.html' | ||
18 | }) | ||
19 | export class ActorAvatarComponent { | ||
20 | @Input() account: ActorInput | ||
21 | @Input() channel: ActorInput | ||
22 | |||
23 | @Input() previewImage: SafeResourceUrl | ||
24 | |||
25 | @Input() size: ActorAvatarSize | ||
26 | |||
27 | // Use an external link | ||
28 | @Input() href: string | ||
29 | // Use routerLink | ||
30 | @Input() internalHref: string | any[] | ||
31 | |||
32 | @Input() set title (value) { | ||
33 | this._title = value | ||
34 | } | ||
35 | |||
36 | private _title: string | ||
37 | |||
38 | get title () { | ||
39 | if (this._title) return this._title | ||
40 | if (this.account) return $localize`${this.account.name} (account page)` | ||
41 | if (this.channel) return $localize`${this.channel.name} (channel page)` | ||
42 | |||
43 | return '' | ||
44 | } | ||
45 | |||
46 | get alt () { | ||
47 | if (this.account) return $localize`Account avatar` | ||
48 | if (this.channel) return $localize`Channel avatar` | ||
49 | |||
50 | return '' | ||
51 | } | ||
52 | |||
53 | getClass (type: 'avatar' | 'initial') { | ||
54 | const base = [ 'avatar' ] | ||
55 | |||
56 | if (this.size) base.push(`avatar-${this.size}`) | ||
57 | |||
58 | if (this.channel) base.push('channel') | ||
59 | else base.push('account') | ||
60 | |||
61 | if (type === 'initial' && this.initial) { | ||
62 | base.push('initial') | ||
63 | base.push(this.getColorTheme()) | ||
64 | } | ||
65 | |||
66 | return base | ||
67 | } | ||
68 | |||
69 | get defaultAvatarUrl () { | ||
70 | if (this.channel) return VideoChannel.GET_DEFAULT_AVATAR_URL() | ||
71 | |||
72 | return Account.GET_DEFAULT_AVATAR_URL() | ||
73 | } | ||
74 | |||
75 | get avatarUrl () { | ||
76 | if (this.account) return Account.GET_ACTOR_AVATAR_URL(this.account) | ||
77 | if (this.channel) return VideoChannel.GET_ACTOR_AVATAR_URL(this.channel) | ||
78 | |||
79 | return '' | ||
80 | } | ||
81 | |||
82 | get initial () { | ||
83 | const name = this.account?.name | ||
84 | if (!name) return '' | ||
85 | |||
86 | return name.slice(0, 1) | ||
87 | } | ||
88 | |||
89 | hasActor () { | ||
90 | return !!this.account || !!this.channel | ||
91 | } | ||
92 | |||
93 | private getColorTheme () { | ||
94 | // Keep consistency with CSS | ||
95 | const themes = { | ||
96 | abc: 'blue', | ||
97 | def: 'green', | ||
98 | ghi: 'purple', | ||
99 | jkl: 'gray', | ||
100 | mno: 'yellow', | ||
101 | pqr: 'orange', | ||
102 | stvu: 'red', | ||
103 | wxyz: 'dark-blue' | ||
104 | } | ||
105 | |||
106 | const theme = Object.keys(themes) | ||
107 | .find(chars => chars.includes(this.initial)) | ||
108 | |||
109 | return themes[theme] | ||
110 | } | ||
111 | } | ||
diff --git a/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts b/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts index 6044f9925..8ea4bb2bf 100644 --- a/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts +++ b/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts | |||
@@ -1,27 +1,21 @@ | |||
1 | 1 | ||
2 | import { CommonModule } from '@angular/common' | ||
3 | import { NgModule } from '@angular/core' | 2 | import { NgModule } from '@angular/core' |
4 | import { SharedGlobalIconModule } from '../shared-icons' | 3 | import { SharedGlobalIconModule } from '../shared-icons' |
5 | import { SharedMainModule } from '../shared-main' | 4 | import { SharedMainModule } from '../shared-main/shared-main.module' |
6 | import { ActorAvatarEditComponent } from './actor-avatar-edit.component' | 5 | import { ActorAvatarComponent } from './actor-avatar.component' |
7 | import { ActorBannerEditComponent } from './actor-banner-edit.component' | ||
8 | 6 | ||
9 | @NgModule({ | 7 | @NgModule({ |
10 | imports: [ | 8 | imports: [ |
11 | CommonModule, | ||
12 | |||
13 | SharedMainModule, | 9 | SharedMainModule, |
14 | SharedGlobalIconModule | 10 | SharedGlobalIconModule |
15 | ], | 11 | ], |
16 | 12 | ||
17 | declarations: [ | 13 | declarations: [ |
18 | ActorAvatarEditComponent, | 14 | ActorAvatarComponent |
19 | ActorBannerEditComponent | ||
20 | ], | 15 | ], |
21 | 16 | ||
22 | exports: [ | 17 | exports: [ |
23 | ActorAvatarEditComponent, | 18 | ActorAvatarComponent |
24 | ActorBannerEditComponent | ||
25 | ], | 19 | ], |
26 | 20 | ||
27 | providers: [ ] | 21 | providers: [ ] |
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.html b/client/src/app/shared/shared-forms/advanced-input-filter.component.html new file mode 100644 index 000000000..10d1296cf --- /dev/null +++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.html | |||
@@ -0,0 +1,24 @@ | |||
1 | <div class="input-group has-feedback has-clear"> | ||
2 | <div *ngIf="hasFilters()" class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body"> | ||
3 | <div class="input-group-text" ngbDropdownToggle> | ||
4 | <span class="caret" aria-haspopup="menu" role="button"></span> | ||
5 | </div> | ||
6 | |||
7 | <div role="menu" ngbDropdownMenu> | ||
8 | <h6 class="dropdown-header" i18n>Advanced filters</h6> | ||
9 | |||
10 | <a *ngFor="let filter of filters" [routerLink]="[ '.' ]" [queryParams]="filter.queryParams" class="dropdown-item"> | ||
11 | {{ filter.label }} | ||
12 | </a> | ||
13 | </div> | ||
14 | </div> | ||
15 | |||
16 | <input | ||
17 | type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..." | ||
18 | [(ngModel)]="searchValue" | ||
19 | (keyup)="onInputSearch($event)" | ||
20 | > | ||
21 | |||
22 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="onResetTableFilter()"></a> | ||
23 | <span class="sr-only" i18n>Clear filters</span> | ||
24 | </div> | ||
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.scss b/client/src/app/shared/shared-forms/advanced-input-filter.component.scss new file mode 100644 index 000000000..7c2198927 --- /dev/null +++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.scss | |||
@@ -0,0 +1,10 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | input { | ||
5 | @include peertube-input-text(250px); | ||
6 | } | ||
7 | |||
8 | .input-group-text { | ||
9 | background-color: transparent; | ||
10 | } | ||
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.ts b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts new file mode 100644 index 000000000..c11f1ad1d --- /dev/null +++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts | |||
@@ -0,0 +1,116 @@ | |||
1 | import * as debug from 'debug' | ||
2 | import { Subject } from 'rxjs' | ||
3 | import { debounceTime, distinctUntilChanged } from 'rxjs/operators' | ||
4 | import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output } from '@angular/core' | ||
5 | import { ActivatedRoute, Params, Router } from '@angular/router' | ||
6 | |||
7 | export type AdvancedInputFilter = { | ||
8 | label: string | ||
9 | queryParams: Params | ||
10 | } | ||
11 | |||
12 | const logger = debug('peertube:AdvancedInputFilterComponent') | ||
13 | |||
14 | @Component({ | ||
15 | selector: 'my-advanced-input-filter', | ||
16 | templateUrl: './advanced-input-filter.component.html', | ||
17 | styleUrls: [ './advanced-input-filter.component.scss' ] | ||
18 | }) | ||
19 | export class AdvancedInputFilterComponent implements OnInit, AfterViewInit { | ||
20 | @Input() filters: AdvancedInputFilter[] = [] | ||
21 | |||
22 | @Output() search = new EventEmitter<string>() | ||
23 | |||
24 | searchValue: string | ||
25 | |||
26 | private searchStream: Subject<string> | ||
27 | |||
28 | private viewInitialized = false | ||
29 | private emitSearchAfterViewInit = false | ||
30 | |||
31 | constructor ( | ||
32 | private route: ActivatedRoute, | ||
33 | private router: Router | ||
34 | ) { } | ||
35 | |||
36 | ngOnInit () { | ||
37 | this.initSearchStream() | ||
38 | this.listenToRouteSearchChange() | ||
39 | } | ||
40 | |||
41 | ngAfterViewInit () { | ||
42 | this.viewInitialized = true | ||
43 | |||
44 | // Init after view init to not send an event too early | ||
45 | if (this.emitSearchAfterViewInit) this.emitSearch() | ||
46 | } | ||
47 | |||
48 | onInputSearch (event: Event) { | ||
49 | this.scheduleSearchUpdate((event.target as HTMLInputElement).value) | ||
50 | } | ||
51 | |||
52 | onResetTableFilter () { | ||
53 | this.immediateSearchUpdate('') | ||
54 | } | ||
55 | |||
56 | hasFilters () { | ||
57 | return this.filters.length !== 0 | ||
58 | } | ||
59 | |||
60 | private scheduleSearchUpdate (value: string) { | ||
61 | this.searchValue = value | ||
62 | this.searchStream.next(this.searchValue) | ||
63 | } | ||
64 | |||
65 | private immediateSearchUpdate (value: string) { | ||
66 | this.searchValue = value | ||
67 | |||
68 | this.setQueryParams(this.searchValue) | ||
69 | this.emitSearch() | ||
70 | } | ||
71 | |||
72 | private listenToRouteSearchChange () { | ||
73 | this.route.queryParams | ||
74 | .subscribe(params => { | ||
75 | const search = params.search || '' | ||
76 | |||
77 | logger('On route search change "%s".', search) | ||
78 | |||
79 | this.searchValue = search | ||
80 | this.emitSearch() | ||
81 | }) | ||
82 | } | ||
83 | |||
84 | private initSearchStream () { | ||
85 | this.searchStream = new Subject() | ||
86 | |||
87 | this.searchStream | ||
88 | .pipe( | ||
89 | debounceTime(300), | ||
90 | distinctUntilChanged() | ||
91 | ) | ||
92 | .subscribe(() => { | ||
93 | this.setQueryParams(this.searchValue) | ||
94 | |||
95 | this.emitSearch() | ||
96 | }) | ||
97 | } | ||
98 | |||
99 | private emitSearch () { | ||
100 | if (!this.viewInitialized) { | ||
101 | this.emitSearchAfterViewInit = true | ||
102 | return | ||
103 | } | ||
104 | |||
105 | logger('On search "%s".', this.searchValue) | ||
106 | |||
107 | this.search.emit(this.searchValue) | ||
108 | } | ||
109 | |||
110 | private setQueryParams (search: string) { | ||
111 | const queryParams: Params = {} | ||
112 | |||
113 | if (search) Object.assign(queryParams, { search }) | ||
114 | this.router.navigate([ ], { queryParams }) | ||
115 | } | ||
116 | } | ||
diff --git a/client/src/app/shared/shared-forms/index.ts b/client/src/app/shared/shared-forms/index.ts index 1d859b991..727416a40 100644 --- a/client/src/app/shared/shared-forms/index.ts +++ b/client/src/app/shared/shared-forms/index.ts | |||
@@ -1,12 +1,14 @@ | |||
1 | export * from './form-validator.service' | 1 | export * from './advanced-input-filter.component' |
2 | export * from './form-reactive' | 2 | export * from './form-reactive' |
3 | export * from './select' | 3 | export * from './form-validator.service' |
4 | export * from './input-toggle-hidden.component' | 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 './markdown-textarea.component' | 7 | export * from './markdown-textarea.component' |
7 | export * from './peertube-checkbox.component' | 8 | export * from './peertube-checkbox.component' |
8 | export * from './preview-upload.component' | 9 | export * from './preview-upload.component' |
9 | export * from './reactive-file.component' | 10 | export * from './reactive-file.component' |
11 | export * from './select' | ||
12 | export * from './shared-form.module' | ||
10 | export * from './textarea-autoresize.directive' | 13 | export * from './textarea-autoresize.directive' |
11 | export * from './timestamp-input.component' | 14 | export * from './timestamp-input.component' |
12 | export * from './shared-form.module' | ||
diff --git a/client/src/app/shared/shared-forms/input-switch.component.scss b/client/src/app/shared/shared-forms/input-switch.component.scss index c14950bd7..290a70db8 100644 --- a/client/src/app/shared/shared-forms/input-switch.component.scss +++ b/client/src/app/shared/shared-forms/input-switch.component.scss | |||
@@ -5,7 +5,7 @@ input { | |||
5 | position: absolute; | 5 | position: absolute; |
6 | visibility: hidden; | 6 | visibility: hidden; |
7 | 7 | ||
8 | & + label { | 8 | + label { |
9 | cursor: pointer; | 9 | cursor: pointer; |
10 | text-indent: -9999px; | 10 | text-indent: -9999px; |
11 | width: 35px; | 11 | width: 35px; |
@@ -16,7 +16,7 @@ input { | |||
16 | position: relative; | 16 | position: relative; |
17 | margin: 0; | 17 | margin: 0; |
18 | 18 | ||
19 | &:after { | 19 | &::after { |
20 | content: ''; | 20 | content: ''; |
21 | position: absolute; | 21 | position: absolute; |
22 | top: 3px; | 22 | top: 3px; |
@@ -28,7 +28,7 @@ input { | |||
28 | transition: 0.3s ease-out; | 28 | transition: 0.3s ease-out; |
29 | } | 29 | } |
30 | 30 | ||
31 | &:active:after { | 31 | &:active::after { |
32 | width: 40px; | 32 | width: 40px; |
33 | } | 33 | } |
34 | } | 34 | } |
@@ -36,7 +36,7 @@ input { | |||
36 | &:checked + label { | 36 | &:checked + label { |
37 | background: pvar(--mainColor); | 37 | background: pvar(--mainColor); |
38 | 38 | ||
39 | &:after { | 39 | &::after { |
40 | left: calc(100% - 3px); | 40 | left: calc(100% - 3px); |
41 | transform: translateX(-100%); | 41 | transform: translateX(-100%); |
42 | } | 42 | } |
diff --git a/client/src/app/shared/shared-forms/markdown-textarea.component.scss b/client/src/app/shared/shared-forms/markdown-textarea.component.scss index 8203c7d1c..1f72dbc32 100644 --- a/client/src/app/shared/shared-forms/markdown-textarea.component.scss +++ b/client/src/app/shared/shared-forms/markdown-textarea.component.scss | |||
@@ -18,7 +18,7 @@ $input-border-radius: 3px; | |||
18 | 18 | ||
19 | font-family: monospace; | 19 | font-family: monospace; |
20 | font-size: 13px; | 20 | font-size: 13px; |
21 | border-bottom: none; | 21 | border-bottom: 0; |
22 | border-bottom-left-radius: unset; | 22 | border-bottom-left-radius: unset; |
23 | border-bottom-right-radius: unset; | 23 | border-bottom-right-radius: unset; |
24 | } | 24 | } |
@@ -51,7 +51,8 @@ $input-border-radius: 3px; | |||
51 | opacity: 0.6; | 51 | opacity: 0.6; |
52 | } | 52 | } |
53 | 53 | ||
54 | &:hover, &:active { | 54 | &:hover, |
55 | &:active { | ||
55 | svg { | 56 | svg { |
56 | opacity: 1; | 57 | opacity: 1; |
57 | } | 58 | } |
@@ -105,6 +106,8 @@ $input-border-radius: 3px; | |||
105 | } | 106 | } |
106 | 107 | ||
107 | @mixin maximized-base { | 108 | @mixin maximized-base { |
109 | $nav-preview-vertical-padding: 40px; | ||
110 | |||
108 | flex-direction: row; | 111 | flex-direction: row; |
109 | z-index: #{z(header) - 1}; | 112 | z-index: #{z(header) - 1}; |
110 | position: fixed; | 113 | position: fixed; |
@@ -115,20 +118,18 @@ $input-border-radius: 3px; | |||
115 | width: calc(100% - #{$menu-width}); | 118 | width: calc(100% - #{$menu-width}); |
116 | height: calc(100vh - #{$header-height}) !important; | 119 | height: calc(100vh - #{$header-height}) !important; |
117 | 120 | ||
118 | $nav-preview-vertical-padding: 40px; | ||
119 | |||
120 | .nav-preview { | 121 | .nav-preview { |
121 | @include nav-preview-medium(); | 122 | @include nav-preview-medium(); |
122 | padding-top: #{$nav-preview-vertical-padding / 2}; | 123 | padding-top: #{$nav-preview-vertical-padding / 2}; |
123 | padding-bottom: #{$nav-preview-vertical-padding / 2}; | 124 | padding-bottom: #{$nav-preview-vertical-padding / 2}; |
124 | padding-left: 0px; | 125 | padding-left: 0; |
125 | padding-right: 0px; | 126 | padding-right: 0; |
126 | position: absolute; | 127 | position: absolute; |
127 | background-color: pvar(--mainBackgroundColor); | 128 | background-color: pvar(--mainBackgroundColor); |
128 | width: 100% !important; | 129 | width: 100% !important; |
129 | border-top: none; | 130 | border-top: 0; |
130 | border-left: none; | 131 | border-left: 0; |
131 | border-right: none; | 132 | border-right: 0; |
132 | 133 | ||
133 | :last-child { | 134 | :last-child { |
134 | margin-right: pvar(--horizontalMarginContent); | 135 | margin-right: pvar(--horizontalMarginContent); |
@@ -148,7 +149,7 @@ $input-border-radius: 3px; | |||
148 | margin-top: #{$nav-preview-tab-height + $nav-preview-vertical-padding} !important; | 149 | margin-top: #{$nav-preview-tab-height + $nav-preview-vertical-padding} !important; |
149 | height: calc(100vh - #{$header-height + $nav-preview-tab-height + $nav-preview-vertical-padding}) !important; | 150 | height: calc(100vh - #{$header-height + $nav-preview-tab-height + $nav-preview-vertical-padding}) !important; |
150 | width: 50% !important; | 151 | width: 50% !important; |
151 | border: none !important; | 152 | border: 0 !important; |
152 | border-radius: unset !important; | 153 | border-radius: unset !important; |
153 | } | 154 | } |
154 | 155 | ||
@@ -249,11 +250,11 @@ $input-border-radius: 3px; | |||
249 | } | 250 | } |
250 | 251 | ||
251 | @media only screen and (min-width: $small-view) { | 252 | @media only screen and (min-width: $small-view) { |
253 | @include maximized-in-medium-view(); | ||
254 | |||
252 | :host-context(.expanded) { | 255 | :host-context(.expanded) { |
253 | @include in-medium-view(); | 256 | @include in-medium-view(); |
254 | } | 257 | } |
255 | |||
256 | @include maximized-in-medium-view(); | ||
257 | } | 258 | } |
258 | 259 | ||
259 | @media only screen and (min-width: #{$small-view + $menu-width}) { | 260 | @media only screen and (min-width: #{$small-view + $menu-width}) { |
diff --git a/client/src/app/shared/shared-forms/peertube-checkbox.component.scss b/client/src/app/shared/shared-forms/peertube-checkbox.component.scss index cf8540dc3..203b82d0b 100644 --- a/client/src/app/shared/shared-forms/peertube-checkbox.component.scss +++ b/client/src/app/shared/shared-forms/peertube-checkbox.component.scss | |||
@@ -46,7 +46,7 @@ | |||
46 | line-height: 12px; | 46 | line-height: 12px; |
47 | font-weight: 500; | 47 | font-weight: 500; |
48 | color: pvar(--inputPlaceholderColor); | 48 | color: pvar(--inputPlaceholderColor); |
49 | background-color: rgba(217,225,232,.1); | 49 | background-color: rgba(217, 225, 232, .1); |
50 | border: 1px solid rgba(217,225,232,.5); | 50 | border: 1px solid rgba(217, 225, 232, .5); |
51 | } | 51 | } |
52 | } \ No newline at end of file | 52 | } |
diff --git a/client/src/app/shared/shared-forms/preview-upload.component.scss b/client/src/app/shared/shared-forms/preview-upload.component.scss index 88eccd5f7..c2ee0d6a9 100644 --- a/client/src/app/shared/shared-forms/preview-upload.component.scss +++ b/client/src/app/shared/shared-forms/preview-upload.component.scss | |||
@@ -21,7 +21,7 @@ | |||
21 | max-width: 100%; | 21 | max-width: 100%; |
22 | 22 | ||
23 | &.no-image { | 23 | &.no-image { |
24 | border: 2px solid grey; | 24 | border: 2px solid #808080; |
25 | background-color: pvar(--mainBackgroundColor); | 25 | background-color: pvar(--mainBackgroundColor); |
26 | } | 26 | } |
27 | } | 27 | } |
diff --git a/client/src/app/shared/shared-forms/select/select-shared.component.scss b/client/src/app/shared/shared-forms/select/select-shared.component.scss index 80196b8df..7006adab1 100644 --- a/client/src/app/shared/shared-forms/select/select-shared.component.scss +++ b/client/src/app/shared/shared-forms/select/select-shared.component.scss | |||
@@ -32,7 +32,7 @@ ng-select ::ng-deep { | |||
32 | } | 32 | } |
33 | 33 | ||
34 | .root { | 34 | .root { |
35 | display:flex; | 35 | display: flex; |
36 | align-items: center; | 36 | align-items: center; |
37 | 37 | ||
38 | > my-select-options { | 38 | > my-select-options { |
@@ -41,9 +41,9 @@ ng-select ::ng-deep { | |||
41 | } | 41 | } |
42 | 42 | ||
43 | my-select-options + input { | 43 | my-select-options + input { |
44 | margin-left: 5px; | ||
45 | |||
46 | @include peertube-input-text($form-base-input-width); | 44 | @include peertube-input-text($form-base-input-width); |
45 | |||
46 | margin-left: 5px; | ||
47 | display: block; | 47 | display: block; |
48 | } | 48 | } |
49 | 49 | ||
diff --git a/client/src/app/shared/shared-forms/shared-form.module.ts b/client/src/app/shared/shared-forms/shared-form.module.ts index 9bdd138a1..5417f7342 100644 --- a/client/src/app/shared/shared-forms/shared-form.module.ts +++ b/client/src/app/shared/shared-forms/shared-form.module.ts | |||
@@ -5,6 +5,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms' | |||
5 | import { NgSelectModule } from '@ng-select/ng-select' | 5 | import { NgSelectModule } from '@ng-select/ng-select' |
6 | import { SharedGlobalIconModule } from '../shared-icons' | 6 | import { SharedGlobalIconModule } from '../shared-icons' |
7 | import { SharedMainModule } from '../shared-main/shared-main.module' | 7 | import { SharedMainModule } from '../shared-main/shared-main.module' |
8 | import { AdvancedInputFilterComponent } from './advanced-input-filter.component' | ||
8 | import { DynamicFormFieldComponent } from './dynamic-form-field.component' | 9 | import { DynamicFormFieldComponent } from './dynamic-form-field.component' |
9 | import { FormValidatorService } from './form-validator.service' | 10 | import { FormValidatorService } from './form-validator.service' |
10 | import { InputSwitchComponent } from './input-switch.component' | 11 | import { InputSwitchComponent } from './input-switch.component' |
@@ -52,7 +53,9 @@ import { TimestampInputComponent } from './timestamp-input.component' | |||
52 | SelectCheckboxComponent, | 53 | SelectCheckboxComponent, |
53 | SelectCustomValueComponent, | 54 | SelectCustomValueComponent, |
54 | 55 | ||
55 | DynamicFormFieldComponent | 56 | DynamicFormFieldComponent, |
57 | |||
58 | AdvancedInputFilterComponent | ||
56 | ], | 59 | ], |
57 | 60 | ||
58 | exports: [ | 61 | exports: [ |
@@ -78,7 +81,9 @@ import { TimestampInputComponent } from './timestamp-input.component' | |||
78 | SelectCheckboxComponent, | 81 | SelectCheckboxComponent, |
79 | SelectCustomValueComponent, | 82 | SelectCustomValueComponent, |
80 | 83 | ||
81 | DynamicFormFieldComponent | 84 | DynamicFormFieldComponent, |
85 | |||
86 | AdvancedInputFilterComponent | ||
82 | ], | 87 | ], |
83 | 88 | ||
84 | providers: [ | 89 | providers: [ |
diff --git a/client/src/app/shared/shared-forms/timestamp-input.component.scss b/client/src/app/shared/shared-forms/timestamp-input.component.scss index 66e9aa032..36f5711a6 100644 --- a/client/src/app/shared/shared-forms/timestamp-input.component.scss +++ b/client/src/app/shared/shared-forms/timestamp-input.component.scss | |||
@@ -4,8 +4,7 @@ p-inputmask { | |||
4 | ::ng-deep input { | 4 | ::ng-deep input { |
5 | width: 80px; | 5 | width: 80px; |
6 | font-size: 15px; | 6 | font-size: 15px; |
7 | 7 | border: 0; | |
8 | border: none; | ||
9 | 8 | ||
10 | &:focus-within, | 9 | &:focus-within, |
11 | &:focus { | 10 | &:focus { |
diff --git a/client/src/app/shared/shared-instance/instance-about-accordion.component.scss b/client/src/app/shared/shared-instance/instance-about-accordion.component.scss index 2f6b420e3..615e08bcc 100644 --- a/client/src/app/shared/shared-instance/instance-about-accordion.component.scss +++ b/client/src/app/shared/shared-instance/instance-about-accordion.component.scss | |||
@@ -1,6 +1,6 @@ | |||
1 | @import '_variables'; | 1 | @import '_variables'; |
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | @import "./_bootstrap-variables"; | 3 | @import './_bootstrap-variables'; |
4 | 4 | ||
5 | @import '~bootstrap/scss/functions'; | 5 | @import '~bootstrap/scss/functions'; |
6 | @import '~bootstrap/scss/variables'; | 6 | @import '~bootstrap/scss/variables'; |
@@ -30,7 +30,7 @@ ngb-accordion ::ng-deep { | |||
30 | background-color: unset; | 30 | background-color: unset; |
31 | padding: 0; | 31 | padding: 0; |
32 | 32 | ||
33 | & + .collapse.show { | 33 | + .collapse.show { |
34 | background-color: var(--submenuBackgroundColor); | 34 | background-color: var(--submenuBackgroundColor); |
35 | } | 35 | } |
36 | } | 36 | } |
diff --git a/client/src/app/shared/shared-instance/instance-features-table.component.scss b/client/src/app/shared/shared-instance/instance-features-table.component.scss index d17e91fc2..11cf11616 100644 --- a/client/src/app/shared/shared-instance/instance-features-table.component.scss +++ b/client/src/app/shared/shared-instance/instance-features-table.component.scss | |||
@@ -19,7 +19,7 @@ table { | |||
19 | .more-info { | 19 | .more-info { |
20 | font-style: italic; | 20 | font-style: italic; |
21 | font-weight: initial; | 21 | font-weight: initial; |
22 | font-size: 14px | 22 | font-size: 14px; |
23 | } | 23 | } |
24 | } | 24 | } |
25 | 25 | ||
diff --git a/client/src/app/shared/shared-main/account/account.model.ts b/client/src/app/shared/shared-main/account/account.model.ts index 65e6798d4..6d9f0ee65 100644 --- a/client/src/app/shared/shared-main/account/account.model.ts +++ b/client/src/app/shared/shared-main/account/account.model.ts | |||
@@ -14,7 +14,7 @@ export class Account extends Actor implements ServerAccount { | |||
14 | userId?: number | 14 | userId?: number |
15 | 15 | ||
16 | static GET_ACTOR_AVATAR_URL (actor: { avatar?: { url?: string, path: string } }) { | 16 | static GET_ACTOR_AVATAR_URL (actor: { avatar?: { url?: string, path: string } }) { |
17 | return Actor.GET_ACTOR_AVATAR_URL(actor) || this.GET_DEFAULT_AVATAR_URL() | 17 | return Actor.GET_ACTOR_AVATAR_URL(actor) |
18 | } | 18 | } |
19 | 19 | ||
20 | static GET_DEFAULT_AVATAR_URL () { | 20 | static GET_DEFAULT_AVATAR_URL () { |
@@ -24,8 +24,6 @@ export class Account extends Actor implements ServerAccount { | |||
24 | constructor (hash: ServerAccount) { | 24 | constructor (hash: ServerAccount) { |
25 | super(hash) | 25 | super(hash) |
26 | 26 | ||
27 | this.updateComputedAttributes() | ||
28 | |||
29 | this.displayName = hash.displayName | 27 | this.displayName = hash.displayName |
30 | this.description = hash.description | 28 | this.description = hash.description |
31 | this.userId = hash.userId | 29 | this.userId = hash.userId |
@@ -40,16 +38,9 @@ export class Account extends Actor implements ServerAccount { | |||
40 | 38 | ||
41 | updateAvatar (newAvatar: ActorImage) { | 39 | updateAvatar (newAvatar: ActorImage) { |
42 | this.avatar = newAvatar | 40 | this.avatar = newAvatar |
43 | |||
44 | this.updateComputedAttributes() | ||
45 | } | 41 | } |
46 | 42 | ||
47 | resetAvatar () { | 43 | resetAvatar () { |
48 | this.avatar = null | 44 | this.avatar = null |
49 | this.avatarUrl = Account.GET_DEFAULT_AVATAR_URL() | ||
50 | } | ||
51 | |||
52 | private updateComputedAttributes () { | ||
53 | this.avatarUrl = Account.GET_ACTOR_AVATAR_URL(this) | ||
54 | } | 45 | } |
55 | } | 46 | } |
diff --git a/client/src/app/shared/shared-main/account/actor.model.ts b/client/src/app/shared/shared-main/account/actor.model.ts index 4b036341f..6ba0bb09e 100644 --- a/client/src/app/shared/shared-main/account/actor.model.ts +++ b/client/src/app/shared/shared-main/account/actor.model.ts | |||
@@ -15,7 +15,6 @@ export abstract class Actor implements ServerActor { | |||
15 | updatedAt: Date | string | 15 | updatedAt: Date | string |
16 | 16 | ||
17 | avatar: ActorImage | 17 | avatar: ActorImage |
18 | avatarUrl: string | ||
19 | 18 | ||
20 | isLocal: boolean | 19 | isLocal: boolean |
21 | 20 | ||
diff --git a/client/src/app/shared/shared-main/buttons/action-dropdown.component.scss b/client/src/app/shared/shared-main/buttons/action-dropdown.component.scss index 724a04efc..b9a4d46dc 100644 --- a/client/src/app/shared/shared-main/buttons/action-dropdown.component.scss +++ b/client/src/app/shared/shared-main/buttons/action-dropdown.component.scss | |||
@@ -8,6 +8,9 @@ | |||
8 | .action-button { | 8 | .action-button { |
9 | @include peertube-button; | 9 | @include peertube-button; |
10 | 10 | ||
11 | display: inline-block; | ||
12 | padding: 0 10px; | ||
13 | |||
11 | &.button-styled { | 14 | &.button-styled { |
12 | 15 | ||
13 | &.grey { | 16 | &.grey { |
@@ -18,14 +21,13 @@ | |||
18 | @include orange-button; | 21 | @include orange-button; |
19 | } | 22 | } |
20 | 23 | ||
21 | &:hover, &:active, &:focus { | 24 | &:hover, |
25 | &:active, | ||
26 | &:focus { | ||
22 | background-color: $grey-background-color; | 27 | background-color: $grey-background-color; |
23 | } | 28 | } |
24 | } | 29 | } |
25 | 30 | ||
26 | display: inline-block; | ||
27 | padding: 0 10px; | ||
28 | |||
29 | &::after { | 31 | &::after { |
30 | display: none; | 32 | display: none; |
31 | } | 33 | } |
@@ -64,7 +66,8 @@ | |||
64 | @include dropdown-with-icon-item; | 66 | @include dropdown-with-icon-item; |
65 | } | 67 | } |
66 | 68 | ||
67 | a, span { | 69 | a, |
70 | span { | ||
68 | display: block; | 71 | display: block; |
69 | width: 100%; | 72 | width: 100%; |
70 | } | 73 | } |
diff --git a/client/src/app/shared/shared-main/buttons/button.component.scss b/client/src/app/shared/shared-main/buttons/button.component.scss index f73b7b808..09b5f95d7 100644 --- a/client/src/app/shared/shared-main/buttons/button.component.scss +++ b/client/src/app/shared/shared-main/buttons/button.component.scss | |||
@@ -1,6 +1,16 @@ | |||
1 | @import '_variables'; | 1 | @import '_variables'; |
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | @mixin responsive-label { | ||
5 | .action-button { | ||
6 | padding: 0 13px; | ||
7 | } | ||
8 | |||
9 | .button-label { | ||
10 | display: none; | ||
11 | } | ||
12 | } | ||
13 | |||
4 | :host { | 14 | :host { |
5 | outline: none; | 15 | outline: none; |
6 | } | 16 | } |
@@ -46,12 +56,12 @@ span[class$=-button] { | |||
46 | // In a table, try to minimize the space taken by this button | 56 | // In a table, try to minimize the space taken by this button |
47 | @media screen and (max-width: 1400px) { | 57 | @media screen and (max-width: 1400px) { |
48 | :host-context(td) { | 58 | :host-context(td) { |
49 | .action-button { | 59 | @include responsive-label; |
50 | padding: 0 13px; | 60 | } |
51 | } | 61 | } |
52 | 62 | ||
53 | .button-label { | 63 | @media screen and (max-width: $small-view) { |
54 | display: none; | 64 | .responsive-label { |
55 | } | 65 | @include responsive-label; |
56 | } | 66 | } |
57 | } | 67 | } |
diff --git a/client/src/app/shared/shared-main/buttons/button.component.ts b/client/src/app/shared/shared-main/buttons/button.component.ts index 1d2be0bf9..ee74b3d12 100644 --- a/client/src/app/shared/shared-main/buttons/button.component.ts +++ b/client/src/app/shared/shared-main/buttons/button.component.ts | |||
@@ -3,7 +3,7 @@ import { GlobalIconName } from '@app/shared/shared-icons' | |||
3 | 3 | ||
4 | @Component({ | 4 | @Component({ |
5 | selector: 'my-button', | 5 | selector: 'my-button', |
6 | styleUrls: ['./button.component.scss'], | 6 | styleUrls: [ './button.component.scss' ], |
7 | templateUrl: './button.component.html' | 7 | templateUrl: './button.component.html' |
8 | }) | 8 | }) |
9 | 9 | ||
@@ -14,6 +14,7 @@ export class ButtonComponent { | |||
14 | @Input() title: string = undefined | 14 | @Input() title: string = undefined |
15 | @Input() loading = false | 15 | @Input() loading = false |
16 | @Input() disabled = false | 16 | @Input() disabled = false |
17 | @Input() responsiveLabel = false | ||
17 | 18 | ||
18 | getTitle () { | 19 | getTitle () { |
19 | return this.title || this.label | 20 | return this.title || this.label |
@@ -22,7 +23,8 @@ export class ButtonComponent { | |||
22 | getClasses () { | 23 | getClasses () { |
23 | return { | 24 | return { |
24 | [this.className]: true, | 25 | [this.className]: true, |
25 | disabled: this.disabled | 26 | disabled: this.disabled, |
27 | 'responsive-label': this.responsiveLabel | ||
26 | } | 28 | } |
27 | } | 29 | } |
28 | } | 30 | } |
diff --git a/client/src/app/shared/shared-main/buttons/delete-button.component.html b/client/src/app/shared/shared-main/buttons/delete-button.component.html index c94d8d0c9..d7a6702a7 100644 --- a/client/src/app/shared/shared-main/buttons/delete-button.component.html +++ b/client/src/app/shared/shared-main/buttons/delete-button.component.html | |||
@@ -1,4 +1,7 @@ | |||
1 | <span class="action-button action-button-delete grey-button" [ngbTooltip]="title" role="button" tabindex="0"> | 1 | <span |
2 | class="action-button action-button-delete grey-button" | ||
3 | [ngClass]="{ 'responsive-label': responsiveLabel }" [ngbTooltip]="title" role="button" tabindex="0" | ||
4 | > | ||
2 | <my-global-icon iconName="delete" aria-hidden="true"></my-global-icon> | 5 | <my-global-icon iconName="delete" aria-hidden="true"></my-global-icon> |
3 | 6 | ||
4 | <span class="button-label" *ngIf="label">{{ label }}</span> | 7 | <span class="button-label" *ngIf="label">{{ label }}</span> |
diff --git a/client/src/app/shared/shared-main/buttons/delete-button.component.ts b/client/src/app/shared/shared-main/buttons/delete-button.component.ts index 18995422a..c091f5309 100644 --- a/client/src/app/shared/shared-main/buttons/delete-button.component.ts +++ b/client/src/app/shared/shared-main/buttons/delete-button.component.ts | |||
@@ -9,6 +9,7 @@ import { Component, Input, OnInit } from '@angular/core' | |||
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 | 13 | ||
13 | ngOnInit () { | 14 | ngOnInit () { |
14 | // <my-delete-button /> No label | 15 | // <my-delete-button /> No label |
diff --git a/client/src/app/shared/shared-main/buttons/edit-button.component.html b/client/src/app/shared/shared-main/buttons/edit-button.component.html index ecb709be1..8beeee6c4 100644 --- a/client/src/app/shared/shared-main/buttons/edit-button.component.html +++ b/client/src/app/shared/shared-main/buttons/edit-button.component.html | |||
@@ -1,4 +1,7 @@ | |||
1 | <a class="action-button action-button-edit grey-button" [routerLink]="routerLink" [ngbTooltip]="title"> | 1 | <a |
2 | class="action-button action-button-edit grey-button" | ||
3 | [ngClass]="{ 'responsive-label': responsiveLabel }" [routerLink]="routerLink" [ngbTooltip]="title" | ||
4 | > | ||
2 | <my-global-icon iconName="edit" aria-hidden="true"></my-global-icon> | 5 | <my-global-icon iconName="edit" aria-hidden="true"></my-global-icon> |
3 | 6 | ||
4 | <span class="button-label" *ngIf="label">{{ label }}</span> | 7 | <span class="button-label" *ngIf="label">{{ label }}</span> |
diff --git a/client/src/app/shared/shared-main/buttons/edit-button.component.ts b/client/src/app/shared/shared-main/buttons/edit-button.component.ts index 4b76551ca..24c8625ff 100644 --- a/client/src/app/shared/shared-main/buttons/edit-button.component.ts +++ b/client/src/app/shared/shared-main/buttons/edit-button.component.ts | |||
@@ -5,11 +5,11 @@ import { Component, Input, OnInit } from '@angular/core' | |||
5 | styleUrls: [ './button.component.scss' ], | 5 | styleUrls: [ './button.component.scss' ], |
6 | templateUrl: './edit-button.component.html' | 6 | templateUrl: './edit-button.component.html' |
7 | }) | 7 | }) |
8 | |||
9 | export class EditButtonComponent implements OnInit { | 8 | export class EditButtonComponent implements OnInit { |
10 | @Input() label: string | 9 | @Input() label: string |
11 | @Input() title: string | 10 | @Input() title: string |
12 | @Input() routerLink: string[] | string = [] | 11 | @Input() routerLink: string[] | string = [] |
12 | @Input() responsiveLabel = false | ||
13 | 13 | ||
14 | ngOnInit () { | 14 | ngOnInit () { |
15 | // <my-edit-button /> No label | 15 | // <my-edit-button /> No label |
diff --git a/client/src/app/shared/shared-main/date/date-toggle.component.scss b/client/src/app/shared/shared-main/date/date-toggle.component.scss index 86700d1d4..b87f7c475 100644 --- a/client/src/app/shared/shared-main/date/date-toggle.component.scss +++ b/client/src/app/shared/shared-main/date/date-toggle.component.scss | |||
@@ -1,5 +1,5 @@ | |||
1 | .date-toggle { | 1 | .date-toggle { |
2 | &:hover { | 2 | &:hover { |
3 | cursor: default | 3 | cursor: default; |
4 | } | 4 | } |
5 | } | 5 | } |
diff --git a/client/src/app/shared/shared-main/feeds/feed.component.scss b/client/src/app/shared/shared-main/feeds/feed.component.scss index b655ee708..d39f31d70 100644 --- a/client/src/app/shared/shared-main/feeds/feed.component.scss +++ b/client/src/app/shared/shared-main/feeds/feed.component.scss | |||
@@ -5,14 +5,14 @@ | |||
5 | width: 100%; | 5 | width: 100%; |
6 | 6 | ||
7 | a { | 7 | a { |
8 | color: black; | 8 | color: #000; |
9 | display: block; | 9 | display: block; |
10 | } | 10 | } |
11 | } | 11 | } |
12 | 12 | ||
13 | my-global-icon { | 13 | my-global-icon { |
14 | @include apply-svg-color(pvar(--mainForegroundColor)); | ||
15 | |||
14 | cursor: pointer; | 16 | cursor: pointer; |
15 | width: 100%; | 17 | width: 100%; |
16 | |||
17 | @include apply-svg-color(pvar(--mainForegroundColor)) | ||
18 | } | 18 | } |
diff --git a/client/src/app/shared/shared-main/loaders/loader.component.scss b/client/src/app/shared/shared-main/loaders/loader.component.scss index ffac9c707..64138afe4 100644 --- a/client/src/app/shared/shared-main/loaders/loader.component.scss +++ b/client/src/app/shared/shared-main/loaders/loader.component.scss | |||
@@ -20,7 +20,7 @@ | |||
20 | border: 4px solid; | 20 | border: 4px solid; |
21 | border-radius: 50%; | 21 | border-radius: 50%; |
22 | animation: loader 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; | 22 | animation: loader 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; |
23 | border-color: #999999 transparent transparent transparent; | 23 | border-color: #999999 transparent transparent; |
24 | } | 24 | } |
25 | 25 | ||
26 | .loader div:nth-child(1) { | 26 | .loader div:nth-child(1) { |
diff --git a/client/src/app/shared/shared-main/misc/help.component.scss b/client/src/app/shared/shared-main/misc/help.component.scss index ccc91ffab..68d7ad48f 100644 --- a/client/src/app/shared/shared-main/misc/help.component.scss +++ b/client/src/app/shared/shared-main/misc/help.component.scss | |||
@@ -2,20 +2,19 @@ | |||
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | .help-tooltip-button { | 4 | .help-tooltip-button { |
5 | cursor: pointer; | 5 | @include disable-outline; |
6 | border: none; | ||
7 | 6 | ||
7 | cursor: pointer; | ||
8 | border: 0; | ||
8 | margin: 5px; | 9 | margin: 5px; |
9 | 10 | ||
10 | my-global-icon { | 11 | my-global-icon { |
12 | @include apply-svg-color(pvar(--greyForegroundColor)); | ||
13 | |||
11 | width: 17px; | 14 | width: 17px; |
12 | position: relative; | 15 | position: relative; |
13 | top: -1px; | 16 | top: -1px; |
14 | |||
15 | @include apply-svg-color(pvar(--greyForegroundColor)) | ||
16 | } | 17 | } |
17 | |||
18 | @include disable-outline; | ||
19 | } | 18 | } |
20 | 19 | ||
21 | ::ng-deep { | 20 | ::ng-deep { |
diff --git a/client/src/app/shared/shared-main/misc/list-overflow.component.html b/client/src/app/shared/shared-main/misc/list-overflow.component.html index 986572801..b2e0982f1 100644 --- a/client/src/app/shared/shared-main/misc/list-overflow.component.html +++ b/client/src/app/shared/shared-main/misc/list-overflow.component.html | |||
@@ -2,19 +2,19 @@ | |||
2 | <span [id]="getId(id)" #itemsRendered *ngFor="let item of items; index as id"> | 2 | <span [id]="getId(id)" #itemsRendered *ngFor="let item of items; index as id"> |
3 | <ng-container *ngTemplateOutlet="itemTemplate; context: {item: item}"></ng-container> | 3 | <ng-container *ngTemplateOutlet="itemTemplate; context: {item: item}"></ng-container> |
4 | </span> | 4 | </span> |
5 | 5 | ||
6 | <ng-container *ngIf="isMenuDisplayed()"> | 6 | <ng-container *ngIf="isMenuDisplayed()"> |
7 | <button *ngIf="isInMobileView" class="btn btn-outline-secondary btn-sm list-overflow-menu" (click)="toggleModal()"> | 7 | <button *ngIf="isInMobileView" class="btn btn-outline-secondary btn-sm list-overflow-menu" (click)="toggleModal()"> |
8 | <span class="glyphicon glyphicon-chevron-down"></span> | 8 | <span class="glyphicon glyphicon-chevron-down"></span> |
9 | </button> | 9 | </button> |
10 | 10 | ||
11 | <div *ngIf="!isInMobileView" class="list-overflow-menu" ngbDropdown container="body" #dropdown="ngbDropdown" (mouseleave)="closeDropdownIfHovered(dropdown)" (mouseenter)="openDropdownOnHover(dropdown)"> | 11 | <div *ngIf="!isInMobileView" class="list-overflow-menu" ngbDropdown container="body" #dropdown="ngbDropdown" (mouseleave)="closeDropdownIfHovered(dropdown)" (mouseenter)="openDropdownOnHover(dropdown)"> |
12 | <button class="btn btn-outline-secondary btn-sm" [ngClass]="{ routeActive: active }" | 12 | <button class="btn btn-outline-secondary btn-sm" [ngClass]="{ 'route-active': active }" |
13 | ngbDropdownAnchor (click)="dropdownAnchorClicked(dropdown)" role="button" | 13 | ngbDropdownAnchor (click)="dropdownAnchorClicked(dropdown)" role="button" |
14 | > | 14 | > |
15 | <span class="glyphicon glyphicon-chevron-down"></span> | 15 | <span class="glyphicon glyphicon-chevron-down"></span> |
16 | </button> | 16 | </button> |
17 | 17 | ||
18 | <div ngbDropdownMenu> | 18 | <div ngbDropdownMenu> |
19 | <a *ngFor="let item of items | slice:showItemsUntilIndexExcluded:items.length" | 19 | <a *ngFor="let item of items | slice:showItemsUntilIndexExcluded:items.length" |
20 | [routerLink]="item.routerLink" routerLinkActive="active" class="dropdown-item"> | 20 | [routerLink]="item.routerLink" routerLinkActive="active" class="dropdown-item"> |
diff --git a/client/src/app/shared/shared-main/misc/list-overflow.component.scss b/client/src/app/shared/shared-main/misc/list-overflow.component.scss index 1ec044489..7e31d3850 100644 --- a/client/src/app/shared/shared-main/misc/list-overflow.component.scss +++ b/client/src/app/shared/shared-main/misc/list-overflow.component.scss | |||
@@ -15,13 +15,13 @@ | |||
15 | 15 | ||
16 | button { | 16 | button { |
17 | width: 30px; | 17 | width: 30px; |
18 | border: none; | 18 | border: 0; |
19 | 19 | ||
20 | &::after { | 20 | &::after { |
21 | display: none; | 21 | display: none; |
22 | } | 22 | } |
23 | 23 | ||
24 | &.routeActive { | 24 | &.route-active { |
25 | &::after { | 25 | &::after { |
26 | display: inherit; | 26 | display: inherit; |
27 | border: 2px solid pvar(--mainColor); | 27 | border: 2px solid pvar(--mainColor); |
@@ -36,7 +36,7 @@ button { | |||
36 | margin-top: 0 !important; | 36 | margin-top: 0 !important; |
37 | position: static; | 37 | position: static; |
38 | right: auto; | 38 | right: auto; |
39 | bottom: auto | 39 | bottom: auto; |
40 | } | 40 | } |
41 | 41 | ||
42 | .modal-body { | 42 | .modal-body { |
diff --git a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss index 84dd7dce3..ffabb3646 100644 --- a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss +++ b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.scss | |||
@@ -11,12 +11,12 @@ | |||
11 | } | 11 | } |
12 | } | 12 | } |
13 | 13 | ||
14 | ::ng-deep .dropdown-toggle::after { | 14 | .sub-menu ::ng-deep .dropdown-toggle::after { |
15 | position: relative; | 15 | position: relative; |
16 | top: 2px; | 16 | top: 2px; |
17 | } | 17 | } |
18 | 18 | ||
19 | ::ng-deep .dropdown-menu { | 19 | .sub-menu ::ng-deep .dropdown-menu { |
20 | margin-top: 0 !important; | 20 | margin-top: 0 !important; |
21 | } | 21 | } |
22 | 22 | ||
diff --git a/client/src/app/shared/shared-main/users/user-notification.model.ts b/client/src/app/shared/shared-main/users/user-notification.model.ts index 88a4811da..ed5791794 100644 --- a/client/src/app/shared/shared-main/users/user-notification.model.ts +++ b/client/src/app/shared/shared-main/users/user-notification.model.ts | |||
@@ -258,10 +258,10 @@ export class UserNotification implements UserNotificationServer { | |||
258 | } | 258 | } |
259 | 259 | ||
260 | private setAccountAvatarUrl (actor: { avatarUrl?: string, avatar?: { url?: string, path: string } }) { | 260 | private setAccountAvatarUrl (actor: { avatarUrl?: string, avatar?: { url?: string, path: string } }) { |
261 | actor.avatarUrl = Account.GET_ACTOR_AVATAR_URL(actor) | 261 | actor.avatarUrl = Account.GET_ACTOR_AVATAR_URL(actor) || Account.GET_DEFAULT_AVATAR_URL() |
262 | } | 262 | } |
263 | 263 | ||
264 | private setVideoChannelAvatarUrl (actor: { avatarUrl?: string, avatar?: { url?: string, path: string } }) { | 264 | private setVideoChannelAvatarUrl (actor: { avatarUrl?: string, avatar?: { url?: string, path: string } }) { |
265 | actor.avatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(actor) | 265 | actor.avatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(actor) || VideoChannel.GET_DEFAULT_AVATAR_URL() |
266 | } | 266 | } |
267 | } | 267 | } |
diff --git a/client/src/app/shared/shared-main/users/user-notifications.component.scss b/client/src/app/shared/shared-main/users/user-notifications.component.scss index 5166bd559..b69d4b5d6 100644 --- a/client/src/app/shared/shared-main/users/user-notifications.component.scss +++ b/client/src/app/shared/shared-main/users/user-notifications.component.scss | |||
@@ -21,16 +21,19 @@ | |||
21 | } | 21 | } |
22 | 22 | ||
23 | my-global-icon { | 23 | my-global-icon { |
24 | @include apply-svg-color(#333); | ||
25 | |||
24 | width: 24px; | 26 | width: 24px; |
25 | margin-right: 11px; | 27 | margin-right: 11px; |
26 | margin-left: 3px; | 28 | margin-left: 3px; |
27 | |||
28 | @include apply-svg-color(#333); | ||
29 | } | 29 | } |
30 | 30 | ||
31 | .avatar { | 31 | .avatar { |
32 | @include avatar(30px); | 32 | width: 30px; |
33 | 33 | height: 30px; | |
34 | min-width: 30px; | ||
35 | min-height: 30px; | ||
36 | border-radius: 5px; | ||
34 | margin-right: 10px; | 37 | margin-right: 10px; |
35 | } | 38 | } |
36 | 39 | ||
diff --git a/client/src/app/shared/shared-main/users/user-quota.component.scss b/client/src/app/shared/shared-main/users/user-quota.component.scss index c670559d3..c06cafe29 100644 --- a/client/src/app/shared/shared-main/users/user-quota.component.scss +++ b/client/src/app/shared/shared-main/users/user-quota.component.scss | |||
@@ -11,7 +11,8 @@ label { | |||
11 | margin-right: 5px; | 11 | margin-right: 5px; |
12 | } | 12 | } |
13 | 13 | ||
14 | &, .progress { | 14 | &, |
15 | .progress { | ||
15 | width: 100% !important; | 16 | width: 100% !important; |
16 | } | 17 | } |
17 | 18 | ||
diff --git a/client/src/app/shared/shared-main/video-channel/video-channel.model.ts b/client/src/app/shared/shared-main/video-channel/video-channel.model.ts index 1ba3fcc0e..c40dd5311 100644 --- a/client/src/app/shared/shared-main/video-channel/video-channel.model.ts +++ b/client/src/app/shared/shared-main/video-channel/video-channel.model.ts | |||
@@ -18,14 +18,13 @@ export class VideoChannel extends Actor implements ServerVideoChannel { | |||
18 | 18 | ||
19 | ownerAccount?: ServerAccount | 19 | ownerAccount?: ServerAccount |
20 | ownerBy?: string | 20 | ownerBy?: string |
21 | ownerAvatarUrl?: string | ||
22 | 21 | ||
23 | videosCount?: number | 22 | videosCount?: number |
24 | 23 | ||
25 | viewsPerDay?: ViewsPerDate[] | 24 | viewsPerDay?: ViewsPerDate[] |
26 | 25 | ||
27 | static GET_ACTOR_AVATAR_URL (actor: object) { | 26 | static GET_ACTOR_AVATAR_URL (actor: object) { |
28 | return Actor.GET_ACTOR_AVATAR_URL(actor) || this.GET_DEFAULT_AVATAR_URL() | 27 | return Actor.GET_ACTOR_AVATAR_URL(actor) |
29 | } | 28 | } |
30 | 29 | ||
31 | static GET_ACTOR_BANNER_URL (channel: ServerVideoChannel) { | 30 | static GET_ACTOR_BANNER_URL (channel: ServerVideoChannel) { |
@@ -67,7 +66,6 @@ export class VideoChannel extends Actor implements ServerVideoChannel { | |||
67 | if (hash.ownerAccount) { | 66 | if (hash.ownerAccount) { |
68 | this.ownerAccount = hash.ownerAccount | 67 | this.ownerAccount = hash.ownerAccount |
69 | this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host) | 68 | this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host) |
70 | this.ownerAvatarUrl = Account.GET_ACTOR_AVATAR_URL(this.ownerAccount) | ||
71 | } | 69 | } |
72 | 70 | ||
73 | this.updateComputedAttributes() | 71 | this.updateComputedAttributes() |
@@ -94,7 +92,6 @@ export class VideoChannel extends Actor implements ServerVideoChannel { | |||
94 | } | 92 | } |
95 | 93 | ||
96 | updateComputedAttributes () { | 94 | updateComputedAttributes () { |
97 | this.avatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(this) | ||
98 | this.bannerUrl = VideoChannel.GET_ACTOR_BANNER_URL(this) | 95 | this.bannerUrl = VideoChannel.GET_ACTOR_BANNER_URL(this) |
99 | } | 96 | } |
100 | } | 97 | } |
diff --git a/client/src/app/shared/shared-main/video/video.model.ts b/client/src/app/shared/shared-main/video/video.model.ts index 14c507295..526d10e32 100644 --- a/client/src/app/shared/shared-main/video/video.model.ts +++ b/client/src/app/shared/shared-main/video/video.model.ts | |||
@@ -20,8 +20,6 @@ export class Video implements VideoServerModel { | |||
20 | byVideoChannel: string | 20 | byVideoChannel: string |
21 | byAccount: string | 21 | byAccount: string |
22 | 22 | ||
23 | videoChannelAvatarUrl: string | ||
24 | |||
25 | createdAt: Date | 23 | createdAt: Date |
26 | updatedAt: Date | 24 | updatedAt: Date |
27 | publishedAt: Date | 25 | publishedAt: Date |
@@ -143,7 +141,6 @@ export class Video implements VideoServerModel { | |||
143 | 141 | ||
144 | this.byAccount = Actor.CREATE_BY_STRING(hash.account.name, hash.account.host) | 142 | this.byAccount = Actor.CREATE_BY_STRING(hash.account.name, hash.account.host) |
145 | this.byVideoChannel = Actor.CREATE_BY_STRING(hash.channel.name, hash.channel.host) | 143 | this.byVideoChannel = Actor.CREATE_BY_STRING(hash.channel.name, hash.channel.host) |
146 | this.videoChannelAvatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(this.channel) | ||
147 | 144 | ||
148 | this.category.label = peertubeTranslate(this.category.label, translations) | 145 | this.category.label = peertubeTranslate(this.category.label, translations) |
149 | this.licence.label = peertubeTranslate(this.licence.label, translations) | 146 | this.licence.label = peertubeTranslate(this.licence.label, translations) |
diff --git a/client/src/app/shared/shared-main/video/video.service.ts b/client/src/app/shared/shared-main/video/video.service.ts index 0b708b692..7b17bd2ab 100644 --- a/client/src/app/shared/shared-main/video/video.service.ts +++ b/client/src/app/shared/shared-main/video/video.service.ts | |||
@@ -124,7 +124,17 @@ export class VideoService implements VideosProvider { | |||
124 | 124 | ||
125 | let params = new HttpParams() | 125 | let params = new HttpParams() |
126 | params = this.restService.addRestGetParams(params, pagination, sort) | 126 | params = this.restService.addRestGetParams(params, pagination, sort) |
127 | params = this.restService.addObjectParams(params, { search }) | 127 | |
128 | if (search) { | ||
129 | const filters = this.restService.parseQueryStringFilter(search, { | ||
130 | isLive: { | ||
131 | prefix: 'isLive:', | ||
132 | isBoolean: true | ||
133 | } | ||
134 | }) | ||
135 | |||
136 | params = this.restService.addObjectParams(params, filters) | ||
137 | } | ||
128 | 138 | ||
129 | return this.authHttp | 139 | return this.authHttp |
130 | .get<ResultList<Video>>(UserService.BASE_USERS_URL + 'me/videos', { params }) | 140 | .get<ResultList<Video>>(UserService.BASE_USERS_URL + 'me/videos', { params }) |
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.html b/client/src/app/shared/shared-moderation/account-blocklist.component.html index 3f2f55559..a9fac0810 100644 --- a/client/src/app/shared/shared-moderation/account-blocklist.component.html +++ b/client/src/app/shared/shared-moderation/account-blocklist.component.html | |||
@@ -11,13 +11,8 @@ | |||
11 | > | 11 | > |
12 | <ng-template pTemplate="caption"> | 12 | <ng-template pTemplate="caption"> |
13 | <div class="caption"> | 13 | <div class="caption"> |
14 | <div class="ml-auto has-feedback has-clear"> | 14 | <div class="ml-auto"> |
15 | <input | 15 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
16 | type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..." | ||
17 | (keyup)="onSearch($event)" | ||
18 | > | ||
19 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
20 | <span class="sr-only" i18n>Clear filters</span> | ||
21 | </div> | 16 | </div> |
22 | </div> | 17 | </div> |
23 | </ng-template> | 18 | </ng-template> |
@@ -38,7 +33,7 @@ | |||
38 | <td> | 33 | <td> |
39 | <a [href]="accountBlock.blockedAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> | 34 | <a [href]="accountBlock.blockedAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer"> |
40 | <div class="chip two-lines"> | 35 | <div class="chip two-lines"> |
41 | <my-account-avatar [account]="accountBlock.blockedAccount"></my-account-avatar> | 36 | <my-actor-avatar [account]="accountBlock.blockedAccount"></my-actor-avatar> |
42 | <div> | 37 | <div> |
43 | {{ accountBlock.blockedAccount.displayName }} | 38 | {{ accountBlock.blockedAccount.displayName }} |
44 | <span class="text-muted">{{ accountBlock.blockedAccount.nameWithHost }}</span> | 39 | <span class="text-muted">{{ accountBlock.blockedAccount.nameWithHost }}</span> |
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.scss b/client/src/app/shared/shared-moderation/account-blocklist.component.scss index 3eede44eb..bc441811e 100644 --- a/client/src/app/shared/shared-moderation/account-blocklist.component.scss +++ b/client/src/app/shared/shared-moderation/account-blocklist.component.scss | |||
@@ -1,15 +1,6 @@ | |||
1 | @import '_variables'; | 1 | @import '_variables'; |
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | .caption { | ||
5 | justify-content: flex-end; | ||
6 | |||
7 | input { | ||
8 | @include peertube-input-text(250px); | ||
9 | flex-grow: 1; | ||
10 | } | ||
11 | } | ||
12 | |||
13 | .chip { | 4 | .chip { |
14 | @include chip; | 5 | @include chip; |
15 | } | 6 | } |
@@ -17,4 +8,4 @@ | |||
17 | .unblock-button { | 8 | .unblock-button { |
18 | @include peertube-button; | 9 | @include peertube-button; |
19 | @include grey-button; | 10 | @include grey-button; |
20 | } \ No newline at end of file | 11 | } |
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.ts b/client/src/app/shared/shared-moderation/account-blocklist.component.ts index 1bce65bf0..1146aeec0 100644 --- a/client/src/app/shared/shared-moderation/account-blocklist.component.ts +++ b/client/src/app/shared/shared-moderation/account-blocklist.component.ts | |||
@@ -44,12 +44,12 @@ export class GenericAccountBlocklistComponent extends RestTable implements OnIni | |||
44 | : $localize`Account ${blockedAccount.nameWithHost} unmuted by your instance.` | 44 | : $localize`Account ${blockedAccount.nameWithHost} unmuted by your instance.` |
45 | ) | 45 | ) |
46 | 46 | ||
47 | this.loadData() | 47 | this.reloadData() |
48 | } | 48 | } |
49 | ) | 49 | ) |
50 | } | 50 | } |
51 | 51 | ||
52 | protected loadData () { | 52 | protected reloadData () { |
53 | const operation = this.mode === BlocklistComponentType.Account | 53 | const operation = this.mode === BlocklistComponentType.Account |
54 | ? this.blocklistService.getUserAccountBlocklist({ | 54 | ? this.blocklistService.getUserAccountBlocklist({ |
55 | pagination: this.pagination, | 55 | pagination: this.pagination, |
diff --git a/client/src/app/shared/shared-moderation/moderation.scss b/client/src/app/shared/shared-moderation/moderation.scss index cdcc12fe0..b13d06f03 100644 --- a/client/src/app/shared/shared-moderation/moderation.scss +++ b/client/src/app/shared/shared-moderation/moderation.scss | |||
@@ -17,33 +17,25 @@ | |||
17 | word-wrap: break-word; | 17 | word-wrap: break-word; |
18 | 18 | ||
19 | ::ng-deep p:last-child { | 19 | ::ng-deep p:last-child { |
20 | margin-bottom: 0px !important; | 20 | margin-bottom: 0 !important; |
21 | } | 21 | } |
22 | } | 22 | } |
23 | } | 23 | } |
24 | 24 | ||
25 | .screenratio { | 25 | .screenratio { |
26 | div { | ||
27 | @include miniature-thumbnail; | ||
28 | |||
29 | display: inline-flex; | ||
30 | justify-content: center; | ||
31 | align-items: center; | ||
32 | color: pvar(--inputPlaceholderColor); | ||
33 | } | ||
34 | |||
35 | @include block-ratio($selector: 'div, ::ng-deep iframe') { | 26 | @include block-ratio($selector: 'div, ::ng-deep iframe') { |
36 | width: 100% !important; | 27 | width: 100% !important; |
37 | height: 100% !important; | 28 | height: 100% !important; |
38 | left: 0; | 29 | left: 0; |
39 | }; | 30 | }; |
40 | } | ||
41 | 31 | ||
42 | .input-group { | 32 | div { |
43 | @include peertube-input-group(300px); | 33 | @include miniature-thumbnail; |
44 | 34 | ||
45 | .dropdown-toggle::after { | 35 | display: inline-flex; |
46 | margin-left: 0; | 36 | justify-content: center; |
37 | align-items: center; | ||
38 | color: pvar(--inputPlaceholderColor); | ||
47 | } | 39 | } |
48 | } | 40 | } |
49 | 41 | ||
@@ -51,15 +43,6 @@ | |||
51 | @include chip; | 43 | @include chip; |
52 | } | 44 | } |
53 | 45 | ||
54 | .caption { | ||
55 | justify-content: flex-end; | ||
56 | |||
57 | input { | ||
58 | @include peertube-input-text(250px); | ||
59 | flex-grow: 1; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | my-action-dropdown.show { | 46 | my-action-dropdown.show { |
64 | ::ng-deep .dropdown-root { | 47 | ::ng-deep .dropdown-root { |
65 | display: block !important; | 48 | display: block !important; |
@@ -93,15 +76,15 @@ my-action-dropdown.show { | |||
93 | display: inline-flex; | 76 | display: inline-flex; |
94 | 77 | ||
95 | .table-video-image { | 78 | .table-video-image { |
96 | @include miniature-thumbnail; | ||
97 | |||
98 | $image-height: 45px; | 79 | $image-height: 45px; |
99 | 80 | ||
81 | @include miniature-thumbnail; | ||
82 | |||
100 | height: $image-height; | 83 | height: $image-height; |
101 | width: #{(16/9) * $image-height}; | 84 | width: #{(16/9) * $image-height}; |
102 | margin-right: 0.5rem; | 85 | margin-right: 0.5rem; |
103 | border-radius: 2px; | 86 | border-radius: 2px; |
104 | border: none; | 87 | border: 0; |
105 | background: transparent; | 88 | background: transparent; |
106 | display: inline-flex; | 89 | display: inline-flex; |
107 | justify-content: center; | 90 | justify-content: center; |
@@ -139,7 +122,7 @@ my-action-dropdown.show { | |||
139 | 122 | ||
140 | div .glyphicon { | 123 | div .glyphicon { |
141 | font-size: 80%; | 124 | font-size: 80%; |
142 | color: gray; | 125 | color: #808080; |
143 | margin-left: 0.1rem; | 126 | margin-left: 0.1rem; |
144 | } | 127 | } |
145 | 128 | ||
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.html b/client/src/app/shared/shared-moderation/server-blocklist.component.html index 537186f05..c6d29bb21 100644 --- a/client/src/app/shared/shared-moderation/server-blocklist.component.html +++ b/client/src/app/shared/shared-moderation/server-blocklist.component.html | |||
@@ -4,8 +4,9 @@ | |||
4 | </h1> | 4 | </h1> |
5 | 5 | ||
6 | <p-table | 6 | <p-table |
7 | [value]="blockedServers" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" | 7 | [value]="blockedServers" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" |
8 | [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)" | 8 | [sortField]="sort.field" [sortOrder]="sort.order" (onPage)="onPage($event)" |
9 | [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" | ||
9 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate | 10 | [showCurrentPageReport]="true" i18n-currentPageReportTemplate |
10 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted instances" | 11 | currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted instances" |
11 | > | 12 | > |
@@ -18,13 +19,8 @@ | |||
18 | </a> | 19 | </a> |
19 | </div> | 20 | </div> |
20 | 21 | ||
21 | <div class="ml-auto has-feedback has-clear"> | 22 | <div class="ml-auto"> |
22 | <input | 23 | <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> |
23 | type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..." | ||
24 | (keyup)="onSearch($event)" | ||
25 | > | ||
26 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | ||
27 | <span class="sr-only" i18n>Clear filters</span> | ||
28 | </div> | 24 | </div> |
29 | </div> | 25 | </div> |
30 | </ng-template> | 26 | </ng-template> |
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.scss b/client/src/app/shared/shared-moderation/server-blocklist.component.scss index 31db4d92b..a22972c5f 100644 --- a/client/src/app/shared/shared-moderation/server-blocklist.component.scss +++ b/client/src/app/shared/shared-moderation/server-blocklist.component.scss | |||
@@ -5,7 +5,8 @@ a { | |||
5 | @include disable-default-a-behaviour; | 5 | @include disable-default-a-behaviour; |
6 | display: inline-block; | 6 | display: inline-block; |
7 | 7 | ||
8 | &, &:hover { | 8 | &, |
9 | &:hover { | ||
9 | color: pvar(--mainForegroundColor); | 10 | color: pvar(--mainForegroundColor); |
10 | } | 11 | } |
11 | 12 | ||
@@ -15,15 +16,6 @@ a { | |||
15 | } | 16 | } |
16 | } | 17 | } |
17 | 18 | ||
18 | .caption { | ||
19 | justify-content: flex-end; | ||
20 | |||
21 | input { | ||
22 | @include peertube-input-text(250px); | ||
23 | flex-grow: 1; | ||
24 | } | ||
25 | } | ||
26 | |||
27 | .unblock-button { | 19 | .unblock-button { |
28 | @include peertube-button; | 20 | @include peertube-button; |
29 | @include grey-button; | 21 | @include grey-button; |
@@ -33,15 +25,6 @@ a { | |||
33 | @include create-button; | 25 | @include create-button; |
34 | } | 26 | } |
35 | 27 | ||
36 | .caption { | ||
37 | justify-content: flex-end; | ||
38 | |||
39 | input { | ||
40 | @include peertube-input-text(250px); | ||
41 | flex-grow: 1; | ||
42 | } | ||
43 | } | ||
44 | |||
45 | .chip { | 28 | .chip { |
46 | @include chip; | 29 | @include chip; |
47 | } | 30 | } |
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.ts b/client/src/app/shared/shared-moderation/server-blocklist.component.ts index 546fd53c3..274d8f6e9 100644 --- a/client/src/app/shared/shared-moderation/server-blocklist.component.ts +++ b/client/src/app/shared/shared-moderation/server-blocklist.component.ts | |||
@@ -46,7 +46,7 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit | |||
46 | : $localize`Instance ${host} unmuted by your instance.` | 46 | : $localize`Instance ${host} unmuted by your instance.` |
47 | ) | 47 | ) |
48 | 48 | ||
49 | this.loadData() | 49 | this.reloadData() |
50 | } | 50 | } |
51 | ) | 51 | ) |
52 | } | 52 | } |
@@ -69,13 +69,13 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit | |||
69 | : $localize`Instance ${domain} muted by your instance.` | 69 | : $localize`Instance ${domain} muted by your instance.` |
70 | ) | 70 | ) |
71 | 71 | ||
72 | this.loadData() | 72 | this.reloadData() |
73 | } | 73 | } |
74 | ) | 74 | ) |
75 | }) | 75 | }) |
76 | } | 76 | } |
77 | 77 | ||
78 | protected loadData () { | 78 | protected reloadData () { |
79 | const operation = this.mode === BlocklistComponentType.Account | 79 | const operation = this.mode === BlocklistComponentType.Account |
80 | ? this.blocklistService.getUserServerBlocklist({ | 80 | ? this.blocklistService.getUserServerBlocklist({ |
81 | pagination: this.pagination, | 81 | pagination: this.pagination, |
diff --git a/client/src/app/shared/shared-moderation/shared-moderation.module.ts b/client/src/app/shared/shared-moderation/shared-moderation.module.ts index c7e201792..95213e2bd 100644 --- a/client/src/app/shared/shared-moderation/shared-moderation.module.ts +++ b/client/src/app/shared/shared-moderation/shared-moderation.module.ts | |||
@@ -13,7 +13,7 @@ import { UserBanModalComponent } from './user-ban-modal.component' | |||
13 | import { UserModerationDropdownComponent } from './user-moderation-dropdown.component' | 13 | import { UserModerationDropdownComponent } from './user-moderation-dropdown.component' |
14 | import { VideoBlockComponent } from './video-block.component' | 14 | import { VideoBlockComponent } from './video-block.component' |
15 | import { VideoBlockService } from './video-block.service' | 15 | import { VideoBlockService } from './video-block.service' |
16 | import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-account-avatar.module' | 16 | import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module' |
17 | 17 | ||
18 | @NgModule({ | 18 | @NgModule({ |
19 | imports: [ | 19 | imports: [ |
@@ -21,7 +21,7 @@ import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-accou | |||
21 | SharedFormModule, | 21 | SharedFormModule, |
22 | SharedGlobalIconModule, | 22 | SharedGlobalIconModule, |
23 | SharedVideoCommentModule, | 23 | SharedVideoCommentModule, |
24 | SharedAccountAvatarModule | 24 | SharedActorImageModule |
25 | ], | 25 | ], |
26 | 26 | ||
27 | declarations: [ | 27 | declarations: [ |
diff --git a/client/src/app/shared/shared-moderation/video-block.component.scss b/client/src/app/shared/shared-moderation/video-block.component.scss index afa0d96f7..a6e33070b 100644 --- a/client/src/app/shared/shared-moderation/video-block.component.scss +++ b/client/src/app/shared/shared-moderation/video-block.component.scss | |||
@@ -7,5 +7,5 @@ textarea { | |||
7 | 7 | ||
8 | .live-info { | 8 | .live-info { |
9 | font-size: 15px; | 9 | font-size: 15px; |
10 | margin: 40px 0 20px 0; | 10 | margin: 40px 0 20px; |
11 | } | 11 | } |
diff --git a/client/src/app/shared/shared-search/advanced-search.model.ts b/client/src/app/shared/shared-search/advanced-search.model.ts index 30badc8fa..0e3924841 100644 --- a/client/src/app/shared/shared-search/advanced-search.model.ts +++ b/client/src/app/shared/shared-search/advanced-search.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { NSFWQuery, SearchTargetType } from '@shared/models' | 1 | import { BooleanBothQuery, SearchTargetType } from '@shared/models' |
2 | 2 | ||
3 | export class AdvancedSearch { | 3 | export class AdvancedSearch { |
4 | startDate: string // ISO 8601 | 4 | startDate: string // ISO 8601 |
@@ -7,7 +7,7 @@ export class AdvancedSearch { | |||
7 | originallyPublishedStartDate: string // ISO 8601 | 7 | originallyPublishedStartDate: string // ISO 8601 |
8 | originallyPublishedEndDate: string // ISO 8601 | 8 | originallyPublishedEndDate: string // ISO 8601 |
9 | 9 | ||
10 | nsfw: NSFWQuery | 10 | nsfw: BooleanBothQuery |
11 | 11 | ||
12 | categoryOneOf: string | 12 | categoryOneOf: string |
13 | 13 | ||
@@ -33,7 +33,7 @@ export class AdvancedSearch { | |||
33 | endDate?: string | 33 | endDate?: string |
34 | originallyPublishedStartDate?: string | 34 | originallyPublishedStartDate?: string |
35 | originallyPublishedEndDate?: string | 35 | originallyPublishedEndDate?: string |
36 | nsfw?: NSFWQuery | 36 | nsfw?: BooleanBothQuery |
37 | categoryOneOf?: string | 37 | categoryOneOf?: string |
38 | licenceOneOf?: string | 38 | licenceOneOf?: string |
39 | languageOneOf?: string | 39 | languageOneOf?: string |
diff --git a/client/src/app/shared/shared-thumbnail/video-thumbnail.component.scss b/client/src/app/shared/shared-thumbnail/video-thumbnail.component.scss index ea59ab346..e678d6edf 100644 --- a/client/src/app/shared/shared-thumbnail/video-thumbnail.component.scss +++ b/client/src/app/shared/shared-thumbnail/video-thumbnail.component.scss | |||
@@ -11,7 +11,7 @@ | |||
11 | width: 100%; | 11 | width: 100%; |
12 | position: absolute; | 12 | position: absolute; |
13 | bottom: 0; | 13 | bottom: 0; |
14 | background-color: rgba(0, 0, 0, 0.20); | 14 | background-color: rgba(0, 0, 0, 0.2); |
15 | 15 | ||
16 | div { | 16 | div { |
17 | height: 100%; | 17 | height: 100%; |
@@ -39,8 +39,8 @@ | |||
39 | top: 5px; | 39 | top: 5px; |
40 | font-weight: $font-bold; | 40 | font-weight: $font-bold; |
41 | 41 | ||
42 | &.warning { background-color: orange; } | 42 | &.warning { background-color: #ffa500; } |
43 | &.danger { background-color: red; } | 43 | &.danger { background-color: #ff0000; } |
44 | } | 44 | } |
45 | 45 | ||
46 | .video-thumbnail-duration-overlay, | 46 | .video-thumbnail-duration-overlay, |
@@ -77,9 +77,9 @@ | |||
77 | padding: 3px; | 77 | padding: 3px; |
78 | 78 | ||
79 | my-global-icon { | 79 | my-global-icon { |
80 | @include apply-svg-color(#fff); | ||
81 | |||
80 | width: 22px; | 82 | width: 22px; |
81 | height: 22px; | 83 | height: 22px; |
82 | |||
83 | @include apply-svg-color(#fff); | ||
84 | } | 84 | } |
85 | } | 85 | } |
diff --git a/client/src/app/shared/shared-user-settings/user-video-settings.component.html b/client/src/app/shared/shared-user-settings/user-video-settings.component.html index aa261fdce..a49e11485 100644 --- a/client/src/app/shared/shared-user-settings/user-video-settings.component.html +++ b/client/src/app/shared/shared-user-settings/user-video-settings.component.html | |||
@@ -5,7 +5,7 @@ | |||
5 | <my-help> | 5 | <my-help> |
6 | <ng-template ptTemplate="customHtml"> | 6 | <ng-template ptTemplate="customHtml"> |
7 | <ng-container i18n> | 7 | <ng-container i18n> |
8 | With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video. | 8 | With <strong>Hide</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video. |
9 | </ng-container> | 9 | </ng-container> |
10 | </ng-template> | 10 | </ng-template> |
11 | </my-help> | 11 | </my-help> |
@@ -13,7 +13,7 @@ | |||
13 | <div class="peertube-select-container"> | 13 | <div class="peertube-select-container"> |
14 | <select id="nsfwPolicy" formControlName="nsfwPolicy" class="form-control"> | 14 | <select id="nsfwPolicy" formControlName="nsfwPolicy" class="form-control"> |
15 | <option i18n value="undefined" disabled>Policy for sensitive videos</option> | 15 | <option i18n value="undefined" disabled>Policy for sensitive videos</option> |
16 | <option i18n value="do_not_list">Do not list</option> | 16 | <option i18n value="do_not_list">Hide</option> |
17 | <option i18n value="blur">Blur thumbnails</option> | 17 | <option i18n value="blur">Blur thumbnails</option> |
18 | <option i18n value="display">Display</option> | 18 | <option i18n value="display">Display</option> |
19 | </select> | 19 | </select> |
diff --git a/client/src/app/shared/shared-user-settings/user-video-settings.component.ts b/client/src/app/shared/shared-user-settings/user-video-settings.component.ts index d74c2b2d8..ae95030c7 100644 --- a/client/src/app/shared/shared-user-settings/user-video-settings.component.ts +++ b/client/src/app/shared/shared-user-settings/user-video-settings.component.ts | |||
@@ -38,8 +38,6 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit, | |||
38 | ngOnInit () { | 38 | ngOnInit () { |
39 | this.allLanguagesGroup = $localize`All languages` | 39 | this.allLanguagesGroup = $localize`All languages` |
40 | 40 | ||
41 | let oldForm: any | ||
42 | |||
43 | this.buildForm({ | 41 | this.buildForm({ |
44 | nsfwPolicy: null, | 42 | nsfwPolicy: null, |
45 | webTorrentEnabled: null, | 43 | webTorrentEnabled: null, |
@@ -73,16 +71,7 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit, | |||
73 | videoLanguages | 71 | videoLanguages |
74 | }) | 72 | }) |
75 | 73 | ||
76 | if (this.reactiveUpdate) { | 74 | if (this.reactiveUpdate) this.handleReactiveUpdate() |
77 | oldForm = { ...this.form.value } | ||
78 | |||
79 | this.formValuesWatcher = this.form.valueChanges.subscribe((formValue: any) => { | ||
80 | const updatedKey = Object.keys(formValue).find(k => formValue[k] !== oldForm[k]) | ||
81 | oldForm = { ...this.form.value } | ||
82 | |||
83 | this.updateDetails([ updatedKey ]) | ||
84 | }) | ||
85 | } | ||
86 | }) | 75 | }) |
87 | } | 76 | } |
88 | 77 | ||
@@ -96,7 +85,7 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit, | |||
96 | const autoPlayVideo = this.form.value['autoPlayVideo'] | 85 | const autoPlayVideo = this.form.value['autoPlayVideo'] |
97 | const autoPlayNextVideo = this.form.value['autoPlayNextVideo'] | 86 | const autoPlayNextVideo = this.form.value['autoPlayNextVideo'] |
98 | 87 | ||
99 | const videoLanguagesForm = this.form.value['videoLanguages'] | 88 | let videoLanguagesForm = this.form.value['videoLanguages'] |
100 | 89 | ||
101 | if (Array.isArray(videoLanguagesForm)) { | 90 | if (Array.isArray(videoLanguagesForm)) { |
102 | if (videoLanguagesForm.length > 20) { | 91 | if (videoLanguagesForm.length > 20) { |
@@ -104,13 +93,14 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit, | |||
104 | return | 93 | return |
105 | } | 94 | } |
106 | 95 | ||
96 | // Automatically use "All languages" if the user did not select any language | ||
107 | if (videoLanguagesForm.length === 0) { | 97 | if (videoLanguagesForm.length === 0) { |
108 | this.notifier.error($localize`You need to enable at least 1 video language.`) | 98 | videoLanguagesForm = [ this.allLanguagesGroup ] |
109 | return | 99 | this.form.patchValue({ videoLanguages: [ { group: this.allLanguagesGroup } ] }) |
110 | } | 100 | } |
111 | } | 101 | } |
112 | 102 | ||
113 | const videoLanguages = this.getVideoLanguages(videoLanguagesForm) | 103 | const videoLanguages = this.buildLanguagesFromForm(videoLanguagesForm) |
114 | 104 | ||
115 | let details: UserUpdateMe = { | 105 | let details: UserUpdateMe = { |
116 | nsfwPolicy, | 106 | nsfwPolicy, |
@@ -127,22 +117,13 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit, | |||
127 | if (onlyKeys) details = pick(details, onlyKeys) | 117 | if (onlyKeys) details = pick(details, onlyKeys) |
128 | 118 | ||
129 | if (this.authService.isLoggedIn()) { | 119 | if (this.authService.isLoggedIn()) { |
130 | this.userService.updateMyProfile(details).subscribe( | 120 | return this.updateLoggedProfile(details) |
131 | () => { | ||
132 | this.authService.refreshUserInformation() | ||
133 | |||
134 | if (this.notifyOnUpdate) this.notifier.success($localize`Video settings updated.`) | ||
135 | }, | ||
136 | |||
137 | err => this.notifier.error(err.message) | ||
138 | ) | ||
139 | } else { | ||
140 | this.userService.updateMyAnonymousProfile(details) | ||
141 | if (this.notifyOnUpdate) this.notifier.success($localize`Display/Video settings updated.`) | ||
142 | } | 121 | } |
122 | |||
123 | return this.updateAnonymousProfile(details) | ||
143 | } | 124 | } |
144 | 125 | ||
145 | private getVideoLanguages (videoLanguages: ItemSelectCheckboxValue[]) { | 126 | private buildLanguagesFromForm (videoLanguages: ItemSelectCheckboxValue[]) { |
146 | if (!Array.isArray(videoLanguages)) return undefined | 127 | if (!Array.isArray(videoLanguages)) return undefined |
147 | 128 | ||
148 | // null means "All" | 129 | // null means "All" |
@@ -166,4 +147,34 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit, | |||
166 | return l.id + '' | 147 | return l.id + '' |
167 | }) | 148 | }) |
168 | } | 149 | } |
150 | |||
151 | private handleReactiveUpdate () { | ||
152 | let oldForm = { ...this.form.value } | ||
153 | |||
154 | this.formValuesWatcher = this.form.valueChanges.subscribe((formValue: any) => { | ||
155 | const updatedKey = Object.keys(formValue) | ||
156 | .find(k => formValue[k] !== oldForm[k]) | ||
157 | |||
158 | oldForm = { ...this.form.value } | ||
159 | |||
160 | this.updateDetails([ updatedKey ]) | ||
161 | }) | ||
162 | } | ||
163 | |||
164 | private updateLoggedProfile (details: UserUpdateMe) { | ||
165 | this.userService.updateMyProfile(details).subscribe( | ||
166 | () => { | ||
167 | this.authService.refreshUserInformation() | ||
168 | |||
169 | if (this.notifyOnUpdate) this.notifier.success($localize`Video settings updated.`) | ||
170 | }, | ||
171 | |||
172 | err => this.notifier.error(err.message) | ||
173 | ) | ||
174 | } | ||
175 | |||
176 | private updateAnonymousProfile (details: UserUpdateMe) { | ||
177 | this.userService.updateMyAnonymousProfile(details) | ||
178 | if (this.notifyOnUpdate) this.notifier.success($localize`Display/Video settings updated.`) | ||
179 | } | ||
169 | } | 180 | } |
diff --git a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.scss b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.scss index 698c5866a..73db0d090 100644 --- a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.scss +++ b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.scss | |||
@@ -3,4 +3,4 @@ | |||
3 | .btn-remote-follow { | 3 | .btn-remote-follow { |
4 | @include peertube-button; | 4 | @include peertube-button; |
5 | @include orange-button; | 5 | @include orange-button; |
6 | } \ No newline at end of file | 6 | } |
diff --git a/client/src/app/shared/shared-user-subscription/subscribe-button.component.scss b/client/src/app/shared/shared-user-subscription/subscribe-button.component.scss index f6cdc11c0..897ee7799 100644 --- a/client/src/app/shared/shared-user-subscription/subscribe-button.component.scss +++ b/client/src/app/shared/shared-user-subscription/subscribe-button.component.scss | |||
@@ -8,8 +8,8 @@ | |||
8 | float: right; | 8 | float: right; |
9 | padding: 0; | 9 | padding: 0; |
10 | 10 | ||
11 | & > .btn, | 11 | > .btn, |
12 | & > .dropdown > .dropdown-toggle { | 12 | > .dropdown > .dropdown-toggle { |
13 | font-size: 15px; | 13 | font-size: 15px; |
14 | } | 14 | } |
15 | 15 | ||
@@ -20,7 +20,7 @@ | |||
20 | &.big { | 20 | &.big { |
21 | height: 35px; | 21 | height: 35px; |
22 | 22 | ||
23 | & > button:first-child { | 23 | > button:first-child { |
24 | width: max-content; | 24 | width: max-content; |
25 | min-width: 175px; | 25 | min-width: 175px; |
26 | } | 26 | } |
@@ -29,7 +29,7 @@ | |||
29 | span:first-child { | 29 | span:first-child { |
30 | line-height: 80%; | 30 | line-height: 80%; |
31 | } | 31 | } |
32 | 32 | ||
33 | span:not(:first-child) { | 33 | span:not(:first-child) { |
34 | font-size: 75%; | 34 | font-size: 75%; |
35 | } | 35 | } |
@@ -37,15 +37,15 @@ | |||
37 | } | 37 | } |
38 | 38 | ||
39 | // Unlogged | 39 | // Unlogged |
40 | & > .dropdown > .dropdown-toggle span { | 40 | > .dropdown > .dropdown-toggle span { |
41 | padding-right: 3px; | 41 | padding-right: 3px; |
42 | } | 42 | } |
43 | 43 | ||
44 | // Logged | 44 | // Logged |
45 | & > .btn { | 45 | > .btn { |
46 | padding-right: 4px; | 46 | padding-right: 4px; |
47 | 47 | ||
48 | & + .dropdown > button { | 48 | + .dropdown > button { |
49 | padding-left: 2px; | 49 | padding-left: 2px; |
50 | 50 | ||
51 | &::after { | 51 | &::after { |
diff --git a/client/src/app/shared/shared-video-comment/video-comment.service.ts b/client/src/app/shared/shared-video-comment/video-comment.service.ts index 0f09778df..c5aeb3c12 100644 --- a/client/src/app/shared/shared-video-comment/video-comment.service.ts +++ b/client/src/app/shared/shared-video-comment/video-comment.service.ts | |||
@@ -190,13 +190,7 @@ export class VideoCommentService { | |||
190 | const filters = this.restService.parseQueryStringFilter(search, { | 190 | const filters = this.restService.parseQueryStringFilter(search, { |
191 | isLocal: { | 191 | isLocal: { |
192 | prefix: 'local:', | 192 | prefix: 'local:', |
193 | isBoolean: true, | 193 | isBoolean: true |
194 | handler: v => { | ||
195 | if (v === 'true') return v | ||
196 | if (v === 'false') return v | ||
197 | |||
198 | return undefined | ||
199 | } | ||
200 | }, | 194 | }, |
201 | 195 | ||
202 | searchAccount: { prefix: 'account:' }, | 196 | searchAccount: { prefix: 'account:' }, |
diff --git a/client/src/app/shared/shared-video-miniature/abstract-video-list.scss b/client/src/app/shared/shared-video-miniature/abstract-video-list.scss index 467ca1d2c..d9cf7a14f 100644 --- a/client/src/app/shared/shared-video-miniature/abstract-video-list.scss +++ b/client/src/app/shared/shared-video-miniature/abstract-video-list.scss | |||
@@ -3,7 +3,7 @@ | |||
3 | @import '_mixins'; | 3 | @import '_mixins'; |
4 | @import '_miniature'; | 4 | @import '_miniature'; |
5 | 5 | ||
6 | $iconSize: 16px; | 6 | $icon-size: 16px; |
7 | 7 | ||
8 | ::ng-deep my-video-list-header { | 8 | ::ng-deep my-video-list-header { |
9 | display: flex; | 9 | display: flex; |
@@ -17,20 +17,19 @@ $iconSize: 16px; | |||
17 | 17 | ||
18 | my-feed { | 18 | my-feed { |
19 | display: inline-block; | 19 | display: inline-block; |
20 | width: calc(#{$iconSize} - 2px); | 20 | width: calc(#{$icon-size} - 2px); |
21 | } | 21 | } |
22 | 22 | ||
23 | .moderation-block { | 23 | .moderation-block { |
24 | |||
25 | my-global-icon { | ||
26 | position: relative; | ||
27 | width: $iconSize; | ||
28 | } | ||
29 | |||
30 | margin-left: .4rem; | 24 | margin-left: .4rem; |
31 | display: flex; | 25 | display: flex; |
32 | justify-content: flex-end; | 26 | justify-content: flex-end; |
33 | align-items: center; | 27 | align-items: center; |
28 | |||
29 | my-global-icon { | ||
30 | position: relative; | ||
31 | width: $icon-size; | ||
32 | } | ||
34 | } | 33 | } |
35 | } | 34 | } |
36 | 35 | ||
@@ -72,7 +71,7 @@ $iconSize: 16px; | |||
72 | 71 | ||
73 | .title-page { | 72 | .title-page { |
74 | margin-bottom: 10px; | 73 | margin-bottom: 10px; |
75 | margin-right: 0px; | 74 | margin-right: 0; |
76 | } | 75 | } |
77 | } | 76 | } |
78 | } | 77 | } |
diff --git a/client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts b/client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts index 32cfdfd68..03be6d2ff 100644 --- a/client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts +++ b/client/src/app/shared/shared-video-miniature/shared-video-miniature.module.ts | |||
@@ -13,7 +13,7 @@ import { VideoDownloadComponent } from './video-download.component' | |||
13 | import { VideoMiniatureComponent } from './video-miniature.component' | 13 | import { VideoMiniatureComponent } from './video-miniature.component' |
14 | import { VideosSelectionComponent } from './videos-selection.component' | 14 | import { VideosSelectionComponent } from './videos-selection.component' |
15 | import { VideoListHeaderComponent } from './video-list-header.component' | 15 | import { VideoListHeaderComponent } from './video-list-header.component' |
16 | import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-account-avatar.module' | 16 | import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module' |
17 | 17 | ||
18 | @NgModule({ | 18 | @NgModule({ |
19 | imports: [ | 19 | imports: [ |
@@ -25,7 +25,7 @@ import { SharedAccountAvatarModule } from '../shared-account-avatar/shared-accou | |||
25 | SharedGlobalIconModule, | 25 | SharedGlobalIconModule, |
26 | SharedVideoLiveModule, | 26 | SharedVideoLiveModule, |
27 | SharedVideoModule, | 27 | SharedVideoModule, |
28 | SharedAccountAvatarModule | 28 | SharedActorImageModule |
29 | ], | 29 | ], |
30 | 30 | ||
31 | declarations: [ | 31 | declarations: [ |
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.scss b/client/src/app/shared/shared-video-miniature/video-download.component.scss index 7f6e03c87..b689b1046 100644 --- a/client/src/app/shared/shared-video-miniature/video-download.component.scss +++ b/client/src/app/shared/shared-video-miniature/video-download.component.scss | |||
@@ -28,7 +28,7 @@ | |||
28 | 28 | ||
29 | border-top-right-radius: 0; | 29 | border-top-right-radius: 0; |
30 | border-bottom-right-radius: 0; | 30 | border-bottom-right-radius: 0; |
31 | border-right: none; | 31 | border-right: 0; |
32 | 32 | ||
33 | select { | 33 | select { |
34 | height: inherit; | 34 | height: inherit; |
@@ -85,7 +85,7 @@ | |||
85 | &.metadata-attribute-tags { | 85 | &.metadata-attribute-tags { |
86 | .metadata-attribute-value:not(:nth-child(2)) { | 86 | .metadata-attribute-value:not(:nth-child(2)) { |
87 | &::before { | 87 | &::before { |
88 | content: ', ' | 88 | content: ', '; |
89 | } | 89 | } |
90 | } | 90 | } |
91 | } | 91 | } |
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.html b/client/src/app/shared/shared-video-miniature/video-miniature.component.html index bc19127aa..645be92bd 100644 --- a/client/src/app/shared/shared-video-miniature/video-miniature.component.html +++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.html | |||
@@ -10,14 +10,15 @@ | |||
10 | <div class="video-bottom"> | 10 | <div class="video-bottom"> |
11 | <div class="video-miniature-information"> | 11 | <div class="video-miniature-information"> |
12 | <div class="d-flex video-miniature-meta"> | 12 | <div class="d-flex video-miniature-meta"> |
13 | <a *ngIf="displayOptions.avatar && displayOwnerVideoChannel()" class="channel-avatar" [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle"> | 13 | <my-actor-avatar |
14 | <img [src]="getAvatarUrl()" alt="" /> | 14 | *ngIf="displayOptions.avatar && displayOwnerVideoChannel()" [title]="channelLinkTitle" |
15 | </a> | 15 | [channel]="video.channel" [size]="actorImageSize" [internalHref]="[ '/video-channels', video.byVideoChannel ]" |
16 | ></my-actor-avatar> | ||
16 | 17 | ||
17 | <my-account-avatar | 18 | <my-actor-avatar |
18 | *ngIf="displayOptions.avatar && displayOwnerAccount()" [title]="channelLinkTitle" | 19 | *ngIf="displayOptions.avatar && displayOwnerAccount()" [title]="channelLinkTitle" |
19 | [account]="video.account" size="40" [internalHref]="'/video-channels/' + video.byVideoChannel" | 20 | [account]="video.account" [size]="actorImageSize" [internalHref]="[ '/video-channels', video.byVideoChannel ]" |
20 | ></my-account-avatar> | 21 | ></my-actor-avatar> |
21 | 22 | ||
22 | <div class="w-100 d-flex flex-column"> | 23 | <div class="w-100 d-flex flex-column"> |
23 | <a *ngIf="!videoHref" tabindex="-1" class="video-miniature-name" | 24 | <a *ngIf="!videoHref" tabindex="-1" class="video-miniature-name" |
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.scss b/client/src/app/shared/shared-video-miniature/video-miniature.component.scss index f6f2925f0..5df89d019 100644 --- a/client/src/app/shared/shared-video-miniature/video-miniature.component.scss +++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.scss | |||
@@ -12,15 +12,10 @@ $more-button-width: 40px; | |||
12 | width: calc(100% - #{$more-button-width}); | 12 | width: calc(100% - #{$more-button-width}); |
13 | } | 13 | } |
14 | 14 | ||
15 | my-account-avatar, | 15 | my-actor-avatar { |
16 | .channel-avatar { | ||
17 | margin: 10px 10px 0 0; | 16 | margin: 10px 10px 0 0; |
18 | } | 17 | } |
19 | 18 | ||
20 | .channel-avatar img{ | ||
21 | @include channel-avatar(40px); | ||
22 | } | ||
23 | |||
24 | .video-miniature-created-at-views { | 19 | .video-miniature-created-at-views { |
25 | font-size: 13px; | 20 | font-size: 13px; |
26 | } | 21 | } |
@@ -46,7 +41,7 @@ my-account-avatar, | |||
46 | } | 41 | } |
47 | 42 | ||
48 | .video-info-blocked { | 43 | .video-info-blocked { |
49 | color: red; | 44 | color: #ff0000; |
50 | 45 | ||
51 | .blocked-reason::before { | 46 | .blocked-reason::before { |
52 | content: ' - '; | 47 | content: ' - '; |
@@ -54,7 +49,7 @@ my-account-avatar, | |||
54 | } | 49 | } |
55 | 50 | ||
56 | .video-info-nsfw { | 51 | .video-info-nsfw { |
57 | color: red; | 52 | color: #ff0000; |
58 | } | 53 | } |
59 | 54 | ||
60 | .video-actions { | 55 | .video-actions { |
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts index 8d66aaee2..b58c118be 100644 --- a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts | |||
@@ -12,6 +12,7 @@ import { | |||
12 | } from '@angular/core' | 12 | } from '@angular/core' |
13 | import { AuthService, ScreenService, ServerService, User } from '@app/core' | 13 | import { AuthService, ScreenService, ServerService, User } from '@app/core' |
14 | import { ServerConfig, VideoPlaylistType, VideoPrivacy, VideoState } from '@shared/models' | 14 | import { ServerConfig, VideoPlaylistType, VideoPrivacy, VideoState } from '@shared/models' |
15 | import { ActorAvatarSize } from '../shared-actor-image/actor-avatar.component' | ||
15 | import { Video } from '../shared-main' | 16 | import { Video } from '../shared-main' |
16 | import { VideoPlaylistService } from '../shared-video-playlist' | 17 | import { VideoPlaylistService } from '../shared-video-playlist' |
17 | import { VideoActionsDisplayType } from './video-actions-dropdown.component' | 18 | import { VideoActionsDisplayType } from './video-actions-dropdown.component' |
@@ -51,6 +52,8 @@ export class VideoMiniatureComponent implements OnInit { | |||
51 | } | 52 | } |
52 | @Input() displayVideoActions = true | 53 | @Input() displayVideoActions = true |
53 | 54 | ||
55 | @Input() actorImageSize: ActorAvatarSize = '40' | ||
56 | |||
54 | @Input() displayAsRow = false | 57 | @Input() displayAsRow = false |
55 | 58 | ||
56 | @Input() videoLinkType: VideoLinkType = 'internal' | 59 | @Input() videoLinkType: VideoLinkType = 'internal' |
@@ -180,14 +183,6 @@ export class VideoMiniatureComponent implements OnInit { | |||
180 | return '' | 183 | return '' |
181 | } | 184 | } |
182 | 185 | ||
183 | getAvatarUrl () { | ||
184 | if (this.displayOwnerAccount()) { | ||
185 | return this.video.account.avatar?.url | ||
186 | } | ||
187 | |||
188 | return this.video.videoChannelAvatarUrl | ||
189 | } | ||
190 | |||
191 | loadActions () { | 186 | loadActions () { |
192 | if (this.displayVideoActions) this.showActions = true | 187 | if (this.displayVideoActions) this.showActions = true |
193 | 188 | ||
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.html b/client/src/app/shared/shared-video-miniature/videos-selection.component.html index dec9e99f3..4ee90ce7f 100644 --- a/client/src/app/shared/shared-video-miniature/videos-selection.component.html +++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.html | |||
@@ -1,9 +1,9 @@ | |||
1 | <div class="no-results" i18n *ngIf="hasDoneFirstQuery && videos.length === 0">No results.</div> | 1 | <div class="no-results" i18n *ngIf="hasDoneFirstQuery && videos.length === 0">{{ noResultMessage }}</div> |
2 | 2 | ||
3 | <div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()" class="videos"> | 3 | <div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()" class="videos"> |
4 | <div class="video" *ngFor="let video of videos; let i = index; trackBy: videoById"> | 4 | <div class="video" *ngFor="let video of videos; let i = index; trackBy: videoById"> |
5 | 5 | ||
6 | <div class="checkbox-container"> | 6 | <div class="checkbox-container" *ngIf="enableSelection"> |
7 | <my-peertube-checkbox [inputName]="'video-check-' + video.id" [(ngModel)]="_selection[video.id]"></my-peertube-checkbox> | 7 | <my-peertube-checkbox [inputName]="'video-check-' + video.id" [(ngModel)]="_selection[video.id]"></my-peertube-checkbox> |
8 | </div> | 8 | </div> |
9 | 9 | ||
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts index f8c3800d7..d64ee9b98 100644 --- a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts +++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts | |||
@@ -31,6 +31,9 @@ export class VideosSelectionComponent extends AbstractVideoList implements OnIni | |||
31 | @Input() pagination: ComponentPagination | 31 | @Input() pagination: ComponentPagination |
32 | @Input() titlePage: string | 32 | @Input() titlePage: string |
33 | @Input() miniatureDisplayOptions: MiniatureDisplayOptions | 33 | @Input() miniatureDisplayOptions: MiniatureDisplayOptions |
34 | @Input() noResultMessage = $localize`No results.` | ||
35 | @Input() enableSelection = true | ||
36 | @Input() loadOnInit = true | ||
34 | 37 | ||
35 | @Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<ResultList<Video>> | 38 | @Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<ResultList<Video>> |
36 | 39 | ||
diff --git a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss index b84cacece..cb1168196 100644 --- a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss +++ b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.scss | |||
@@ -126,7 +126,7 @@ $timestamp-margin-right: 10px; | |||
126 | border-top: 1px solid $separator-border-color; | 126 | border-top: 1px solid $separator-border-color; |
127 | } | 127 | } |
128 | 128 | ||
129 | .new-playlist-button { | 129 | .new-playlist-button { |
130 | cursor: pointer; | 130 | cursor: pointer; |
131 | 131 | ||
132 | my-global-icon { | 132 | my-global-icon { |
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss index 572f7d7a8..9ccd03912 100644 --- a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss +++ b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.scss | |||
@@ -84,21 +84,23 @@ my-video-thumbnail, | |||
84 | width: auto; | 84 | width: auto; |
85 | } | 85 | } |
86 | 86 | ||
87 | .video-info-account, .video-info-timestamp { | 87 | .video-info-account, |
88 | .video-info-timestamp { | ||
88 | color: pvar(--greyForegroundColor); | 89 | color: pvar(--greyForegroundColor); |
89 | } | 90 | } |
90 | } | 91 | } |
91 | } | 92 | } |
92 | 93 | ||
93 | .video-info-name { | 94 | .video-info-name { |
95 | @include ellipsis; | ||
96 | |||
94 | font-size: 18px; | 97 | font-size: 18px; |
95 | font-weight: $font-semibold; | 98 | font-weight: $font-semibold; |
96 | display: inline-block; | 99 | display: inline-block; |
97 | |||
98 | @include ellipsis; | ||
99 | } | 100 | } |
100 | 101 | ||
101 | .more, my-edit-button { | 102 | .more, |
103 | my-edit-button { | ||
102 | justify-self: flex-end; | 104 | justify-self: flex-end; |
103 | margin-left: auto; | 105 | margin-left: auto; |
104 | cursor: pointer; | 106 | cursor: pointer; |
@@ -118,7 +120,7 @@ my-video-thumbnail, | |||
118 | display: flex; | 120 | display: flex; |
119 | 121 | ||
120 | &::after { | 122 | &::after { |
121 | border: none; | 123 | border: 0; |
122 | } | 124 | } |
123 | } | 125 | } |
124 | } | 126 | } |
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss index 99089166c..a46a6e475 100644 --- a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss +++ b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss | |||
@@ -6,7 +6,7 @@ | |||
6 | display: inline-block; | 6 | display: inline-block; |
7 | width: 100%; | 7 | width: 100%; |
8 | 8 | ||
9 | &.no-videos:not(.to-manage){ | 9 | &.no-videos:not(.to-manage) { |
10 | a { | 10 | a { |
11 | cursor: default !important; | 11 | cursor: default !important; |
12 | } | 12 | } |
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist.model.ts b/client/src/app/shared/shared-video-playlist/video-playlist.model.ts index 9bec16d77..5b6ba9dbf 100644 --- a/client/src/app/shared/shared-video-playlist/video-playlist.model.ts +++ b/client/src/app/shared/shared-video-playlist/video-playlist.model.ts | |||
@@ -37,10 +37,8 @@ export class VideoPlaylist implements ServerVideoPlaylist { | |||
37 | embedUrl: string | 37 | embedUrl: string |
38 | 38 | ||
39 | ownerBy: string | 39 | ownerBy: string |
40 | ownerAvatarUrl: string | ||
41 | 40 | ||
42 | videoChannelBy?: string | 41 | videoChannelBy?: string |
43 | videoChannelAvatarUrl?: string | ||
44 | 42 | ||
45 | private thumbnailVersion: number | 43 | private thumbnailVersion: number |
46 | private originThumbnailUrl: string | 44 | private originThumbnailUrl: string |
@@ -78,12 +76,10 @@ export class VideoPlaylist implements ServerVideoPlaylist { | |||
78 | 76 | ||
79 | this.ownerAccount = hash.ownerAccount | 77 | this.ownerAccount = hash.ownerAccount |
80 | this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host) | 78 | this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host) |
81 | this.ownerAvatarUrl = Account.GET_ACTOR_AVATAR_URL(this.ownerAccount) | ||
82 | 79 | ||
83 | if (hash.videoChannel) { | 80 | if (hash.videoChannel) { |
84 | this.videoChannel = hash.videoChannel | 81 | this.videoChannel = hash.videoChannel |
85 | this.videoChannelBy = Actor.CREATE_BY_STRING(hash.videoChannel.name, hash.videoChannel.host) | 82 | this.videoChannelBy = Actor.CREATE_BY_STRING(hash.videoChannel.name, hash.videoChannel.host) |
86 | this.videoChannelAvatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(this.videoChannel) | ||
87 | } | 83 | } |
88 | 84 | ||
89 | this.privacy.label = peertubeTranslate(this.privacy.label, translations) | 85 | this.privacy.label = peertubeTranslate(this.privacy.label, translations) |