diff options
Diffstat (limited to 'client/src/app')
21 files changed, 213 insertions, 52 deletions
diff --git a/client/src/app/+accounts/accounts.component.html b/client/src/app/+accounts/accounts.component.html index 66d108134..1283105e9 100644 --- a/client/src/app/+accounts/accounts.component.html +++ b/client/src/app/+accounts/accounts.component.html | |||
@@ -25,11 +25,11 @@ | |||
25 | 25 | ||
26 | <div class="actor-handle"> | 26 | <div class="actor-handle"> |
27 | <span>@{{ account.nameWithHost }}</span> | 27 | <span>@{{ account.nameWithHost }}</span> |
28 | <button [cdkCopyToClipboard]="account.nameWithHostForced" (click)="activateCopiedMessage()" | 28 | |
29 | class="btn btn-outline-secondary btn-sm copy-button" title="Copy account handle" i18n-title | 29 | <my-copy-button |
30 | > | 30 | [value]="account.nameWithHostForced" i18n-notification notification="Username copied" |
31 | <my-global-icon iconName="copy"></my-global-icon> | 31 | title="Copy account handle" i18n-title |
32 | </button> | 32 | ></my-copy-button> |
33 | </div> | 33 | </div> |
34 | 34 | ||
35 | <div class="actor-counters"> | 35 | <div class="actor-counters"> |
diff --git a/client/src/app/+accounts/accounts.component.scss b/client/src/app/+accounts/accounts.component.scss index 56b952b65..aadd6f5c0 100644 --- a/client/src/app/+accounts/accounts.component.scss +++ b/client/src/app/+accounts/accounts.component.scss | |||
@@ -28,14 +28,8 @@ | |||
28 | } | 28 | } |
29 | } | 29 | } |
30 | 30 | ||
31 | .copy-button { | 31 | my-copy-button { |
32 | @include margin-left(3px); | 32 | @include margin-left(3px); |
33 | |||
34 | border: 0; | ||
35 | |||
36 | my-global-icon { | ||
37 | width: 15px; | ||
38 | } | ||
39 | } | 33 | } |
40 | 34 | ||
41 | .account-info { | 35 | .account-info { |
diff --git a/client/src/app/+accounts/accounts.component.ts b/client/src/app/+accounts/accounts.component.ts index 0033fbf59..6d912e325 100644 --- a/client/src/app/+accounts/accounts.component.ts +++ b/client/src/app/+accounts/accounts.component.ts | |||
@@ -115,10 +115,6 @@ export class AccountsComponent implements OnInit, OnDestroy { | |||
115 | this.redirectService.redirectToHomepage() | 115 | this.redirectService.redirectToHomepage() |
116 | } | 116 | } |
117 | 117 | ||
118 | activateCopiedMessage () { | ||
119 | this.notifier.success($localize`Username copied`) | ||
120 | } | ||
121 | |||
122 | searchChanged (search: string) { | 118 | searchChanged (search: string) { |
123 | const queryParams = { search } | 119 | const queryParams = { search } |
124 | 120 | ||
diff --git a/client/src/app/+admin/system/runners/runner-job-list/runner-job-list.component.html b/client/src/app/+admin/system/runners/runner-job-list/runner-job-list.component.html index a759e9186..1a40da0c0 100644 --- a/client/src/app/+admin/system/runners/runner-job-list/runner-job-list.component.html +++ b/client/src/app/+admin/system/runners/runner-job-list/runner-job-list.component.html | |||
@@ -31,7 +31,7 @@ | |||
31 | <th style="width: 100px" i18n pSortableColumn="priority">Priority <p-sortIcon field="priority"></p-sortIcon></th> | 31 | <th style="width: 100px" i18n pSortableColumn="priority">Priority <p-sortIcon field="priority"></p-sortIcon></th> |
32 | <th style="width: 100px" i18n pSortableColumn="progress">Progress <p-sortIcon field="progress"></p-sortIcon></th> | 32 | <th style="width: 100px" i18n pSortableColumn="progress">Progress <p-sortIcon field="progress"></p-sortIcon></th> |
33 | <th i18n>Runner</th> | 33 | <th i18n>Runner</th> |
34 | <th style="width: 150px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> | 34 | <th style="width: 200px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> |
35 | </tr> | 35 | </tr> |
36 | </ng-template> | 36 | </ng-template> |
37 | 37 | ||
@@ -47,7 +47,7 @@ | |||
47 | </div> | 47 | </div> |
48 | 48 | ||
49 | <div class="ms-auto d-flex"> | 49 | <div class="ms-auto d-flex"> |
50 | <my-advanced-input-filter class="me-2" (search)="onSearch($event)"></my-advanced-input-filter> | 50 | <my-advanced-input-filter class="me-2" [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter> |
51 | 51 | ||
52 | <my-button i18n-label label="Refresh" icon="refresh" (click)="reloadData()"></my-button> | 52 | <my-button i18n-label label="Refresh" icon="refresh" (click)="reloadData()"></my-button> |
53 | </div> | 53 | </div> |
@@ -73,7 +73,9 @@ | |||
73 | 73 | ||
74 | <td>{{ runnerJob.uuid }}</td> | 74 | <td>{{ runnerJob.uuid }}</td> |
75 | <td>{{ runnerJob.type }}</td> | 75 | <td>{{ runnerJob.type }}</td> |
76 | <td>{{ runnerJob.state.label }}</td> | 76 | <td> |
77 | <span class="pt-badge" [ngClass]="getStateBadgeColor(runnerJob)">{{ runnerJob.state.label }}</span> | ||
78 | </td> | ||
77 | <td>{{ runnerJob.priority }}</td> | 79 | <td>{{ runnerJob.priority }}</td> |
78 | 80 | ||
79 | <td> | 81 | <td> |
diff --git a/client/src/app/+admin/system/runners/runner-job-list/runner-job-list.component.ts b/client/src/app/+admin/system/runners/runner-job-list/runner-job-list.component.ts index 8994c1d00..2670eac86 100644 --- a/client/src/app/+admin/system/runners/runner-job-list/runner-job-list.component.ts +++ b/client/src/app/+admin/system/runners/runner-job-list/runner-job-list.component.ts | |||
@@ -5,6 +5,7 @@ import { formatICU } from '@app/helpers' | |||
5 | import { DropdownAction } from '@app/shared/shared-main' | 5 | import { DropdownAction } from '@app/shared/shared-main' |
6 | import { RunnerJob, RunnerJobState } from '@shared/models' | 6 | import { RunnerJob, RunnerJobState } from '@shared/models' |
7 | import { RunnerJobFormatted, RunnerService } from '../runner.service' | 7 | import { RunnerJobFormatted, RunnerService } from '../runner.service' |
8 | import { AdvancedInputFilter } from '@app/shared/shared-forms' | ||
8 | 9 | ||
9 | @Component({ | 10 | @Component({ |
10 | selector: 'my-runner-job-list', | 11 | selector: 'my-runner-job-list', |
@@ -20,6 +21,30 @@ export class RunnerJobListComponent extends RestTable <RunnerJob> implements OnI | |||
20 | actions: DropdownAction<RunnerJob>[][] = [] | 21 | actions: DropdownAction<RunnerJob>[][] = [] |
21 | bulkActions: DropdownAction<RunnerJob[]>[][] = [] | 22 | bulkActions: DropdownAction<RunnerJob[]>[][] = [] |
22 | 23 | ||
24 | inputFilters: AdvancedInputFilter[] = [ | ||
25 | { | ||
26 | title: $localize`Advanced filters`, | ||
27 | children: [ | ||
28 | { | ||
29 | value: 'state:completed', | ||
30 | label: $localize`Completed jobs` | ||
31 | }, | ||
32 | { | ||
33 | value: 'state:pending state:waiting-for-parent-job', | ||
34 | label: $localize`Pending jobs` | ||
35 | }, | ||
36 | { | ||
37 | value: 'state:processing', | ||
38 | label: $localize`Jobs that are being processed` | ||
39 | }, | ||
40 | { | ||
41 | value: 'state:errored state:parent-errored', | ||
42 | label: $localize`Failed jobs` | ||
43 | } | ||
44 | ] | ||
45 | } | ||
46 | ] | ||
47 | |||
23 | constructor ( | 48 | constructor ( |
24 | private runnerService: RunnerService, | 49 | private runnerService: RunnerService, |
25 | private notifier: Notifier, | 50 | private notifier: Notifier, |
@@ -36,6 +61,12 @@ export class RunnerJobListComponent extends RestTable <RunnerJob> implements OnI | |||
36 | handler: job => this.cancelJobs([ job ]), | 61 | handler: job => this.cancelJobs([ job ]), |
37 | isDisplayed: job => this.canCancelJob(job) | 62 | isDisplayed: job => this.canCancelJob(job) |
38 | } | 63 | } |
64 | ], | ||
65 | [ | ||
66 | { | ||
67 | label: $localize`Delete this job`, | ||
68 | handler: job => this.removeJobs([ job ]) | ||
69 | } | ||
39 | ] | 70 | ] |
40 | ] | 71 | ] |
41 | 72 | ||
@@ -46,6 +77,12 @@ export class RunnerJobListComponent extends RestTable <RunnerJob> implements OnI | |||
46 | handler: jobs => this.cancelJobs(jobs), | 77 | handler: jobs => this.cancelJobs(jobs), |
47 | isDisplayed: jobs => jobs.every(j => this.canCancelJob(j)) | 78 | isDisplayed: jobs => jobs.every(j => this.canCancelJob(j)) |
48 | } | 79 | } |
80 | ], | ||
81 | [ | ||
82 | { | ||
83 | label: $localize`Delete`, | ||
84 | handler: jobs => this.removeJobs(jobs) | ||
85 | } | ||
49 | ] | 86 | ] |
50 | ] | 87 | ] |
51 | 88 | ||
@@ -77,6 +114,45 @@ export class RunnerJobListComponent extends RestTable <RunnerJob> implements OnI | |||
77 | }) | 114 | }) |
78 | } | 115 | } |
79 | 116 | ||
117 | async removeJobs (jobs: RunnerJob[]) { | ||
118 | const message = formatICU( | ||
119 | $localize`Do you really want to remove {count, plural, =1 {this job} other {{count} jobs}}? Children jobs will also be removed.`, | ||
120 | { count: jobs.length } | ||
121 | ) | ||
122 | |||
123 | const res = await this.confirmService.confirm(message, $localize`Remove`) | ||
124 | |||
125 | if (res === false) return | ||
126 | |||
127 | this.runnerService.removeJobs(jobs) | ||
128 | .subscribe({ | ||
129 | next: () => { | ||
130 | this.reloadData() | ||
131 | this.notifier.success($localize`Job(s) removed.`) | ||
132 | }, | ||
133 | |||
134 | error: err => this.notifier.error(err.message) | ||
135 | }) | ||
136 | } | ||
137 | |||
138 | getStateBadgeColor (job: RunnerJob) { | ||
139 | switch (job.state.id) { | ||
140 | case RunnerJobState.ERRORED: | ||
141 | case RunnerJobState.PARENT_ERRORED: | ||
142 | return 'badge-danger' | ||
143 | |||
144 | case RunnerJobState.COMPLETED: | ||
145 | return 'badge-success' | ||
146 | |||
147 | case RunnerJobState.PENDING: | ||
148 | case RunnerJobState.WAITING_FOR_PARENT_JOB: | ||
149 | return 'badge-warning' | ||
150 | |||
151 | default: | ||
152 | return 'badge-info' | ||
153 | } | ||
154 | } | ||
155 | |||
80 | protected reloadDataInternal () { | 156 | protected reloadDataInternal () { |
81 | this.runnerService.listRunnerJobs({ pagination: this.pagination, sort: this.sort, search: this.search }) | 157 | this.runnerService.listRunnerJobs({ pagination: this.pagination, sort: this.sort, search: this.search }) |
82 | .subscribe({ | 158 | .subscribe({ |
diff --git a/client/src/app/+admin/system/runners/runner-registration-token-list/runner-registration-token-list.component.html b/client/src/app/+admin/system/runners/runner-registration-token-list/runner-registration-token-list.component.html index 3e5cea881..4ff2c3b4c 100644 --- a/client/src/app/+admin/system/runners/runner-registration-token-list/runner-registration-token-list.component.html +++ b/client/src/app/+admin/system/runners/runner-registration-token-list/runner-registration-token-list.component.html | |||
@@ -45,7 +45,14 @@ | |||
45 | ></my-action-dropdown> | 45 | ></my-action-dropdown> |
46 | </td> | 46 | </td> |
47 | 47 | ||
48 | <td>{{ registrationToken.registrationToken }}</td> | 48 | <td> |
49 | {{ registrationToken.registrationToken }} | ||
50 | |||
51 | <my-copy-button | ||
52 | [value]="registrationToken.registrationToken" i18n-notification notification="Registration token copied" | ||
53 | i18n-title title="Copy registration token" | ||
54 | ></my-copy-button> | ||
55 | </td> | ||
49 | 56 | ||
50 | <td>{{ registrationToken.createdAt | date: 'short' }}</td> | 57 | <td>{{ registrationToken.createdAt | date: 'short' }}</td> |
51 | 58 | ||
diff --git a/client/src/app/+admin/system/runners/runner-registration-token-list/runner-registration-token-list.component.scss b/client/src/app/+admin/system/runners/runner-registration-token-list/runner-registration-token-list.component.scss new file mode 100644 index 000000000..1cfb2e65f --- /dev/null +++ b/client/src/app/+admin/system/runners/runner-registration-token-list/runner-registration-token-list.component.scss | |||
@@ -0,0 +1,12 @@ | |||
1 | @use '_variables' as *; | ||
2 | @use '_mixins' as *; | ||
3 | |||
4 | my-copy-button { | ||
5 | @include margin-left(3px); | ||
6 | } | ||
7 | |||
8 | tr:not(:hover) { | ||
9 | my-copy-button { | ||
10 | opacity: 0; | ||
11 | } | ||
12 | } | ||
diff --git a/client/src/app/+admin/system/runners/runner-registration-token-list/runner-registration-token-list.component.ts b/client/src/app/+admin/system/runners/runner-registration-token-list/runner-registration-token-list.component.ts index f03aab189..77908a2e1 100644 --- a/client/src/app/+admin/system/runners/runner-registration-token-list/runner-registration-token-list.component.ts +++ b/client/src/app/+admin/system/runners/runner-registration-token-list/runner-registration-token-list.component.ts | |||
@@ -7,6 +7,7 @@ import { RunnerService } from '../runner.service' | |||
7 | 7 | ||
8 | @Component({ | 8 | @Component({ |
9 | selector: 'my-runner-registration-token-list', | 9 | selector: 'my-runner-registration-token-list', |
10 | styleUrls: [ './runner-registration-token-list.component.scss' ], | ||
10 | templateUrl: './runner-registration-token-list.component.html' | 11 | templateUrl: './runner-registration-token-list.component.html' |
11 | }) | 12 | }) |
12 | export class RunnerRegistrationTokenListComponent extends RestTable <RunnerRegistrationToken> implements OnInit { | 13 | export class RunnerRegistrationTokenListComponent extends RestTable <RunnerRegistrationToken> implements OnInit { |
diff --git a/client/src/app/+admin/system/runners/runner.service.ts b/client/src/app/+admin/system/runners/runner.service.ts index 392ec82bc..3ab36c4ff 100644 --- a/client/src/app/+admin/system/runners/runner.service.ts +++ b/client/src/app/+admin/system/runners/runner.service.ts | |||
@@ -6,7 +6,7 @@ import { Injectable } from '@angular/core' | |||
6 | import { RestExtractor, RestPagination, RestService, ServerService } from '@app/core' | 6 | import { RestExtractor, RestPagination, RestService, ServerService } from '@app/core' |
7 | import { arrayify, peertubeTranslate } from '@shared/core-utils' | 7 | import { arrayify, peertubeTranslate } from '@shared/core-utils' |
8 | import { ResultList } from '@shared/models/common' | 8 | import { ResultList } from '@shared/models/common' |
9 | import { Runner, RunnerJob, RunnerJobAdmin, RunnerRegistrationToken } from '@shared/models/runners' | 9 | import { Runner, RunnerJob, RunnerJobAdmin, RunnerJobState, RunnerRegistrationToken } from '@shared/models/runners' |
10 | import { environment } from '../../../../environments/environment' | 10 | import { environment } from '../../../../environments/environment' |
11 | 11 | ||
12 | export type RunnerJobFormatted = RunnerJob & { | 12 | export type RunnerJobFormatted = RunnerJob & { |
@@ -60,7 +60,9 @@ export class RunnerService { | |||
60 | let params = new HttpParams() | 60 | let params = new HttpParams() |
61 | params = this.restService.addRestGetParams(params, pagination, sort) | 61 | params = this.restService.addRestGetParams(params, pagination, sort) |
62 | 62 | ||
63 | if (search) params = params.append('search', search) | 63 | if (search) { |
64 | params = this.buildParamsFromSearch(search, params) | ||
65 | } | ||
64 | 66 | ||
65 | return forkJoin([ | 67 | return forkJoin([ |
66 | this.authHttp.get<ResultList<RunnerJobAdmin>>(RunnerService.BASE_RUNNER_URL + '/jobs', { params }), | 68 | this.authHttp.get<ResultList<RunnerJobAdmin>>(RunnerService.BASE_RUNNER_URL + '/jobs', { params }), |
@@ -90,6 +92,31 @@ export class RunnerService { | |||
90 | ) | 92 | ) |
91 | } | 93 | } |
92 | 94 | ||
95 | private buildParamsFromSearch (search: string, params: HttpParams) { | ||
96 | const filters = this.restService.parseQueryStringFilter(search, { | ||
97 | stateOneOf: { | ||
98 | prefix: 'state:', | ||
99 | multiple: true, | ||
100 | handler: v => { | ||
101 | if (v === 'completed') return RunnerJobState.COMPLETED | ||
102 | if (v === 'processing') return RunnerJobState.PROCESSING | ||
103 | if (v === 'errored') return RunnerJobState.ERRORED | ||
104 | if (v === 'pending') return RunnerJobState.PENDING | ||
105 | if (v === 'waiting-for-parent-job') return RunnerJobState.WAITING_FOR_PARENT_JOB | ||
106 | if (v === 'parent-errored') return RunnerJobState.PARENT_ERRORED | ||
107 | |||
108 | return undefined | ||
109 | } | ||
110 | } | ||
111 | }) | ||
112 | |||
113 | console.log(filters) | ||
114 | |||
115 | return this.restService.addObjectParams(params, filters) | ||
116 | } | ||
117 | |||
118 | // --------------------------------------------------------------------------- | ||
119 | |||
93 | cancelJobs (jobsArg: RunnerJob | RunnerJob[]) { | 120 | cancelJobs (jobsArg: RunnerJob | RunnerJob[]) { |
94 | const jobs = arrayify(jobsArg) | 121 | const jobs = arrayify(jobsArg) |
95 | 122 | ||
@@ -101,6 +128,17 @@ export class RunnerService { | |||
101 | ) | 128 | ) |
102 | } | 129 | } |
103 | 130 | ||
131 | removeJobs (jobsArg: RunnerJob | RunnerJob[]) { | ||
132 | const jobs = arrayify(jobsArg) | ||
133 | |||
134 | return from(jobs) | ||
135 | .pipe( | ||
136 | concatMap(job => this.authHttp.delete(RunnerService.BASE_RUNNER_URL + '/jobs/' + job.uuid)), | ||
137 | toArray(), | ||
138 | catchError(err => this.restExtractor.handleError(err)) | ||
139 | ) | ||
140 | } | ||
141 | |||
104 | // --------------------------------------------------------------------------- | 142 | // --------------------------------------------------------------------------- |
105 | 143 | ||
106 | listRunners (options: { | 144 | listRunners (options: { |
diff --git a/client/src/app/+video-channels/video-channels.component.html b/client/src/app/+video-channels/video-channels.component.html index fff160f2e..228cc4edd 100644 --- a/client/src/app/+video-channels/video-channels.component.html +++ b/client/src/app/+video-channels/video-channels.component.html | |||
@@ -64,11 +64,11 @@ | |||
64 | 64 | ||
65 | <div class="actor-handle"> | 65 | <div class="actor-handle"> |
66 | <span>@{{ videoChannel.nameWithHost }}</span> | 66 | <span>@{{ videoChannel.nameWithHost }}</span> |
67 | <button [cdkCopyToClipboard]="videoChannel.nameWithHostForced" (click)="activateCopiedMessage()" | 67 | |
68 | class="btn btn-outline-secondary btn-sm copy-button" title="Copy channel handle" i18n-title | 68 | <my-copy-button |
69 | > | 69 | [value]="videoChannel.nameWithHostForced" i18n-notification notification="Handle copied" |
70 | <my-global-icon iconName="copy"></my-global-icon> | 70 | title="Copy channel handle" i18n-title |
71 | </button> | 71 | ></my-copy-button> |
72 | </div> | 72 | </div> |
73 | 73 | ||
74 | <div class="actor-counters"> | 74 | <div class="actor-counters"> |
diff --git a/client/src/app/+video-channels/video-channels.component.scss b/client/src/app/+video-channels/video-channels.component.scss index aba266fcc..182e8d845 100644 --- a/client/src/app/+video-channels/video-channels.component.scss +++ b/client/src/app/+video-channels/video-channels.component.scss | |||
@@ -152,14 +152,8 @@ | |||
152 | display: none; | 152 | display: none; |
153 | } | 153 | } |
154 | 154 | ||
155 | .copy-button { | 155 | my-copy-button { |
156 | @include margin-left(3px); | 156 | @include margin-left(3px); |
157 | |||
158 | border: 0; | ||
159 | |||
160 | my-global-icon { | ||
161 | width: 15px; | ||
162 | } | ||
163 | } | 157 | } |
164 | 158 | ||
165 | @media screen and (max-width: 1400px) { | 159 | @media screen and (max-width: 1400px) { |
diff --git a/client/src/app/+video-channels/video-channels.component.ts b/client/src/app/+video-channels/video-channels.component.ts index afbf96032..f5bea66ec 100644 --- a/client/src/app/+video-channels/video-channels.component.ts +++ b/client/src/app/+video-channels/video-channels.component.ts | |||
@@ -120,10 +120,6 @@ export class VideoChannelsComponent implements OnInit, OnDestroy { | |||
120 | return this.isOwner() || this.authService.getUser().hasRight(UserRight.MANAGE_ANY_VIDEO_CHANNEL) | 120 | return this.isOwner() || this.authService.getUser().hasRight(UserRight.MANAGE_ANY_VIDEO_CHANNEL) |
121 | } | 121 | } |
122 | 122 | ||
123 | activateCopiedMessage () { | ||
124 | this.notifier.success($localize`Username copied`) | ||
125 | } | ||
126 | |||
127 | hasShowMoreDescription () { | 123 | hasShowMoreDescription () { |
128 | return !this.channelDescriptionExpanded && this.channelDescriptionHTML.length > 100 | 124 | return !this.channelDescriptionExpanded && this.channelDescriptionHTML.length > 100 |
129 | } | 125 | } |
diff --git a/client/src/app/core/rest/rest.service.ts b/client/src/app/core/rest/rest.service.ts index d8b5ffb18..f07afb7e8 100644 --- a/client/src/app/core/rest/rest.service.ts +++ b/client/src/app/core/rest/rest.service.ts | |||
@@ -7,16 +7,18 @@ import { RestPagination } from './rest-pagination' | |||
7 | 7 | ||
8 | const debugLogger = debug('peertube:rest') | 8 | const debugLogger = debug('peertube:rest') |
9 | 9 | ||
10 | type ParseQueryHandlerResult = string | number | boolean | string[] | number[] | boolean[] | ||
11 | |||
10 | interface QueryStringFilterPrefixes { | 12 | interface QueryStringFilterPrefixes { |
11 | [key: string]: { | 13 | [key: string]: { |
12 | prefix: string | 14 | prefix: string |
13 | handler?: (v: string) => string | number | boolean | 15 | handler?: (v: string) => ParseQueryHandlerResult |
14 | multiple?: boolean | 16 | multiple?: boolean |
15 | isBoolean?: boolean | 17 | isBoolean?: boolean |
16 | } | 18 | } |
17 | } | 19 | } |
18 | 20 | ||
19 | type ParseQueryStringFilters <K extends keyof any> = Partial<Record<K, string | number | boolean | (string | number | boolean)[]>> | 21 | type ParseQueryStringFilters <K extends keyof any> = Partial<Record<K, ParseQueryHandlerResult | ParseQueryHandlerResult[]>> |
20 | type ParseQueryStringFiltersResult <K extends keyof any> = ParseQueryStringFilters<K> & { search?: string } | 22 | type ParseQueryStringFiltersResult <K extends keyof any> = ParseQueryStringFilters<K> & { search?: string } |
21 | 23 | ||
22 | @Injectable() | 24 | @Injectable() |
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.scss b/client/src/app/shared/shared-forms/advanced-input-filter.component.scss index 4efbeb85d..f5438ffdb 100644 --- a/client/src/app/shared/shared-forms/advanced-input-filter.component.scss +++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.scss | |||
@@ -33,6 +33,5 @@ my-global-icon { | |||
33 | 33 | ||
34 | div[role=menu] { | 34 | div[role=menu] { |
35 | max-height: 50vh; | 35 | max-height: 50vh; |
36 | min-height: 200px; | ||
37 | overflow: auto; | 36 | overflow: auto; |
38 | } | 37 | } |
diff --git a/client/src/app/shared/shared-forms/input-text.component.html b/client/src/app/shared/shared-forms/input-text.component.html index abb53a085..4747e2f8f 100644 --- a/client/src/app/shared/shared-forms/input-text.component.html +++ b/client/src/app/shared/shared-forms/input-text.component.html | |||
@@ -11,13 +11,12 @@ | |||
11 | <my-global-icon *ngIf="!show" iconName="eye-close"></my-global-icon> | 11 | <my-global-icon *ngIf="!show" iconName="eye-close"></my-global-icon> |
12 | </button> | 12 | </button> |
13 | 13 | ||
14 | <button | 14 | <my-copy-button |
15 | *ngIf="withCopy" [cdkCopyToClipboard]="input.value" (click)="activateCopiedMessage()" type="button" | 15 | *ngIf="withCopy" [value]="input.value" i18n-notification notification="Copied" |
16 | class="btn btn-outline-secondary text-uppercase" i18n-title title="Copy" | 16 | [isInputGroup]="true" i18n |
17 | > | 17 | > |
18 | <my-global-icon iconName="copy"></my-global-icon> | 18 | COPY |
19 | <span class="copy-text">Copy</span> | 19 | </my-copy-button> |
20 | </button> | ||
21 | </div> | 20 | </div> |
22 | 21 | ||
23 | <div *ngIf="formError" class="form-error">{{ formError }}</div> | 22 | <div *ngIf="formError" class="form-error">{{ formError }}</div> |
diff --git a/client/src/app/shared/shared-forms/input-text.component.ts b/client/src/app/shared/shared-forms/input-text.component.ts index aa4a1cba8..be03f25b9 100644 --- a/client/src/app/shared/shared-forms/input-text.component.ts +++ b/client/src/app/shared/shared-forms/input-text.component.ts | |||
@@ -46,10 +46,6 @@ export class InputTextComponent implements ControlValueAccessor { | |||
46 | this.show = !this.show | 46 | this.show = !this.show |
47 | } | 47 | } |
48 | 48 | ||
49 | activateCopiedMessage () { | ||
50 | this.notifier.success($localize`Copied`) | ||
51 | } | ||
52 | |||
53 | propagateChange = (_: any) => { /* empty */ } | 49 | propagateChange = (_: any) => { /* empty */ } |
54 | 50 | ||
55 | writeValue (value: string) { | 51 | writeValue (value: string) { |
diff --git a/client/src/app/shared/shared-main/buttons/copy-button.component.html b/client/src/app/shared/shared-main/buttons/copy-button.component.html new file mode 100644 index 000000000..a99c0a93a --- /dev/null +++ b/client/src/app/shared/shared-main/buttons/copy-button.component.html | |||
@@ -0,0 +1,9 @@ | |||
1 | <button | ||
2 | class="btn btn-outline-secondary btn-sm copy-button" | ||
3 | [cdkCopyToClipboard]="value" (click)="activateCopiedMessage()" | ||
4 | [title]="title" [ngClass]="{ 'is-input-group': isInputGroup }" | ||
5 | > | ||
6 | <my-global-icon iconName="copy"></my-global-icon> | ||
7 | |||
8 | <ng-content></ng-content> | ||
9 | </button> | ||
diff --git a/client/src/app/shared/shared-main/buttons/copy-button.component.scss b/client/src/app/shared/shared-main/buttons/copy-button.component.scss new file mode 100644 index 000000000..7e3720418 --- /dev/null +++ b/client/src/app/shared/shared-main/buttons/copy-button.component.scss | |||
@@ -0,0 +1,15 @@ | |||
1 | @use '_variables' as *; | ||
2 | @use '_mixins' as *; | ||
3 | |||
4 | button:not(.is-input-group) { | ||
5 | border: 0; | ||
6 | } | ||
7 | |||
8 | .is-input-group { | ||
9 | border-top-left-radius: 0; | ||
10 | border-bottom-left-radius: 0; | ||
11 | } | ||
12 | |||
13 | my-global-icon { | ||
14 | width: 15px; | ||
15 | } | ||
diff --git a/client/src/app/shared/shared-main/buttons/copy-button.component.ts b/client/src/app/shared/shared-main/buttons/copy-button.component.ts new file mode 100644 index 000000000..aac9ab8b0 --- /dev/null +++ b/client/src/app/shared/shared-main/buttons/copy-button.component.ts | |||
@@ -0,0 +1,22 @@ | |||
1 | import { Component, Input } from '@angular/core' | ||
2 | import { Notifier } from '@app/core' | ||
3 | |||
4 | @Component({ | ||
5 | selector: 'my-copy-button', | ||
6 | styleUrls: [ './copy-button.component.scss' ], | ||
7 | templateUrl: './copy-button.component.html' | ||
8 | }) | ||
9 | export class CopyButtonComponent { | ||
10 | @Input() value: string | ||
11 | @Input() title: string | ||
12 | @Input() notification: string | ||
13 | @Input() isInputGroup = false | ||
14 | |||
15 | constructor (private notifier: Notifier) { | ||
16 | |||
17 | } | ||
18 | |||
19 | activateCopiedMessage () { | ||
20 | if (this.notification) this.notifier.success(this.notification) | ||
21 | } | ||
22 | } | ||
diff --git a/client/src/app/shared/shared-main/buttons/index.ts b/client/src/app/shared/shared-main/buttons/index.ts index 775a47a39..75efbdea3 100644 --- a/client/src/app/shared/shared-main/buttons/index.ts +++ b/client/src/app/shared/shared-main/buttons/index.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | export * from './action-dropdown.component' | 1 | export * from './action-dropdown.component' |
2 | export * from './button.component' | 2 | export * from './button.component' |
3 | export * from './copy-button.component' | ||
3 | export * from './delete-button.component' | 4 | export * from './delete-button.component' |
4 | export * from './edit-button.component' | 5 | export * from './edit-button.component' |
diff --git a/client/src/app/shared/shared-main/shared-main.module.ts b/client/src/app/shared/shared-main/shared-main.module.ts index 480277450..243394bda 100644 --- a/client/src/app/shared/shared-main/shared-main.module.ts +++ b/client/src/app/shared/shared-main/shared-main.module.ts | |||
@@ -31,7 +31,7 @@ import { | |||
31 | PeerTubeTemplateDirective | 31 | PeerTubeTemplateDirective |
32 | } from './angular' | 32 | } from './angular' |
33 | import { AUTH_INTERCEPTOR_PROVIDER } from './auth' | 33 | import { AUTH_INTERCEPTOR_PROVIDER } from './auth' |
34 | import { ActionDropdownComponent, ButtonComponent, DeleteButtonComponent, EditButtonComponent } from './buttons' | 34 | import { ActionDropdownComponent, ButtonComponent, CopyButtonComponent, DeleteButtonComponent, EditButtonComponent } from './buttons' |
35 | import { CustomPageService } from './custom-page' | 35 | import { CustomPageService } from './custom-page' |
36 | import { DateToggleComponent } from './date' | 36 | import { DateToggleComponent } from './date' |
37 | import { FeedComponent } from './feeds' | 37 | import { FeedComponent } from './feeds' |
@@ -100,6 +100,7 @@ import { VideoChannelService } from './video-channel' | |||
100 | 100 | ||
101 | ActionDropdownComponent, | 101 | ActionDropdownComponent, |
102 | ButtonComponent, | 102 | ButtonComponent, |
103 | CopyButtonComponent, | ||
103 | DeleteButtonComponent, | 104 | DeleteButtonComponent, |
104 | EditButtonComponent, | 105 | EditButtonComponent, |
105 | 106 | ||
@@ -162,6 +163,7 @@ import { VideoChannelService } from './video-channel' | |||
162 | 163 | ||
163 | ActionDropdownComponent, | 164 | ActionDropdownComponent, |
164 | ButtonComponent, | 165 | ButtonComponent, |
166 | CopyButtonComponent, | ||
165 | DeleteButtonComponent, | 167 | DeleteButtonComponent, |
166 | EditButtonComponent, | 168 | EditButtonComponent, |
167 | 169 | ||