Merge remote-tracking branch 'origin/develop' into feat-4769-my-videos-contained-playlists
This commit is contained in:
commit
b4f332c6f2
|
@ -38,12 +38,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"quotes": "off",
|
||||||
"@typescript-eslint/indent": [
|
"@typescript-eslint/indent": [
|
||||||
"error",
|
"error",
|
||||||
2,
|
2,
|
||||||
{
|
{
|
||||||
"SwitchCase": 1,
|
"SwitchCase": 1,
|
||||||
"MemberExpression": "off"
|
"MemberExpression": "off",
|
||||||
|
// https://github.com/eslint/eslint/issues/15299
|
||||||
|
"ignoredNodes": ["PropertyDefinition"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"@typescript-eslint/consistent-type-assertions": [
|
"@typescript-eslint/consistent-type-assertions": [
|
||||||
|
@ -76,7 +79,14 @@
|
||||||
"@typescript-eslint/dot-notation": "off",
|
"@typescript-eslint/dot-notation": "off",
|
||||||
"@typescript-eslint/method-signature-style": "off",
|
"@typescript-eslint/method-signature-style": "off",
|
||||||
"@typescript-eslint/no-base-to-string": "off",
|
"@typescript-eslint/no-base-to-string": "off",
|
||||||
"@typescript-eslint/quotes": "off",
|
"@typescript-eslint/quotes": [
|
||||||
|
"error",
|
||||||
|
"single",
|
||||||
|
{
|
||||||
|
"avoidEscape": true,
|
||||||
|
"allowTemplateLiterals": true
|
||||||
|
}
|
||||||
|
],
|
||||||
"@typescript-eslint/no-var-requires": "off",
|
"@typescript-eslint/no-var-requires": "off",
|
||||||
"@typescript-eslint/explicit-function-return-type": "off",
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||||||
"@typescript-eslint/promise-function-async": "off",
|
"@typescript-eslint/promise-function-async": "off",
|
||||||
|
|
1
.github/workflows/test.yml
vendored
1
.github/workflows/test.yml
vendored
|
@ -82,7 +82,6 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
( \
|
( \
|
||||||
test -f dist/scripts/parse-log.js && \
|
test -f dist/scripts/parse-log.js && \
|
||||||
cat *-ci.log | uniq -c && \
|
|
||||||
NODE_ENV=test node dist/scripts/parse-log.js -l error -f artifacts/*.log \
|
NODE_ENV=test node dist/scripts/parse-log.js -l error -f artifacts/*.log \
|
||||||
) || \
|
) || \
|
||||||
echo "parse-log.js script does not exist, skipping."
|
echo "parse-log.js script does not exist, skipping."
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
"arrow-body-style": "off",
|
"arrow-body-style": "off",
|
||||||
"import/no-webpack-loader-syntax": "off",
|
"import/no-webpack-loader-syntax": "off",
|
||||||
"no-underscore-dangle": "off",
|
"no-underscore-dangle": "off",
|
||||||
"node/no-callback-literal": "off",
|
"n/no-callback-literal": "off",
|
||||||
"@angular-eslint/component-selector": [
|
"@angular-eslint/component-selector": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
|
|
|
@ -77,6 +77,10 @@
|
||||||
"translation": "src/locale/angular.sq.xlf",
|
"translation": "src/locale/angular.sq.xlf",
|
||||||
"baseHref": "/client/sq/"
|
"baseHref": "/client/sq/"
|
||||||
},
|
},
|
||||||
|
"hr": {
|
||||||
|
"translation": "src/locale/angular.hr.xlf",
|
||||||
|
"baseHref": "/client/hr/"
|
||||||
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"translation": "src/locale/angular.zh-Hans-CN.xlf",
|
"translation": "src/locale/angular.zh-Hans-CN.xlf",
|
||||||
"baseHref": "/client/zh-Hans-CN/"
|
"baseHref": "/client/zh-Hans-CN/"
|
||||||
|
@ -179,6 +183,14 @@
|
||||||
"video.js",
|
"video.js",
|
||||||
"sha.js",
|
"sha.js",
|
||||||
"postcss",
|
"postcss",
|
||||||
|
"focus-visible",
|
||||||
|
"path-browserify",
|
||||||
|
"deep-merge",
|
||||||
|
"escape-string-regexp",
|
||||||
|
"mousetrap",
|
||||||
|
"is-plain-object",
|
||||||
|
"parse-srcset",
|
||||||
|
"deepmerge",
|
||||||
"core-js/features/reflect"
|
"core-js/features/reflect"
|
||||||
],
|
],
|
||||||
"scripts": [],
|
"scripts": [],
|
||||||
|
@ -318,5 +330,8 @@
|
||||||
"@schematics/angular:directive": {
|
"@schematics/angular:directive": {
|
||||||
"prefix": "my"
|
"prefix": "my"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"cli": {
|
||||||
|
"analytics": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { getCheckbox, selectCustomSelect } from '../utils'
|
||||||
|
|
||||||
export class VideoUploadPage {
|
export class VideoUploadPage {
|
||||||
async navigateTo () {
|
async navigateTo () {
|
||||||
const publishButton = await $('.header .publish-button')
|
const publishButton = await $('.root-header .publish-button')
|
||||||
|
|
||||||
await publishButton.waitForClickable()
|
await publishButton.waitForClickable()
|
||||||
await publishButton.click()
|
await publishButton.click()
|
||||||
|
|
|
@ -27,11 +27,11 @@
|
||||||
"typings": "*.d.ts",
|
"typings": "*.d.ts",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^14.0.1",
|
"@angular-devkit/build-angular": "^14.0.1",
|
||||||
"@angular-eslint/builder": "13.5.0",
|
"@angular-eslint/builder": "14.0.2",
|
||||||
"@angular-eslint/eslint-plugin": "13.5.0",
|
"@angular-eslint/eslint-plugin": "14.0.2",
|
||||||
"@angular-eslint/eslint-plugin-template": "13.5.0",
|
"@angular-eslint/eslint-plugin-template": "14.0.2",
|
||||||
"@angular-eslint/schematics": "13.5.0",
|
"@angular-eslint/schematics": "14.0.2",
|
||||||
"@angular-eslint/template-parser": "13.5.0",
|
"@angular-eslint/template-parser": "14.0.2",
|
||||||
"@angular/animations": "^14.0.1",
|
"@angular/animations": "^14.0.1",
|
||||||
"@angular/cdk": "^14.0.1",
|
"@angular/cdk": "^14.0.1",
|
||||||
"@angular/cli": "^14.0.1",
|
"@angular/cli": "^14.0.1",
|
||||||
|
@ -52,8 +52,8 @@
|
||||||
"@ngx-loading-bar/core": "^6.0.0",
|
"@ngx-loading-bar/core": "^6.0.0",
|
||||||
"@ngx-loading-bar/http-client": "^6.0.0",
|
"@ngx-loading-bar/http-client": "^6.0.0",
|
||||||
"@ngx-loading-bar/router": "^6.0.0",
|
"@ngx-loading-bar/router": "^6.0.0",
|
||||||
"@peertube/p2p-media-loader-core": "^1.0.6",
|
"@peertube/p2p-media-loader-core": "^1.0.13",
|
||||||
"@peertube/p2p-media-loader-hlsjs": "^1.0.8",
|
"@peertube/p2p-media-loader-hlsjs": "^1.0.13",
|
||||||
"@peertube/videojs-contextmenu": "^5.5.0",
|
"@peertube/videojs-contextmenu": "^5.5.0",
|
||||||
"@peertube/xliffmerge": "^2.0.3",
|
"@peertube/xliffmerge": "^2.0.3",
|
||||||
"@popperjs/core": "^2.11.5",
|
"@popperjs/core": "^2.11.5",
|
||||||
|
@ -69,8 +69,8 @@
|
||||||
"@types/sha.js": "^2.4.0",
|
"@types/sha.js": "^2.4.0",
|
||||||
"@types/video.js": "^7.3.40",
|
"@types/video.js": "^7.3.40",
|
||||||
"@types/webtorrent": "^0.109.0",
|
"@types/webtorrent": "^0.109.0",
|
||||||
"@typescript-eslint/eslint-plugin": "5.27.1",
|
"@typescript-eslint/eslint-plugin": "5.31.0",
|
||||||
"@typescript-eslint/parser": "5.27.1",
|
"@typescript-eslint/parser": "5.31.0",
|
||||||
"@wdio/browserstack-service": "^7.20.2",
|
"@wdio/browserstack-service": "^7.20.2",
|
||||||
"@wdio/cli": "^7.20.2",
|
"@wdio/cli": "^7.20.2",
|
||||||
"@wdio/local-runner": "^7.20.2",
|
"@wdio/local-runner": "^7.20.2",
|
||||||
|
@ -84,7 +84,7 @@
|
||||||
"cache-chunk-store": "^3.0.0",
|
"cache-chunk-store": "^3.0.0",
|
||||||
"chart.js": "^3.8.0",
|
"chart.js": "^3.8.0",
|
||||||
"chartjs-plugin-zoom": "^1.2.1",
|
"chartjs-plugin-zoom": "^1.2.1",
|
||||||
"chromedriver": "^102.0.0",
|
"chromedriver": "^103.0.0",
|
||||||
"core-js": "^3.22.8",
|
"core-js": "^3.22.8",
|
||||||
"css-loader": "^6.2.0",
|
"css-loader": "^6.2.0",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
|
@ -96,8 +96,8 @@
|
||||||
"expect-webdriverio": "^3.4.0",
|
"expect-webdriverio": "^3.4.0",
|
||||||
"focus-visible": "^5.0.2",
|
"focus-visible": "^5.0.2",
|
||||||
"geckodriver": "^3.0.1",
|
"geckodriver": "^3.0.1",
|
||||||
"hls.js": "^1.0.7",
|
"hls.js": "1.2.0",
|
||||||
"html-loader": "^3.0.1",
|
"html-loader": "^4.1.0",
|
||||||
"html-webpack-plugin": "^5.3.1",
|
"html-webpack-plugin": "^5.3.1",
|
||||||
"https-browserify": "^1.0.0",
|
"https-browserify": "^1.0.0",
|
||||||
"intl-messageformat": "^10.1.0",
|
"intl-messageformat": "^10.1.0",
|
||||||
|
@ -111,7 +111,7 @@
|
||||||
"ngx-uploadx": "^5.1.0",
|
"ngx-uploadx": "^5.1.0",
|
||||||
"path-browserify": "^1.0.0",
|
"path-browserify": "^1.0.0",
|
||||||
"postcss": "^8.4.14",
|
"postcss": "^8.4.14",
|
||||||
"primeng": "^13.4.1",
|
"primeng": "^14.0.0",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"purify-css": "^1.2.5",
|
"purify-css": "^1.2.5",
|
||||||
"querystring": "^0.2.1",
|
"querystring": "^0.2.1",
|
||||||
|
@ -133,7 +133,7 @@
|
||||||
"video.js": "^7.19.2",
|
"video.js": "^7.19.2",
|
||||||
"videostream": "~3.2.1",
|
"videostream": "~3.2.1",
|
||||||
"wdio-chromedriver-service": "^7.3.2",
|
"wdio-chromedriver-service": "^7.3.2",
|
||||||
"wdio-geckodriver-service": "^2.1.1",
|
"wdio-geckodriver-service": "^3.0.2",
|
||||||
"webpack": "^5.73.0",
|
"webpack": "^5.73.0",
|
||||||
"webpack-bundle-analyzer": "^4.4.2",
|
"webpack-bundle-analyzer": "^4.4.2",
|
||||||
"webpack-cli": "^4.10.0",
|
"webpack-cli": "^4.10.0",
|
||||||
|
|
|
@ -103,7 +103,7 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
|
||||||
signupControl.valueChanges
|
signupControl.valueChanges
|
||||||
.pipe(pairwise())
|
.pipe(pairwise())
|
||||||
.subscribe(([ oldValue, newValue ]) => {
|
.subscribe(([ oldValue, newValue ]) => {
|
||||||
if (oldValue !== true && newValue === true) {
|
if (oldValue === false && newValue === true) {
|
||||||
/* eslint-disable max-len */
|
/* eslint-disable max-len */
|
||||||
this.signupAlertMessage = $localize`You enabled signup: we automatically enabled the "Block new videos automatically" checkbox of the "Videos" section just below.`
|
this.signupAlertMessage = $localize`You enabled signup: we automatically enabled the "Block new videos automatically" checkbox of the "Videos" section just below.`
|
||||||
|
|
||||||
|
@ -118,5 +118,7 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
signupControl.updateValueAndValidity()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ input[type=number] {
|
||||||
|
|
||||||
.number-with-unit {
|
.number-with-unit {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: min-content;
|
width: fit-content;
|
||||||
|
|
||||||
input[type=number] + span {
|
input[type=number] + span {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -80,6 +80,7 @@ input[type=submit] {
|
||||||
|
|
||||||
.inner-form-description {
|
.inner-form-description {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
|
|
|
@ -175,6 +175,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||||
profile: null,
|
profile: null,
|
||||||
concurrency: CONCURRENCY_VALIDATOR,
|
concurrency: CONCURRENCY_VALIDATOR,
|
||||||
resolutions: {},
|
resolutions: {},
|
||||||
|
alwaysTranscodeOriginalResolution: null,
|
||||||
hls: {
|
hls: {
|
||||||
enabled: null
|
enabled: null
|
||||||
},
|
},
|
||||||
|
@ -197,7 +198,8 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||||
enabled: null,
|
enabled: null,
|
||||||
threads: TRANSCODING_THREADS_VALIDATOR,
|
threads: TRANSCODING_THREADS_VALIDATOR,
|
||||||
profile: null,
|
profile: null,
|
||||||
resolutions: {}
|
resolutions: {},
|
||||||
|
alwaysTranscodeOriginalResolution: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
videoStudio: {
|
videoStudio: {
|
||||||
|
|
|
@ -41,7 +41,6 @@
|
||||||
<ng-container ngProjectAs="description" i18n>
|
<ng-container ngProjectAs="description" i18n>
|
||||||
Small latency disables P2P and high latency can increase P2P ratio
|
Small latency disables P2P and high latency can increase P2P ratio
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
</my-peertube-checkbox>
|
</my-peertube-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -115,8 +114,8 @@
|
||||||
<label i18n for="liveTranscodingThreads">Live resolutions to generate</label>
|
<label i18n for="liveTranscodingThreads">Live resolutions to generate</label>
|
||||||
|
|
||||||
<div class="ms-2 mt-2 d-flex flex-column">
|
<div class="ms-2 mt-2 d-flex flex-column">
|
||||||
<ng-container formGroupName="resolutions">
|
|
||||||
|
|
||||||
|
<ng-container formGroupName="resolutions">
|
||||||
<div class="form-group" *ngFor="let resolution of liveResolutions">
|
<div class="form-group" *ngFor="let resolution of liveResolutions">
|
||||||
<my-peertube-checkbox
|
<my-peertube-checkbox
|
||||||
[inputName]="getResolutionKey(resolution.id)" [formControlName]="resolution.id"
|
[inputName]="getResolutionKey(resolution.id)" [formControlName]="resolution.id"
|
||||||
|
@ -127,8 +126,18 @@
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</my-peertube-checkbox>
|
</my-peertube-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<my-peertube-checkbox
|
||||||
|
inputName="transcodingAlwaysTranscodeOriginalResolution" formControlName="alwaysTranscodeOriginalResolution"
|
||||||
|
i18n-labelText labelText="Also transcode original resolution"
|
||||||
|
>
|
||||||
|
<ng-container i18n ngProjectAs="description">
|
||||||
|
Even if it's above your maximum enabled resolution
|
||||||
|
</ng-container>
|
||||||
|
</my-peertube-checkbox>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,13 @@
|
||||||
<label i18n>Resolutions to generate per enabled format</label>
|
<label i18n>Resolutions to generate per enabled format</label>
|
||||||
|
|
||||||
<div class="ms-2 d-flex flex-column">
|
<div class="ms-2 d-flex flex-column">
|
||||||
<span class="mb-3 small muted" i18n>
|
<my-peertube-checkbox
|
||||||
|
inputName="transcodingAlwaysTranscodeOriginalResolution" formControlName="alwaysTranscodeOriginalResolution"
|
||||||
|
i18n-labelText labelText="Always transcode original resolution"
|
||||||
|
>
|
||||||
|
</my-peertube-checkbox>
|
||||||
|
|
||||||
|
<span class="mt-3 mb-2 small muted" i18n>
|
||||||
The original file resolution will be the default target if no option is selected.
|
The original file resolution will be the default target if no option is selected.
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
|
@ -104,5 +104,10 @@ export class EditVODTranscodingComponent implements OnInit, OnChanges {
|
||||||
videoStudioControl.setValue(false)
|
videoStudioControl.setValue(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
transcodingControl.updateValueAndValidity()
|
||||||
|
webtorrentControl.updateValueAndValidity()
|
||||||
|
videoStudioControl.updateValueAndValidity()
|
||||||
|
hlsControl.updateValueAndValidity()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,22 +4,34 @@
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p-table
|
<p-table
|
||||||
[value]="followers" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
|
[value]="followers" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
|
||||||
[sortField]="sort.field" [sortOrder]="sort.order"
|
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order"
|
||||||
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
|
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
|
||||||
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
||||||
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} followers"
|
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} followers"
|
||||||
|
[(selection)]="selectedFollows"
|
||||||
>
|
>
|
||||||
<ng-template pTemplate="caption">
|
<ng-template pTemplate="caption">
|
||||||
<div class="caption">
|
<div class="caption">
|
||||||
|
<div class="left-buttons">
|
||||||
|
<my-action-dropdown
|
||||||
|
*ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
|
||||||
|
[actions]="bulkFollowsActions" [entry]="selectedFollows"
|
||||||
|
>
|
||||||
|
</my-action-dropdown>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="ms-auto">
|
<div class="ms-auto">
|
||||||
<my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
|
<my-advanced-input-filter [filters]="searchFilters" (search)="onSearch($event)"></my-advanced-input-filter>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template pTemplate="header">
|
<ng-template pTemplate="header">
|
||||||
<tr>
|
<tr>
|
||||||
|
<th style="width: 40px">
|
||||||
|
<p-tableHeaderCheckbox ariaLabel="Select all rows" i18n-ariaLabel></p-tableHeaderCheckbox>
|
||||||
|
</th>
|
||||||
<th style="width: 150px;" i18n>Actions</th>
|
<th style="width: 150px;" i18n>Actions</th>
|
||||||
<th i18n>Follower</th>
|
<th i18n>Follower</th>
|
||||||
<th style="width: 100px;" i18n pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th>
|
<th style="width: 100px;" i18n pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th>
|
||||||
|
@ -30,26 +42,27 @@
|
||||||
|
|
||||||
<ng-template pTemplate="body" let-follow>
|
<ng-template pTemplate="body" let-follow>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="action-cell">
|
<td class="checkbox-cell">
|
||||||
<ng-container *ngIf="follow.state === 'pending'">
|
<p-tableCheckbox [value]="follow" ariaLabel="Select this row" i18n-ariaLabel></p-tableCheckbox>
|
||||||
<my-button i18n-title title="Accept" icon="tick" (click)="acceptFollower(follow)"></my-button>
|
</td>
|
||||||
<my-button i18n-title title="Refuse" icon="cross" (click)="rejectFollower(follow)"></my-button>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<my-delete-button label *ngIf="follow.state === 'accepted'" (click)="deleteFollower(follow)"></my-delete-button>
|
<td class="action-cell">
|
||||||
|
<my-button *ngIf="follow.state !== 'accepted'" i18n-title title="Accept" icon="tick" (click)="acceptFollower([ follow ])"></my-button>
|
||||||
|
<my-button *ngIf="follow.state !== 'rejected'" i18n-title title="Reject" icon="cross" (click)="rejectFollower([ follow ])"></my-button>
|
||||||
|
|
||||||
|
<my-delete-button *ngIf="follow.state === 'rejected'" (click)="deleteFollowers([ follow ])"></my-delete-button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a [href]="follow.follower.url" i18n-title title="Open actor page in a new tab" target="_blank" rel="noopener noreferrer">
|
<a [href]="follow.follower.url" i18n-title title="Open actor page in a new tab" target="_blank" rel="noopener noreferrer">
|
||||||
{{ follow.follower.name + '@' + follow.follower.host }}
|
{{ buildFollowerName(follow) }}
|
||||||
<my-global-icon iconName="external-link"></my-global-icon>
|
<my-global-icon iconName="external-link"></my-global-icon>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td *ngIf="follow.state === 'accepted'">
|
<td>
|
||||||
<span class="pt-badge badge-green" i18n>Accepted</span>
|
<span *ngIf="follow.state === 'accepted'" class="pt-badge badge-green" i18n>Accepted</span>
|
||||||
</td>
|
<span *ngIf="follow.state === 'pending'" class="pt-badge badge-yellow" i18n>Pending</span>
|
||||||
<td *ngIf="follow.state === 'pending'">
|
<span *ngIf="follow.state === 'rejected'" class="pt-badge badge-red" i18n>Rejected</span>
|
||||||
<span class="pt-badge badge-yellow" i18n>Pending</span>
|
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>{{ follow.score }}</td>
|
<td>{{ follow.score }}</td>
|
||||||
|
@ -59,7 +72,7 @@
|
||||||
|
|
||||||
<ng-template pTemplate="emptymessage">
|
<ng-template pTemplate="emptymessage">
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="5">
|
<td colspan="6">
|
||||||
<div class="no-results">
|
<div class="no-results">
|
||||||
<ng-container *ngIf="search" i18n>No follower found matching current filters.</ng-container>
|
<ng-container *ngIf="search" i18n>No follower found matching current filters.</ng-container>
|
||||||
<ng-container *ngIf="!search" i18n>Your instance doesn't have any follower.</ng-container>
|
<ng-container *ngIf="!search" i18n>Your instance doesn't have any follower.</ng-container>
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import { SortMeta } from 'primeng/api'
|
import { SortMeta } from 'primeng/api'
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
|
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
|
||||||
|
import { prepareIcu } from '@app/helpers'
|
||||||
|
import { AdvancedInputFilter } from '@app/shared/shared-forms'
|
||||||
import { InstanceFollowService } from '@app/shared/shared-instance'
|
import { InstanceFollowService } from '@app/shared/shared-instance'
|
||||||
|
import { DropdownAction } from '@app/shared/shared-main'
|
||||||
import { ActorFollow } from '@shared/models'
|
import { ActorFollow } from '@shared/models'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -15,6 +18,11 @@ export class FollowersListComponent extends RestTable implements OnInit {
|
||||||
sort: SortMeta = { field: 'createdAt', order: -1 }
|
sort: SortMeta = { field: 'createdAt', order: -1 }
|
||||||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||||
|
|
||||||
|
searchFilters: AdvancedInputFilter[] = []
|
||||||
|
|
||||||
|
selectedFollows: ActorFollow[] = []
|
||||||
|
bulkFollowsActions: DropdownAction<ActorFollow[]>[] = []
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private confirmService: ConfirmService,
|
private confirmService: ConfirmService,
|
||||||
private notifier: Notifier,
|
private notifier: Notifier,
|
||||||
|
@ -25,60 +33,42 @@ export class FollowersListComponent extends RestTable implements OnInit {
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.initialize()
|
this.initialize()
|
||||||
|
|
||||||
|
this.searchFilters = this.followService.buildFollowsListFilters()
|
||||||
|
|
||||||
|
this.bulkFollowsActions = [
|
||||||
|
{
|
||||||
|
label: $localize`Reject`,
|
||||||
|
handler: follows => this.rejectFollower(follows),
|
||||||
|
isDisplayed: follows => follows.every(f => f.state !== 'rejected')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $localize`Accept`,
|
||||||
|
handler: follows => this.acceptFollower(follows),
|
||||||
|
isDisplayed: follows => follows.every(f => f.state !== 'accepted')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $localize`Delete`,
|
||||||
|
handler: follows => this.deleteFollowers(follows),
|
||||||
|
isDisplayed: follows => follows.every(f => f.state === 'rejected')
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
getIdentifier () {
|
getIdentifier () {
|
||||||
return 'FollowersListComponent'
|
return 'FollowersListComponent'
|
||||||
}
|
}
|
||||||
|
|
||||||
acceptFollower (follow: ActorFollow) {
|
acceptFollower (follows: ActorFollow[]) {
|
||||||
follow.state = 'accepted'
|
this.followService.acceptFollower(follows)
|
||||||
|
|
||||||
this.followService.acceptFollower(follow)
|
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
const handle = follow.follower.name + '@' + follow.follower.host
|
// eslint-disable-next-line max-len
|
||||||
this.notifier.success($localize`${handle} accepted in instance followers`)
|
const message = prepareIcu($localize`Accepted {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`)(
|
||||||
},
|
{ count: follows.length, followerName: this.buildFollowerName(follows[0]) },
|
||||||
|
$localize`Follow requests accepted`
|
||||||
error: err => {
|
)
|
||||||
follow.state = 'pending'
|
this.notifier.success(message)
|
||||||
this.notifier.error(err.message)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async rejectFollower (follow: ActorFollow) {
|
|
||||||
const message = $localize`Do you really want to reject this follower?`
|
|
||||||
const res = await this.confirmService.confirm(message, $localize`Reject`)
|
|
||||||
if (res === false) return
|
|
||||||
|
|
||||||
this.followService.rejectFollower(follow)
|
|
||||||
.subscribe({
|
|
||||||
next: () => {
|
|
||||||
const handle = follow.follower.name + '@' + follow.follower.host
|
|
||||||
this.notifier.success($localize`${handle} rejected from instance followers`)
|
|
||||||
|
|
||||||
this.reloadData()
|
|
||||||
},
|
|
||||||
|
|
||||||
error: err => {
|
|
||||||
follow.state = 'pending'
|
|
||||||
this.notifier.error(err.message)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteFollower (follow: ActorFollow) {
|
|
||||||
const message = $localize`Do you really want to delete this follower?`
|
|
||||||
const res = await this.confirmService.confirm(message, $localize`Delete`)
|
|
||||||
if (res === false) return
|
|
||||||
|
|
||||||
this.followService.removeFollower(follow)
|
|
||||||
.subscribe({
|
|
||||||
next: () => {
|
|
||||||
const handle = follow.follower.name + '@' + follow.follower.host
|
|
||||||
this.notifier.success($localize`${handle} removed from instance followers`)
|
|
||||||
|
|
||||||
this.reloadData()
|
this.reloadData()
|
||||||
},
|
},
|
||||||
|
@ -87,6 +77,72 @@ export class FollowersListComponent extends RestTable implements OnInit {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async rejectFollower (follows: ActorFollow[]) {
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
const message = prepareIcu($localize`Do you really want to reject {count, plural, =1 {{followerName} follow request?} other {{count} follow requests?}}`)(
|
||||||
|
{ count: follows.length, followerName: this.buildFollowerName(follows[0]) },
|
||||||
|
$localize`Do you really want to reject these follow requests?`
|
||||||
|
)
|
||||||
|
|
||||||
|
const res = await this.confirmService.confirm(message, $localize`Reject`)
|
||||||
|
if (res === false) return
|
||||||
|
|
||||||
|
this.followService.rejectFollower(follows)
|
||||||
|
.subscribe({
|
||||||
|
next: () => {
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
const message = prepareIcu($localize`Rejected {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`)(
|
||||||
|
{ count: follows.length, followerName: this.buildFollowerName(follows[0]) },
|
||||||
|
$localize`Follow requests rejected`
|
||||||
|
)
|
||||||
|
this.notifier.success(message)
|
||||||
|
|
||||||
|
this.reloadData()
|
||||||
|
},
|
||||||
|
|
||||||
|
error: err => this.notifier.error(err.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteFollowers (follows: ActorFollow[]) {
|
||||||
|
let message = $localize`Deleted followers will be able to send again a follow request.`
|
||||||
|
message += '<br /><br />'
|
||||||
|
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
message += prepareIcu($localize`Do you really want to delete {count, plural, =1 {{followerName} follow request?} other {{count} follow requests?}}`)(
|
||||||
|
{ count: follows.length, followerName: this.buildFollowerName(follows[0]) },
|
||||||
|
$localize`Do you really want to delete these follow requests?`
|
||||||
|
)
|
||||||
|
|
||||||
|
const res = await this.confirmService.confirm(message, $localize`Delete`)
|
||||||
|
if (res === false) return
|
||||||
|
|
||||||
|
this.followService.removeFollower(follows)
|
||||||
|
.subscribe({
|
||||||
|
next: () => {
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
const message = prepareIcu($localize`Removed {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`)(
|
||||||
|
{ count: follows.length, followerName: this.buildFollowerName(follows[0]) },
|
||||||
|
$localize`Follow requests removed`
|
||||||
|
)
|
||||||
|
|
||||||
|
this.notifier.success(message)
|
||||||
|
|
||||||
|
this.reloadData()
|
||||||
|
},
|
||||||
|
|
||||||
|
error: err => this.notifier.error(err.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFollowerName (follow: ActorFollow) {
|
||||||
|
return follow.follower.name + '@' + follow.follower.host
|
||||||
|
}
|
||||||
|
|
||||||
|
isInSelectionMode () {
|
||||||
|
return this.selectedFollows.length !== 0
|
||||||
|
}
|
||||||
|
|
||||||
protected reloadData () {
|
protected reloadData () {
|
||||||
this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search })
|
this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search })
|
||||||
.subscribe({
|
.subscribe({
|
||||||
|
|
|
@ -4,29 +4,39 @@
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p-table
|
<p-table
|
||||||
[value]="following" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
|
[value]="following" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
|
||||||
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)"
|
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)"
|
||||||
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
|
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
|
||||||
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
||||||
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} hosts"
|
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} hosts"
|
||||||
|
[(selection)]="selectedFollows"
|
||||||
>
|
>
|
||||||
<ng-template pTemplate="caption">
|
<ng-template pTemplate="caption">
|
||||||
<div class="caption">
|
<div class="caption">
|
||||||
<div class="left-buttons">
|
<div class="left-buttons">
|
||||||
<a class="follow-button" (click)="openFollowModal()" (key.enter)="openFollowModal()">
|
<my-action-dropdown
|
||||||
|
*ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
|
||||||
|
[actions]="bulkFollowsActions" [entry]="selectedFollows"
|
||||||
|
>
|
||||||
|
</my-action-dropdown>
|
||||||
|
|
||||||
|
<a *ngIf="!isInSelectionMode()" class="follow-button" (click)="openFollowModal()" (key.enter)="openFollowModal()">
|
||||||
<my-global-icon iconName="following" aria-hidden="true"></my-global-icon>
|
<my-global-icon iconName="following" aria-hidden="true"></my-global-icon>
|
||||||
<ng-container i18n>Follow</ng-container>
|
<ng-container i18n>Follow</ng-container>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ms-auto">
|
<div class="ms-auto">
|
||||||
<my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
|
<my-advanced-input-filter [filters]="searchFilters" (search)="onSearch($event)"></my-advanced-input-filter>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template pTemplate="header">
|
<ng-template pTemplate="header">
|
||||||
<tr>
|
<tr>
|
||||||
|
<th style="width: 40px">
|
||||||
|
<p-tableHeaderCheckbox ariaLabel="Select all rows" i18n-ariaLabel></p-tableHeaderCheckbox>
|
||||||
|
</th>
|
||||||
<th style="width: 150px;" i18n>Action</th>
|
<th style="width: 150px;" i18n>Action</th>
|
||||||
<th i18n>Following</th>
|
<th i18n>Following</th>
|
||||||
<th style="width: 100px;" i18n pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th>
|
<th style="width: 100px;" i18n pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th>
|
||||||
|
@ -35,23 +45,26 @@
|
||||||
</tr>
|
</tr>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template pTemplate="body" let-follow>
|
<ng-template pSelectableRow="follow" pTemplate="body" let-follow>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td class="checkbox-cell">
|
||||||
|
<p-tableCheckbox [value]="follow" ariaLabel="Select this row" i18n-ariaLabel></p-tableCheckbox>
|
||||||
|
</td>
|
||||||
|
|
||||||
<td class="action-cell">
|
<td class="action-cell">
|
||||||
<my-delete-button label (click)="removeFollowing(follow)"></my-delete-button>
|
<my-delete-button label (click)="removeFollowing([ follow ])"></my-delete-button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a [href]="follow.following.url" i18n-title title="Open instance in a new tab" target="_blank" rel="noopener noreferrer">
|
<a [href]="follow.following.url" i18n-title title="Open instance in a new tab" target="_blank" rel="noopener noreferrer">
|
||||||
{{ follow.following.name + '@' + follow.following.host }}
|
{{ buildFollowingName(follow) }}
|
||||||
<my-global-icon iconName="external-link"></my-global-icon>
|
<my-global-icon iconName="external-link"></my-global-icon>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td *ngIf="follow.state === 'accepted'">
|
<td>
|
||||||
<span class="pt-badge badge-green" i18n>Accepted</span>
|
<span *ngIf="follow.state === 'accepted'" class="pt-badge badge-green" i18n>Accepted</span>
|
||||||
</td>
|
<span *ngIf="follow.state === 'pending'" class="pt-badge badge-yellow" i18n>Pending</span>
|
||||||
<td *ngIf="follow.state === 'pending'">
|
<span *ngIf="follow.state === 'rejected'" class="pt-badge badge-red" i18n>Rejected</span>
|
||||||
<span class="pt-badge badge-yellow" i18n>Pending</span>
|
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>{{ follow.createdAt | date: 'short' }}</td>
|
<td>{{ follow.createdAt | date: 'short' }}</td>
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import { SortMeta } from 'primeng/api'
|
import { SortMeta } from 'primeng/api'
|
||||||
import { Component, OnInit, ViewChild } from '@angular/core'
|
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||||
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
|
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
|
||||||
|
import { AdvancedInputFilter } from '@app/shared/shared-forms'
|
||||||
import { InstanceFollowService } from '@app/shared/shared-instance'
|
import { InstanceFollowService } from '@app/shared/shared-instance'
|
||||||
import { ActorFollow } from '@shared/models'
|
import { ActorFollow } from '@shared/models'
|
||||||
import { FollowModalComponent } from './follow-modal.component'
|
import { FollowModalComponent } from './follow-modal.component'
|
||||||
|
import { DropdownAction } from '@app/shared/shared-main'
|
||||||
|
import { prepareIcu } from '@app/helpers'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './following-list.component.html',
|
templateUrl: './following-list.component.html',
|
||||||
|
@ -17,6 +20,11 @@ export class FollowingListComponent extends RestTable implements OnInit {
|
||||||
sort: SortMeta = { field: 'createdAt', order: -1 }
|
sort: SortMeta = { field: 'createdAt', order: -1 }
|
||||||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||||
|
|
||||||
|
searchFilters: AdvancedInputFilter[] = []
|
||||||
|
|
||||||
|
selectedFollows: ActorFollow[] = []
|
||||||
|
bulkFollowsActions: DropdownAction<ActorFollow[]>[] = []
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private notifier: Notifier,
|
private notifier: Notifier,
|
||||||
private confirmService: ConfirmService,
|
private confirmService: ConfirmService,
|
||||||
|
@ -27,6 +35,15 @@ export class FollowingListComponent extends RestTable implements OnInit {
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.initialize()
|
this.initialize()
|
||||||
|
|
||||||
|
this.searchFilters = this.followService.buildFollowsListFilters()
|
||||||
|
|
||||||
|
this.bulkFollowsActions = [
|
||||||
|
{
|
||||||
|
label: $localize`Delete`,
|
||||||
|
handler: follows => this.removeFollowing(follows)
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
getIdentifier () {
|
getIdentifier () {
|
||||||
|
@ -41,17 +58,33 @@ export class FollowingListComponent extends RestTable implements OnInit {
|
||||||
return follow.following.name === 'peertube'
|
return follow.following.name === 'peertube'
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeFollowing (follow: ActorFollow) {
|
isInSelectionMode () {
|
||||||
const res = await this.confirmService.confirm(
|
return this.selectedFollows.length !== 0
|
||||||
$localize`Do you really want to unfollow ${follow.following.host}?`,
|
}
|
||||||
$localize`Unfollow`
|
|
||||||
|
buildFollowingName (follow: ActorFollow) {
|
||||||
|
return follow.following.name + '@' + follow.following.host
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeFollowing (follows: ActorFollow[]) {
|
||||||
|
const message = prepareIcu($localize`Do you really want to unfollow {count, plural, =1 {{entryName}?} other {{count} entries?}}`)(
|
||||||
|
{ count: follows.length, entryName: this.buildFollowingName(follows[0]) },
|
||||||
|
$localize`Do you really want to unfollow these entries?`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const res = await this.confirmService.confirm(message, $localize`Unfollow`)
|
||||||
if (res === false) return
|
if (res === false) return
|
||||||
|
|
||||||
this.followService.unfollow(follow)
|
this.followService.unfollow(follows)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
this.notifier.success($localize`You are not following ${follow.following.host} anymore.`)
|
// eslint-disable-next-line max-len
|
||||||
|
const message = prepareIcu($localize`You are not following {count, plural, =1 {{entryName} anymore.} other {these {count} entries anymore.}}`)(
|
||||||
|
{ count: follows.length, entryName: this.buildFollowingName(follows[0]) },
|
||||||
|
$localize`You are not following them anymore.`
|
||||||
|
)
|
||||||
|
|
||||||
|
this.notifier.success(message)
|
||||||
this.reloadData()
|
this.reloadData()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p-table
|
<p-table
|
||||||
[value]="videoRedundancies" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
|
[value]="videoRedundancies" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords"
|
||||||
|
[rows]="rowsPerPage" [first]="pagination.start" [rowsPerPageOptions]="rowsPerPageOptions"
|
||||||
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
|
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
|
||||||
[expandedRowKeys]="expandedRows"
|
[expandedRowKeys]="expandedRows"
|
||||||
>
|
>
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p-table
|
<p-table
|
||||||
[value]="blocklist" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
|
[value]="blocklist" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
|
||||||
[sortField]="sort.field" [sortOrder]="sort.order" dataKey="id"
|
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id"
|
||||||
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
|
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
|
||||||
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
||||||
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} blocked videos"
|
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} blocked videos"
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
<em i18n>This view also shows comments from muted accounts.</em>
|
<em i18n>This view also shows comments from muted accounts.</em>
|
||||||
|
|
||||||
<p-table
|
<p-table
|
||||||
[value]="comments" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
|
[value]="comments" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
|
||||||
[sortField]="sort.field" [sortOrder]="sort.order" dataKey="id"
|
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id"
|
||||||
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" [selectionPageOnly]="true"
|
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" [selectionPageOnly]="true"
|
||||||
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
||||||
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} comments"
|
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} comments"
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p-table
|
<p-table
|
||||||
[value]="users" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
|
[value]="users" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
|
||||||
[sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true" [(selection)]="selectedUsers"
|
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true"
|
||||||
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" [selectionPageOnly]="true"
|
[(selection)]="selectedUsers" [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" [selectionPageOnly]="true"
|
||||||
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
||||||
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} users"
|
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} users"
|
||||||
[expandedRowKeys]="expandedRows"
|
[expandedRowKeys]="expandedRows"
|
||||||
|
|
|
@ -2,11 +2,12 @@ import { SortMeta } from 'primeng/api'
|
||||||
import { Component, OnInit, ViewChild } from '@angular/core'
|
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import { AuthService, ConfirmService, LocalStorageService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
|
import { AuthService, ConfirmService, LocalStorageService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
|
||||||
import { prepareIcu, getAPIHost } from '@app/helpers'
|
import { getAPIHost, prepareIcu } from '@app/helpers'
|
||||||
import { AdvancedInputFilter } from '@app/shared/shared-forms'
|
import { AdvancedInputFilter } from '@app/shared/shared-forms'
|
||||||
import { Actor, DropdownAction } from '@app/shared/shared-main'
|
import { Actor, DropdownAction } from '@app/shared/shared-main'
|
||||||
import { AccountMutedStatus, BlocklistService, UserBanModalComponent, UserModerationDisplayType } from '@app/shared/shared-moderation'
|
import { AccountMutedStatus, BlocklistService, UserBanModalComponent, UserModerationDisplayType } from '@app/shared/shared-moderation'
|
||||||
import { UserAdminService } from '@app/shared/shared-users'
|
import { UserAdminService } from '@app/shared/shared-users'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { User, UserRole } from '@shared/models'
|
import { User, UserRole } from '@shared/models'
|
||||||
|
|
||||||
type UserForList = User & {
|
type UserForList = User & {
|
||||||
|
@ -149,7 +150,7 @@ export class UserListComponent extends RestTable implements OnInit {
|
||||||
this.selectedColumns = JSON.parse(result)
|
this.selectedColumns = JSON.parse(result)
|
||||||
return
|
return
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Cannot load selected columns.', err)
|
logger.error('Cannot load selected columns.', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p-table
|
<p-table
|
||||||
[value]="videos" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
|
[value]="videos" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
|
||||||
[sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true" [(selection)]="selectedVideos"
|
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true"
|
||||||
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" [selectionPageOnly]="true"
|
[(selection)]="selectedVideos" [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" [selectionPageOnly]="true"
|
||||||
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
||||||
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} videos"
|
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} videos"
|
||||||
[expandedRowKeys]="expandedRows" [ngClass]="{ loading: loading }"
|
[expandedRowKeys]="expandedRows" [ngClass]="{ loading: loading }"
|
||||||
|
@ -107,6 +107,11 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li *ngFor="let file of video.files">
|
<li *ngFor="let file of video.files">
|
||||||
{{ file.resolution.label }}: {{ file.size | bytes: 1 }}
|
{{ file.resolution.label }}: {{ file.size | bytes: 1 }}
|
||||||
|
|
||||||
|
<my-global-icon
|
||||||
|
i18n-ngbTooltip ngbTooltip="Delete this file" iconName="delete" role="button"
|
||||||
|
(click)="removeVideoFile(video, file, 'webtorrent')"
|
||||||
|
></my-global-icon>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -117,6 +122,11 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li *ngFor="let file of video.streamingPlaylists[0].files">
|
<li *ngFor="let file of video.streamingPlaylists[0].files">
|
||||||
{{ file.resolution.label }}: {{ file.size | bytes: 1 }}
|
{{ file.resolution.label }}: {{ file.size | bytes: 1 }}
|
||||||
|
|
||||||
|
<my-global-icon
|
||||||
|
i18n-ngbTooltip ngbTooltip="Delete this file" iconName="delete" role="button"
|
||||||
|
(click)="removeVideoFile(video, file, 'hls')"
|
||||||
|
></my-global-icon>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,6 +13,13 @@ my-embed {
|
||||||
|
|
||||||
.video-info > div {
|
.video-info > div {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
|
my-global-icon {
|
||||||
|
width: 16px;
|
||||||
|
margin-left: 3px;
|
||||||
|
position: relative;
|
||||||
|
top: -2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading {
|
.loading {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { AdvancedInputFilter } from '@app/shared/shared-forms'
|
||||||
import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
|
import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
|
||||||
import { VideoBlockComponent, VideoBlockService } from '@app/shared/shared-moderation'
|
import { VideoBlockComponent, VideoBlockService } from '@app/shared/shared-moderation'
|
||||||
import { VideoActionsDisplayType } from '@app/shared/shared-video-miniature'
|
import { VideoActionsDisplayType } from '@app/shared/shared-video-miniature'
|
||||||
import { UserRight, VideoPrivacy, VideoState, VideoStreamingPlaylistType } from '@shared/models'
|
import { UserRight, VideoFile, VideoPrivacy, VideoState, VideoStreamingPlaylistType } from '@shared/models'
|
||||||
import { VideoAdminService } from './video-admin.service'
|
import { VideoAdminService } from './video-admin.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -196,6 +196,22 @@ export class VideoListComponent extends RestTable implements OnInit {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async removeVideoFile (video: Video, file: VideoFile, type: 'hls' | 'webtorrent') {
|
||||||
|
const message = $localize`Are you sure you want to delete this ${file.resolution.label} file?`
|
||||||
|
const res = await this.confirmService.confirm(message, $localize`Delete file`)
|
||||||
|
if (res === false) return
|
||||||
|
|
||||||
|
this.videoService.removeFile(video.uuid, file.id, type)
|
||||||
|
.subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.notifier.success($localize`File removed.`)
|
||||||
|
this.reloadData()
|
||||||
|
},
|
||||||
|
|
||||||
|
error: err => this.notifier.error(err.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private async removeVideos (videos: Video[]) {
|
private async removeVideos (videos: Video[]) {
|
||||||
const message = prepareIcu($localize`Are you sure you want to delete {count, plural, =1 {this video} other {these {count} videos}}?`)(
|
const message = prepareIcu($localize`Are you sure you want to delete {count, plural, =1 {this video} other {these {count} videos}}?`)(
|
||||||
{ count: videos.length },
|
{ count: videos.length },
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { Component, OnInit } from '@angular/core'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
|
import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
|
||||||
import { ComponentPagination, ConfirmService, hasMoreItems, Notifier, PluginService } from '@app/core'
|
import { ComponentPagination, ConfirmService, hasMoreItems, Notifier, PluginService } from '@app/core'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { PeerTubePluginIndex, PluginType } from '@shared/models'
|
import { PeerTubePluginIndex, PluginType } from '@shared/models'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -94,7 +95,7 @@ export class PluginSearchComponent implements OnInit {
|
||||||
},
|
},
|
||||||
|
|
||||||
error: err => {
|
error: err => {
|
||||||
console.error(err)
|
logger.error(err)
|
||||||
|
|
||||||
const message = $localize`The plugin index is not available. Please retry later.`
|
const message = $localize`The plugin index is not available. Please retry later.`
|
||||||
this.notifier.error(message)
|
this.notifier.error(message)
|
||||||
|
|
|
@ -111,7 +111,7 @@ export class PluginShowInstalledComponent extends FormReactive implements OnInit
|
||||||
|
|
||||||
this.form.patchValue(settingsValues)
|
this.form.patchValue(settingsValues)
|
||||||
|
|
||||||
setTimeout(() => this.hooks.runAction('action:admin-plugin-settings.init', 'admin-plugin', { npmName: this.npmName }))
|
this.hooks.runAction('action:admin-plugin-settings.init', 'admin-plugin', { npmName: this.npmName })
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSetting (name: string) {
|
private getSetting (name: string) {
|
||||||
|
|
|
@ -32,9 +32,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p-table
|
<p-table
|
||||||
[value]="jobs" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
|
[value]="jobs" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
|
||||||
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="uniqId" [first]="pagination.start"
|
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order"
|
||||||
[tableStyle]="{'table-layout':'auto'}"
|
(onLazyLoad)="loadLazy($event)" dataKey="uniqId" [first]="pagination.start" [tableStyle]="{'table-layout':'auto'}"
|
||||||
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
||||||
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} jobs"
|
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} jobs"
|
||||||
[expandedRowKeys]="expandedRows"
|
[expandedRowKeys]="expandedRows"
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
<ng-template pTemplate="header">
|
<ng-template pTemplate="header">
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width: 40px"></th>
|
<th style="width: 40px"></th>
|
||||||
<th style="width: calc(100% - 390px)" class="job-id" i18n>ID</th>
|
<th class="job-id" i18n>ID</th>
|
||||||
<th style="width: 200px" class="job-type" i18n>Type</th>
|
<th style="width: 200px" class="job-type" i18n>Type</th>
|
||||||
<th style="width: 200px" class="job-priority" i18n>Priority <small>(1 = highest priority)</small></th>
|
<th style="width: 200px" class="job-priority" i18n>Priority <small>(1 = highest priority)</small></th>
|
||||||
<th style="width: 200px" class="job-state" i18n *ngIf="jobState === 'all'">State</th>
|
<th style="width: 200px" class="job-state" i18n *ngIf="jobState === 'all'">State</th>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { LogLevel } from '@shared/models'
|
|
||||||
import omit from 'lodash-es/omit'
|
import omit from 'lodash-es/omit'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
|
import { ServerLogLevel } from '@shared/models'
|
||||||
|
|
||||||
export class LogRow {
|
export class LogRow {
|
||||||
date: Date
|
date: Date
|
||||||
localeDate: string
|
localeDate: string
|
||||||
level: LogLevel
|
level: ServerLogLevel
|
||||||
message: string
|
message: string
|
||||||
meta: string
|
meta: string
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ export class LogRow {
|
||||||
this.meta = JSON.stringify(message, null, 2)
|
this.meta = JSON.stringify(message, null, 2)
|
||||||
this.message = ''
|
this.message = ''
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Cannot parse audit message.', err)
|
logger.error('Cannot parse audit message.', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'
|
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'
|
||||||
import { LocalStorageService, Notifier } from '@app/core'
|
import { LocalStorageService, Notifier } from '@app/core'
|
||||||
import { LogLevel } from '@shared/models'
|
import { ServerLogLevel } from '@shared/models'
|
||||||
import { LogRow } from './log-row.model'
|
import { LogRow } from './log-row.model'
|
||||||
import { LogsService } from './logs.service'
|
import { LogsService } from './logs.service'
|
||||||
|
|
||||||
|
@ -17,11 +17,11 @@ export class LogsComponent implements OnInit {
|
||||||
|
|
||||||
logs: LogRow[] = []
|
logs: LogRow[] = []
|
||||||
timeChoices: { id: string, label: string, dateFormat: string }[] = []
|
timeChoices: { id: string, label: string, dateFormat: string }[] = []
|
||||||
levelChoices: { id: LogLevel, label: string }[] = []
|
levelChoices: { id: ServerLogLevel, label: string }[] = []
|
||||||
logTypeChoices: { id: 'audit' | 'standard', label: string }[] = []
|
logTypeChoices: { id: 'audit' | 'standard', label: string }[] = []
|
||||||
|
|
||||||
startDate: string
|
startDate: string
|
||||||
level: LogLevel
|
level: ServerLogLevel
|
||||||
logType: 'audit' | 'standard'
|
logType: 'audit' | 'standard'
|
||||||
tagsOneOf: string[] = []
|
tagsOneOf: string[] = []
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { catchError, map } from 'rxjs/operators'
|
||||||
import { HttpClient, HttpParams } from '@angular/common/http'
|
import { HttpClient, HttpParams } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { RestExtractor, RestService } from '@app/core'
|
import { RestExtractor, RestService } from '@app/core'
|
||||||
import { LogLevel } from '@shared/models'
|
import { ServerLogLevel } from '@shared/models'
|
||||||
import { environment } from '../../../../environments/environment'
|
import { environment } from '../../../../environments/environment'
|
||||||
import { LogRow } from './log-row.model'
|
import { LogRow } from './log-row.model'
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ export class LogsService {
|
||||||
isAuditLog: boolean
|
isAuditLog: boolean
|
||||||
startDate: string
|
startDate: string
|
||||||
tagsOneOf?: string[]
|
tagsOneOf?: string[]
|
||||||
level?: LogLevel
|
level?: ServerLogLevel
|
||||||
endDate?: string
|
endDate?: string
|
||||||
}): Observable<any[]> {
|
}): Observable<any[]> {
|
||||||
const { isAuditLog, startDate, endDate, tagsOneOf } = options
|
const { isAuditLog, startDate, endDate, tagsOneOf } = options
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { of } from 'rxjs'
|
import { of } from 'rxjs'
|
||||||
import { switchMap } from 'rxjs/operators'
|
import { switchMap } from 'rxjs/operators'
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { AfterViewInit, Component, OnInit } from '@angular/core'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import { AuthService, Notifier } from '@app/core'
|
import { AuthService, HooksService, Notifier } from '@app/core'
|
||||||
import {
|
import {
|
||||||
VIDEO_CHANNEL_DESCRIPTION_VALIDATOR,
|
VIDEO_CHANNEL_DESCRIPTION_VALIDATOR,
|
||||||
VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR,
|
VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR,
|
||||||
|
@ -18,7 +18,7 @@ import { VideoChannelEdit } from './video-channel-edit'
|
||||||
templateUrl: './video-channel-edit.component.html',
|
templateUrl: './video-channel-edit.component.html',
|
||||||
styleUrls: [ './video-channel-edit.component.scss' ]
|
styleUrls: [ './video-channel-edit.component.scss' ]
|
||||||
})
|
})
|
||||||
export class VideoChannelCreateComponent extends VideoChannelEdit implements OnInit {
|
export class VideoChannelCreateComponent extends VideoChannelEdit implements OnInit, AfterViewInit {
|
||||||
error: string
|
error: string
|
||||||
videoChannel = new VideoChannel({})
|
videoChannel = new VideoChannel({})
|
||||||
|
|
||||||
|
@ -30,7 +30,8 @@ export class VideoChannelCreateComponent extends VideoChannelEdit implements OnI
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private notifier: Notifier,
|
private notifier: Notifier,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private videoChannelService: VideoChannelService
|
private videoChannelService: VideoChannelService,
|
||||||
|
private hooks: HooksService
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
@ -44,6 +45,10 @@ export class VideoChannelCreateComponent extends VideoChannelEdit implements OnI
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit () {
|
||||||
|
this.hooks.runAction('action:video-channel-create.init', 'video-channel')
|
||||||
|
}
|
||||||
|
|
||||||
formValidated () {
|
formValidated () {
|
||||||
this.error = undefined
|
this.error = undefined
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
import { HttpErrorResponse } from '@angular/common/http'
|
import { HttpErrorResponse } from '@angular/common/http'
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute } from '@angular/router'
|
||||||
import { AuthService, Notifier, RedirectService, ServerService } from '@app/core'
|
import { AuthService, HooksService, Notifier, RedirectService, ServerService } from '@app/core'
|
||||||
import { genericUploadErrorHandler } from '@app/helpers'
|
import { genericUploadErrorHandler } from '@app/helpers'
|
||||||
import {
|
import {
|
||||||
VIDEO_CHANNEL_DESCRIPTION_VALIDATOR,
|
VIDEO_CHANNEL_DESCRIPTION_VALIDATOR,
|
||||||
|
@ -19,7 +19,7 @@ import { VideoChannelEdit } from './video-channel-edit'
|
||||||
templateUrl: './video-channel-edit.component.html',
|
templateUrl: './video-channel-edit.component.html',
|
||||||
styleUrls: [ './video-channel-edit.component.scss' ]
|
styleUrls: [ './video-channel-edit.component.scss' ]
|
||||||
})
|
})
|
||||||
export class VideoChannelUpdateComponent extends VideoChannelEdit implements OnInit, OnDestroy {
|
export class VideoChannelUpdateComponent extends VideoChannelEdit implements OnInit, AfterViewInit, OnDestroy {
|
||||||
error: string
|
error: string
|
||||||
videoChannel: VideoChannel
|
videoChannel: VideoChannel
|
||||||
|
|
||||||
|
@ -31,11 +31,11 @@ export class VideoChannelUpdateComponent extends VideoChannelEdit implements OnI
|
||||||
protected formValidatorService: FormValidatorService,
|
protected formValidatorService: FormValidatorService,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private notifier: Notifier,
|
private notifier: Notifier,
|
||||||
private router: Router,
|
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private videoChannelService: VideoChannelService,
|
private videoChannelService: VideoChannelService,
|
||||||
private serverService: ServerService,
|
private serverService: ServerService,
|
||||||
private redirectService: RedirectService
|
private redirectService: RedirectService,
|
||||||
|
private hooks: HooksService
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,8 @@ export class VideoChannelUpdateComponent extends VideoChannelEdit implements OnI
|
||||||
next: videoChannelToUpdate => {
|
next: videoChannelToUpdate => {
|
||||||
this.videoChannel = videoChannelToUpdate
|
this.videoChannel = videoChannelToUpdate
|
||||||
|
|
||||||
|
this.hooks.runAction('action:video-channel-update.video-channel.loaded', 'video-channel', { videoChannel: this.videoChannel })
|
||||||
|
|
||||||
this.oldSupportField = videoChannelToUpdate.support
|
this.oldSupportField = videoChannelToUpdate.support
|
||||||
|
|
||||||
this.form.patchValue({
|
this.form.patchValue({
|
||||||
|
@ -74,6 +76,10 @@ export class VideoChannelUpdateComponent extends VideoChannelEdit implements OnI
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit () {
|
||||||
|
this.hooks.runAction('action:video-channel-update.init', 'video-channel')
|
||||||
|
}
|
||||||
|
|
||||||
ngOnDestroy () {
|
ngOnDestroy () {
|
||||||
if (this.paramsSub) this.paramsSub.unsubscribe()
|
if (this.paramsSub) this.paramsSub.unsubscribe()
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p-table
|
<p-table
|
||||||
[value]="videoChangeOwnerships"
|
[value]="videoChangeOwnerships" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage"
|
||||||
[lazy]="true"
|
[first]="pagination.start" [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)"
|
||||||
[paginator]="totalRecords > 0"
|
|
||||||
[totalRecords]="totalRecords"
|
|
||||||
[rows]="rowsPerPage"
|
|
||||||
[sortField]="sort.field"
|
|
||||||
[sortOrder]="sort.order"
|
|
||||||
(onLazyLoad)="loadLazy($event)"
|
|
||||||
>
|
>
|
||||||
<ng-template pTemplate="header">
|
<ng-template pTemplate="header">
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p-table
|
<p-table
|
||||||
[value]="videoImports" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
|
[value]="videoImports" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
|
||||||
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
|
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
|
||||||
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
||||||
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} imports"
|
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} imports"
|
||||||
[expandedRowKeys]="expandedRows"
|
[expandedRowKeys]="expandedRows"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core'
|
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import { PluginService } from '@app/core'
|
import { PluginService } from '@app/core'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './plugin-pages.component.html'
|
templateUrl: './plugin-pages.component.html'
|
||||||
|
@ -26,7 +27,7 @@ export class PluginPagesComponent implements AfterViewInit {
|
||||||
|
|
||||||
const registered = this.pluginService.getRegisteredClientRoute(path)
|
const registered = this.pluginService.getRegisteredClientRoute(path)
|
||||||
if (!registered) {
|
if (!registered) {
|
||||||
console.log('Could not find registered route %s.', path, this.pluginService.getAllRegisteredClientRoutes())
|
logger.info(`Could not find registered route ${path}`, this.pluginService.getAllRegisteredClientRoutes())
|
||||||
|
|
||||||
return this.router.navigate([ '/404' ], { skipLocationChange: true })
|
return this.router.navigate([ '/404' ], { skipLocationChange: true })
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Observable } from 'rxjs'
|
import { Observable } from 'rxjs'
|
||||||
import { map } from 'rxjs/operators'
|
import { map } from 'rxjs/operators'
|
||||||
import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router'
|
import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { ResultList } from '@shared/models'
|
import { ResultList } from '@shared/models'
|
||||||
|
|
||||||
export abstract class AbstractLazyLoadResolver <T> implements Resolve<any> {
|
export abstract class AbstractLazyLoadResolver <T> implements Resolve<any> {
|
||||||
|
@ -10,7 +11,7 @@ export abstract class AbstractLazyLoadResolver <T> implements Resolve<any> {
|
||||||
const url = route.params.url
|
const url = route.params.url
|
||||||
|
|
||||||
if (!url) {
|
if (!url) {
|
||||||
console.error('Could not find url param.', { params: route.params })
|
logger.error('Could not find url param.', { params: route.params })
|
||||||
return this.router.navigateByUrl('/404')
|
return this.router.navigateByUrl('/404')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ export abstract class AbstractLazyLoadResolver <T> implements Resolve<any> {
|
||||||
.pipe(
|
.pipe(
|
||||||
map(result => {
|
map(result => {
|
||||||
if (result.data.length !== 1) {
|
if (result.data.length !== 1) {
|
||||||
console.error('Cannot find result for this URL')
|
logger.error('Cannot find result for this URL')
|
||||||
return this.router.navigateByUrl('/404')
|
return this.router.navigateByUrl('/404')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Subject, Subscription } from 'rxjs'
|
import { Subject, Subscription } from 'rxjs'
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'
|
||||||
import { ComponentPagination, hasMoreItems, ScreenService } from '@app/core'
|
import { ComponentPagination, hasMoreItems, HooksService, ScreenService } from '@app/core'
|
||||||
import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
|
import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
|
||||||
import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
|
import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-pl
|
||||||
templateUrl: './video-channel-playlists.component.html',
|
templateUrl: './video-channel-playlists.component.html',
|
||||||
styleUrls: [ './video-channel-playlists.component.scss' ]
|
styleUrls: [ './video-channel-playlists.component.scss' ]
|
||||||
})
|
})
|
||||||
export class VideoChannelPlaylistsComponent implements OnInit, OnDestroy {
|
export class VideoChannelPlaylistsComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
videoPlaylists: VideoPlaylist[] = []
|
videoPlaylists: VideoPlaylist[] = []
|
||||||
|
|
||||||
pagination: ComponentPagination = {
|
pagination: ComponentPagination = {
|
||||||
|
@ -26,7 +26,8 @@ export class VideoChannelPlaylistsComponent implements OnInit, OnDestroy {
|
||||||
constructor (
|
constructor (
|
||||||
private videoPlaylistService: VideoPlaylistService,
|
private videoPlaylistService: VideoPlaylistService,
|
||||||
private videoChannelService: VideoChannelService,
|
private videoChannelService: VideoChannelService,
|
||||||
private screenService: ScreenService
|
private screenService: ScreenService,
|
||||||
|
private hooks: HooksService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
|
@ -34,10 +35,17 @@ export class VideoChannelPlaylistsComponent implements OnInit, OnDestroy {
|
||||||
this.videoChannelSub = this.videoChannelService.videoChannelLoaded
|
this.videoChannelSub = this.videoChannelService.videoChannelLoaded
|
||||||
.subscribe(videoChannel => {
|
.subscribe(videoChannel => {
|
||||||
this.videoChannel = videoChannel
|
this.videoChannel = videoChannel
|
||||||
|
|
||||||
|
this.hooks.runAction('action:video-channel-playlists.video-channel.loaded', 'video-channel', { videoChannel })
|
||||||
|
|
||||||
this.loadVideoPlaylists()
|
this.loadVideoPlaylists()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit () {
|
||||||
|
this.hooks.runAction('action:video-channel-playlists.init', 'video-channel')
|
||||||
|
}
|
||||||
|
|
||||||
ngOnDestroy () {
|
ngOnDestroy () {
|
||||||
if (this.videoChannelSub) this.videoChannelSub.unsubscribe()
|
if (this.videoChannelSub) this.videoChannelSub.unsubscribe()
|
||||||
}
|
}
|
||||||
|
@ -59,6 +67,8 @@ export class VideoChannelPlaylistsComponent implements OnInit, OnDestroy {
|
||||||
this.videoPlaylists = this.videoPlaylists.concat(res.data)
|
this.videoPlaylists = this.videoPlaylists.concat(res.data)
|
||||||
this.pagination.totalItems = res.total
|
this.pagination.totalItems = res.total
|
||||||
|
|
||||||
|
this.hooks.runAction('action:video-channel-playlists.playlists.loaded', 'video-channel', { playlists: this.videoPlaylists })
|
||||||
|
|
||||||
this.onDataSubject.next(res.data)
|
this.onDataSubject.next(res.data)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,5 +19,7 @@
|
||||||
[loadUserVideoPreferences]="true"
|
[loadUserVideoPreferences]="true"
|
||||||
|
|
||||||
[disabled]="disabled"
|
[disabled]="disabled"
|
||||||
|
|
||||||
|
(videosLoaded)="onVideosLoaded($event)"
|
||||||
>
|
>
|
||||||
</my-videos-list>
|
</my-videos-list>
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
import { first } from 'rxjs/operators'
|
import { first } from 'rxjs/operators'
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'
|
||||||
import { ComponentPaginationLight, DisableForReuseHook, ScreenService } from '@app/core'
|
import { ComponentPaginationLight, DisableForReuseHook, HooksService, ScreenService } from '@app/core'
|
||||||
import { VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
|
import { VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
|
||||||
import { MiniatureDisplayOptions, VideoFilters } from '@app/shared/shared-video-miniature'
|
import { MiniatureDisplayOptions, VideoFilters } from '@app/shared/shared-video-miniature'
|
||||||
import { VideoSortField } from '@shared/models/videos'
|
import { Video, VideoSortField } from '@shared/models'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-channel-videos',
|
selector: 'my-video-channel-videos',
|
||||||
templateUrl: './video-channel-videos.component.html'
|
templateUrl: './video-channel-videos.component.html'
|
||||||
})
|
})
|
||||||
export class VideoChannelVideosComponent implements OnInit, OnDestroy, DisableForReuseHook {
|
export class VideoChannelVideosComponent implements OnInit, AfterViewInit, OnDestroy, DisableForReuseHook {
|
||||||
getVideosObservableFunction = this.getVideosObservable.bind(this)
|
getVideosObservableFunction = this.getVideosObservable.bind(this)
|
||||||
getSyndicationItemsFunction = this.getSyndicationItems.bind(this)
|
getSyndicationItemsFunction = this.getSyndicationItems.bind(this)
|
||||||
|
|
||||||
|
@ -36,7 +36,8 @@ export class VideoChannelVideosComponent implements OnInit, OnDestroy, DisableFo
|
||||||
constructor (
|
constructor (
|
||||||
private screenService: ScreenService,
|
private screenService: ScreenService,
|
||||||
private videoChannelService: VideoChannelService,
|
private videoChannelService: VideoChannelService,
|
||||||
private videoService: VideoService
|
private videoService: VideoService,
|
||||||
|
private hooks: HooksService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,9 +46,15 @@ export class VideoChannelVideosComponent implements OnInit, OnDestroy, DisableFo
|
||||||
this.videoChannelService.videoChannelLoaded.pipe(first())
|
this.videoChannelService.videoChannelLoaded.pipe(first())
|
||||||
.subscribe(videoChannel => {
|
.subscribe(videoChannel => {
|
||||||
this.videoChannel = videoChannel
|
this.videoChannel = videoChannel
|
||||||
|
|
||||||
|
this.hooks.runAction('action:video-channel-videos.video-channel.loaded', 'video-channel', { videoChannel })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit () {
|
||||||
|
this.hooks.runAction('action:video-channel-videos.init', 'video-channel')
|
||||||
|
}
|
||||||
|
|
||||||
ngOnDestroy () {
|
ngOnDestroy () {
|
||||||
if (this.videoChannelSub) this.videoChannelSub.unsubscribe()
|
if (this.videoChannelSub) this.videoChannelSub.unsubscribe()
|
||||||
}
|
}
|
||||||
|
@ -79,4 +86,8 @@ export class VideoChannelVideosComponent implements OnInit, OnDestroy, DisableFo
|
||||||
enabledForReuse () {
|
enabledForReuse () {
|
||||||
this.disabled = false
|
this.disabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onVideosLoaded (videos: Video[]) {
|
||||||
|
this.hooks.runAction('action:video-channel-videos.videos.loaded', 'video-channel', { videos })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { ConfirmService, Notifier, ServerService } from '@app/core'
|
||||||
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
||||||
import { VideoDetails } from '@app/shared/shared-main'
|
import { VideoDetails } from '@app/shared/shared-main'
|
||||||
import { LoadingBarService } from '@ngx-loading-bar/core'
|
import { LoadingBarService } from '@ngx-loading-bar/core'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { secondsToTime } from '@shared/core-utils'
|
import { secondsToTime } from '@shared/core-utils'
|
||||||
import { VideoStudioTask, VideoStudioTaskCut } from '@shared/models'
|
import { VideoStudioTask, VideoStudioTaskCut } from '@shared/models'
|
||||||
import { VideoStudioService } from '../shared'
|
import { VideoStudioService } from '../shared'
|
||||||
|
@ -97,7 +98,7 @@ export class VideoStudioEditComponent extends FormReactive implements OnInit {
|
||||||
this.loadingBar.useRef().complete()
|
this.loadingBar.useRef().complete()
|
||||||
this.isRunningEdition = false
|
this.isRunningEdition = false
|
||||||
this.notifier.error(err.message)
|
this.notifier.error(err.message)
|
||||||
console.error(err)
|
logger.error(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,7 +183,7 @@
|
||||||
[href]="videoCaption.captionPath"
|
[href]="videoCaption.captionPath"
|
||||||
>{{ videoCaption.language.label }}</a>
|
>{{ videoCaption.language.label }}</a>
|
||||||
|
|
||||||
<div i18n class="caption-entry-state">Already uploaded ✔</div>
|
<div i18n class="caption-entry-state">Already uploaded on {{ videoCaption.updatedAt | date }} ✔</div>
|
||||||
|
|
||||||
<span i18n class="caption-entry-edit" (click)="videoCaptionEditModal.show()">Edit</span>
|
<span i18n class="caption-entry-edit" (click)="videoCaptionEditModal.show()">Edit</span>
|
||||||
<span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Delete</span>
|
<span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Delete</span>
|
||||||
|
|
|
@ -41,7 +41,6 @@ my-peertube-checkbox {
|
||||||
a.caption-entry-label {
|
a.caption-entry-label {
|
||||||
@include disable-default-a-behaviour;
|
@include disable-default-a-behaviour;
|
||||||
|
|
||||||
flex-grow: 1;
|
|
||||||
color: #000;
|
color: #000;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -53,11 +52,13 @@ my-peertube-checkbox {
|
||||||
@include margin-right(20px);
|
@include margin-right(20px);
|
||||||
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
width: 150px;
|
min-width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.caption-entry-state {
|
.caption-entry-state {
|
||||||
width: 200px;
|
@include margin-right(15px);
|
||||||
|
|
||||||
|
min-width: 250px;
|
||||||
|
|
||||||
&.caption-entry-state-create {
|
&.caption-entry-state-create {
|
||||||
color: #39CC0B;
|
color: #39CC0B;
|
||||||
|
|
|
@ -38,6 +38,7 @@ import { VideoCaptionAddModalComponent } from './video-caption-add-modal.compone
|
||||||
import { VideoCaptionEditModalComponent } from './video-caption-edit-modal/video-caption-edit-modal.component'
|
import { VideoCaptionEditModalComponent } from './video-caption-edit-modal/video-caption-edit-modal.component'
|
||||||
import { VideoEditType } from './video-edit.type'
|
import { VideoEditType } from './video-edit.type'
|
||||||
import { VideoSource } from '@shared/models/videos/video-source'
|
import { VideoSource } from '@shared/models/videos/video-source'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
|
|
||||||
type VideoLanguages = VideoConstant<string> & { group?: string }
|
type VideoLanguages = VideoConstant<string> & { group?: string }
|
||||||
type PluginField = {
|
type PluginField = {
|
||||||
|
@ -443,7 +444,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
const oldChannel = this.userVideoChannels.find(c => c.id === oldChannelId)
|
const oldChannel = this.userVideoChannels.find(c => c.id === oldChannelId)
|
||||||
if (!newChannel || !oldChannel) {
|
if (!newChannel || !oldChannel) {
|
||||||
console.error('Cannot find new or old channel.')
|
logger.error('Cannot find new or old channel.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { FormValidatorService } from '@app/shared/shared-forms'
|
||||||
import { Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
|
import { Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
|
||||||
import { LiveVideoService } from '@app/shared/shared-video-live'
|
import { LiveVideoService } from '@app/shared/shared-video-live'
|
||||||
import { LoadingBarService } from '@ngx-loading-bar/core'
|
import { LoadingBarService } from '@ngx-loading-bar/core'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { LiveVideo, LiveVideoCreate, LiveVideoLatencyMode, LiveVideoUpdate, PeerTubeProblemDocument, ServerErrorCode } from '@shared/models'
|
import { LiveVideo, LiveVideoCreate, LiveVideoLatencyMode, LiveVideoUpdate, PeerTubeProblemDocument, ServerErrorCode } from '@shared/models'
|
||||||
import { VideoSend } from './video-send'
|
import { VideoSend } from './video-send'
|
||||||
|
|
||||||
|
@ -141,7 +142,7 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterView
|
||||||
error: err => {
|
error: err => {
|
||||||
this.error = err.message
|
this.error = err.message
|
||||||
scrollToTop()
|
scrollToTop()
|
||||||
console.error(err)
|
logger.error(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { scrollToTop } from '@app/helpers'
|
||||||
import { FormValidatorService } from '@app/shared/shared-forms'
|
import { FormValidatorService } from '@app/shared/shared-forms'
|
||||||
import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main'
|
import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main'
|
||||||
import { LoadingBarService } from '@ngx-loading-bar/core'
|
import { LoadingBarService } from '@ngx-loading-bar/core'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { PeerTubeProblemDocument, ServerErrorCode, VideoUpdate } from '@shared/models'
|
import { PeerTubeProblemDocument, ServerErrorCode, VideoUpdate } from '@shared/models'
|
||||||
import { hydrateFormFromVideo } from '../shared/video-edit-utils'
|
import { hydrateFormFromVideo } from '../shared/video-edit-utils'
|
||||||
import { VideoSend } from './video-send'
|
import { VideoSend } from './video-send'
|
||||||
|
@ -139,7 +140,7 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Af
|
||||||
error: err => {
|
error: err => {
|
||||||
this.error = err.message
|
this.error = err.message
|
||||||
scrollToTop()
|
scrollToTop()
|
||||||
console.error(err)
|
logger.error(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { scrollToTop } from '@app/helpers'
|
||||||
import { FormValidatorService } from '@app/shared/shared-forms'
|
import { FormValidatorService } from '@app/shared/shared-forms'
|
||||||
import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main'
|
import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main'
|
||||||
import { LoadingBarService } from '@ngx-loading-bar/core'
|
import { LoadingBarService } from '@ngx-loading-bar/core'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { VideoUpdate } from '@shared/models'
|
import { VideoUpdate } from '@shared/models'
|
||||||
import { hydrateFormFromVideo } from '../shared/video-edit-utils'
|
import { hydrateFormFromVideo } from '../shared/video-edit-utils'
|
||||||
import { VideoSend } from './video-send'
|
import { VideoSend } from './video-send'
|
||||||
|
@ -128,7 +129,7 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, AfterV
|
||||||
error: err => {
|
error: err => {
|
||||||
this.error = err.message
|
this.error = err.message
|
||||||
scrollToTop()
|
scrollToTop()
|
||||||
console.error(err)
|
logger.error(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { truncate } from 'lodash-es'
|
import { truncate } from 'lodash-es'
|
||||||
import { UploadState, UploadxOptions, UploadxService } from 'ngx-uploadx'
|
import { UploadState, UploadxOptions, UploadxService } from 'ngx-uploadx'
|
||||||
import { isIOS } from '@root-helpers/web-browser'
|
|
||||||
import { HttpErrorResponse, HttpEventType, HttpHeaders } from '@angular/common/http'
|
import { HttpErrorResponse, HttpEventType, HttpHeaders } from '@angular/common/http'
|
||||||
import { AfterViewInit, Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'
|
import { AfterViewInit, Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
|
@ -9,6 +8,8 @@ import { genericUploadErrorHandler, scrollToTop } from '@app/helpers'
|
||||||
import { FormValidatorService } from '@app/shared/shared-forms'
|
import { FormValidatorService } from '@app/shared/shared-forms'
|
||||||
import { BytesPipe, Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
|
import { BytesPipe, Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
|
||||||
import { LoadingBarService } from '@ngx-loading-bar/core'
|
import { LoadingBarService } from '@ngx-loading-bar/core'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
|
import { isIOS } from '@root-helpers/web-browser'
|
||||||
import { HttpStatusCode, VideoCreateResult } from '@shared/models'
|
import { HttpStatusCode, VideoCreateResult } from '@shared/models'
|
||||||
import { UploaderXFormData } from './uploaderx-form-data'
|
import { UploaderXFormData } from './uploaderx-form-data'
|
||||||
import { VideoSend } from './video-send'
|
import { VideoSend } from './video-send'
|
||||||
|
@ -264,7 +265,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
|
||||||
error: err => {
|
error: err => {
|
||||||
this.error = err.message
|
this.error = err.message
|
||||||
scrollToTop()
|
scrollToTop()
|
||||||
console.error(err)
|
logger.error(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,10 @@ import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
||||||
import { Video, VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main'
|
import { Video, VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main'
|
||||||
import { LiveVideoService } from '@app/shared/shared-video-live'
|
import { LiveVideoService } from '@app/shared/shared-video-live'
|
||||||
import { LoadingBarService } from '@ngx-loading-bar/core'
|
import { LoadingBarService } from '@ngx-loading-bar/core'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { LiveVideo, LiveVideoUpdate, VideoPrivacy } from '@shared/models'
|
import { LiveVideo, LiveVideoUpdate, VideoPrivacy } from '@shared/models'
|
||||||
import { hydrateFormFromVideo } from './shared/video-edit-utils'
|
|
||||||
import { VideoSource } from '@shared/models/videos/video-source'
|
import { VideoSource } from '@shared/models/videos/video-source'
|
||||||
|
import { hydrateFormFromVideo } from './shared/video-edit-utils'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-videos-update',
|
selector: 'my-videos-update',
|
||||||
|
@ -156,7 +157,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
|
||||||
this.loadingBar.useRef().complete()
|
this.loadingBar.useRef().complete()
|
||||||
this.isUpdatingVideo = false
|
this.isUpdatingVideo = false
|
||||||
this.notifier.error(err.message)
|
this.notifier.error(err.message)
|
||||||
console.error(err)
|
logger.error(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { AuthService, ComponentPagination, ConfirmService, hasMoreItems, Notifie
|
||||||
import { HooksService } from '@app/core/plugins/hooks.service'
|
import { HooksService } from '@app/core/plugins/hooks.service'
|
||||||
import { Syndication, VideoDetails } from '@app/shared/shared-main'
|
import { Syndication, VideoDetails } from '@app/shared/shared-main'
|
||||||
import { VideoComment, VideoCommentService, VideoCommentThreadTree } from '@app/shared/shared-video-comment'
|
import { VideoComment, VideoCommentService, VideoCommentThreadTree } from '@app/shared/shared-video-comment'
|
||||||
|
import { PeerTubeProblemDocument, ServerErrorCode } from '@shared/models'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-comments',
|
selector: 'my-video-comments',
|
||||||
|
@ -104,7 +105,14 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
error: err => this.notifier.error(err.message)
|
error: err => {
|
||||||
|
// We may try to fetch highlighted thread of another video, skip the error if it is the case
|
||||||
|
// We'll retry the request on video Input() change
|
||||||
|
const errorBody = err.body as PeerTubeProblemDocument
|
||||||
|
if (highlightThread && errorBody?.code === ServerErrorCode.COMMENT_NOT_ASSOCIATED_TO_VIDEO) return
|
||||||
|
|
||||||
|
this.notifier.error(err.message)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +138,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
this.totalNotDeletedComments = res.totalNotDeletedComments
|
this.totalNotDeletedComments = res.totalNotDeletedComments
|
||||||
|
|
||||||
this.onDataSubject.next(res.data)
|
this.onDataSubject.next(res.data)
|
||||||
|
|
||||||
this.hooks.runAction('action:video-watch.video-threads.loaded', 'video-watch', { data: this.componentPagination })
|
this.hooks.runAction('action:video-watch.video-threads.loaded', 'video-watch', { data: this.componentPagination })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -253,6 +262,10 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
|
|
||||||
this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video)
|
this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video)
|
||||||
this.loadMoreThreads()
|
this.loadMoreThreads()
|
||||||
|
|
||||||
|
if (this.activatedRoute.params['threadId']) {
|
||||||
|
this.processHighlightedThread(+this.activatedRoute.params['threadId'])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'
|
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'
|
||||||
import { MarkdownService, Notifier } from '@app/core'
|
import { MarkdownService, Notifier } from '@app/core'
|
||||||
import { VideoDetails, VideoService } from '@app/shared/shared-main'
|
import { VideoDetails, VideoService } from '@app/shared/shared-main'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-description',
|
selector: 'my-video-description',
|
||||||
|
@ -75,7 +76,7 @@ export class VideoDescriptionComponent implements OnChanges {
|
||||||
private updateVideoDescription (description: string) {
|
private updateVideoDescription (description: string) {
|
||||||
this.video.description = description
|
this.video.description = description
|
||||||
this.setVideoDescriptionHTML()
|
this.setVideoDescriptionHTML()
|
||||||
.catch(err => console.error(err))
|
.catch(err => logger.error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setVideoDescriptionHTML () {
|
private async setVideoDescriptionHTML () {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/sha
|
||||||
import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
|
import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
|
||||||
import { LiveVideoService } from '@app/shared/shared-video-live'
|
import { LiveVideoService } from '@app/shared/shared-video-live'
|
||||||
import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
|
import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { isP2PEnabled } from '@root-helpers/video'
|
import { isP2PEnabled } from '@root-helpers/video'
|
||||||
import { timeToInt } from '@shared/core-utils'
|
import { timeToInt } from '@shared/core-utils'
|
||||||
import {
|
import {
|
||||||
|
@ -225,7 +226,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
: parseInt(positionParam + '', 10)
|
: parseInt(positionParam + '', 10)
|
||||||
|
|
||||||
if (isNaN(this.playlistPosition)) {
|
if (isNaN(this.playlistPosition)) {
|
||||||
console.error(`playlistPosition query param '${positionParam}' was parsed as NaN, defaulting to 1.`)
|
logger.error(`playlistPosition query param '${positionParam}' was parsed as NaN, defaulting to 1.`)
|
||||||
this.playlistPosition = 1
|
this.playlistPosition = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,6 +242,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
if (this.player) this.player.pause()
|
if (this.player) this.player.pause()
|
||||||
|
|
||||||
|
this.video = undefined
|
||||||
|
|
||||||
const videoObs = this.hooks.wrapObsFun(
|
const videoObs = this.hooks.wrapObsFun(
|
||||||
this.videoService.getVideo.bind(this.videoService),
|
this.videoService.getVideo.bind(this.videoService),
|
||||||
{ videoId },
|
{ videoId },
|
||||||
|
@ -378,7 +381,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.buildPlayer(urlOptions, loggedInOrAnonymousUser)
|
this.buildPlayer(urlOptions, loggedInOrAnonymousUser)
|
||||||
.catch(err => console.error('Cannot build the player', err))
|
.catch(err => logger.error('Cannot build the player', err))
|
||||||
|
|
||||||
this.setOpenGraphTags()
|
this.setOpenGraphTags()
|
||||||
|
|
||||||
|
@ -550,7 +553,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
this.player.dispose()
|
this.player.dispose()
|
||||||
this.player = undefined
|
this.player = undefined
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Cannot dispose player.', err)
|
logger.error('Cannot dispose player.', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -717,7 +720,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
private handleLiveStateChange (newState: VideoState) {
|
private handleLiveStateChange (newState: VideoState) {
|
||||||
if (newState !== VideoState.PUBLISHED) return
|
if (newState !== VideoState.PUBLISHED) return
|
||||||
|
|
||||||
console.log('Loading video after live update.')
|
logger.info('Loading video after live update.')
|
||||||
|
|
||||||
const videoUUID = this.video.uuid
|
const videoUUID = this.video.uuid
|
||||||
|
|
||||||
|
@ -728,11 +731,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
private handleLiveViewsChange (newViewers: number) {
|
private handleLiveViewsChange (newViewers: number) {
|
||||||
if (!this.video) {
|
if (!this.video) {
|
||||||
console.error('Cannot update video live views because video is no defined.')
|
logger.error('Cannot update video live views because video is no defined.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Updating live views.')
|
logger.info('Updating live views.')
|
||||||
|
|
||||||
this.video.viewers = newViewers
|
this.video.viewers = newViewers
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<my-hotkeys-cheatsheet></my-hotkeys-cheatsheet>
|
<my-hotkeys-cheatsheet></my-hotkeys-cheatsheet>
|
||||||
|
|
||||||
<div class="peertube-container" [ngClass]="{ 'user-logged-in': isUserLoggedIn(), 'user-not-logged-in': !isUserLoggedIn() }">
|
<div class="peertube-container" [ngClass]="{ 'user-logged-in': isUserLoggedIn(), 'user-not-logged-in': !isUserLoggedIn() }">
|
||||||
<div class="header">
|
<div class="root-header">
|
||||||
|
|
||||||
<div class="top-left-block">
|
<div class="top-left-block">
|
||||||
<span class="icon icon-menu" role="button" [title]="getToggleTitle()" (click)="menu.toggleMenu()"></span>
|
<span class="icon icon-menu" role="button" [title]="getToggleTitle()" (click)="menu.toggleMenu()"></span>
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header-right">
|
<div class="root-header-right">
|
||||||
<my-header class="w-100 d-flex justify-content-end"></my-header>
|
<my-header class="w-100 d-flex justify-content-end"></my-header>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.root-header {
|
||||||
height: $header-height;
|
height: $header-height;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-right {
|
.root-header-right {
|
||||||
height: $header-height;
|
height: $header-height;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
|
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
|
||||||
import { forkJoin, delay } from 'rxjs'
|
import { delay, forkJoin } from 'rxjs'
|
||||||
import { filter, first, map } from 'rxjs/operators'
|
import { filter, first, map } from 'rxjs/operators'
|
||||||
import { DOCUMENT, getLocaleDirection, PlatformLocation } from '@angular/common'
|
import { DOCUMENT, getLocaleDirection, PlatformLocation } from '@angular/common'
|
||||||
import { AfterViewInit, Component, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core'
|
import { AfterViewInit, Component, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core'
|
||||||
|
@ -20,18 +20,19 @@ import {
|
||||||
import { HooksService } from '@app/core/plugins/hooks.service'
|
import { HooksService } from '@app/core/plugins/hooks.service'
|
||||||
import { PluginService } from '@app/core/plugins/plugin.service'
|
import { PluginService } from '@app/core/plugins/plugin.service'
|
||||||
import { AccountSetupWarningModalComponent } from '@app/modal/account-setup-warning-modal.component'
|
import { AccountSetupWarningModalComponent } from '@app/modal/account-setup-warning-modal.component'
|
||||||
|
import { AdminWelcomeModalComponent } from '@app/modal/admin-welcome-modal.component'
|
||||||
import { CustomModalComponent } from '@app/modal/custom-modal.component'
|
import { CustomModalComponent } from '@app/modal/custom-modal.component'
|
||||||
import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component'
|
import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component'
|
||||||
import { AdminWelcomeModalComponent } from '@app/modal/admin-welcome-modal.component'
|
|
||||||
import { NgbConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { LoadingBarService } from '@ngx-loading-bar/core'
|
import { LoadingBarService } from '@ngx-loading-bar/core'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
|
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
|
||||||
import { getShortLocale } from '@shared/core-utils/i18n'
|
import { getShortLocale } from '@shared/core-utils/i18n'
|
||||||
import { BroadcastMessageLevel, HTMLServerConfig, UserRole } from '@shared/models'
|
import { BroadcastMessageLevel, HTMLServerConfig, UserRole } from '@shared/models'
|
||||||
import { MenuService } from './core/menu/menu.service'
|
import { MenuService } from './core/menu/menu.service'
|
||||||
import { POP_STATE_MODAL_DISMISS } from './helpers'
|
import { POP_STATE_MODAL_DISMISS } from './helpers'
|
||||||
import { InstanceService } from './shared/shared-instance'
|
|
||||||
import { GlobalIconName } from './shared/shared-icons'
|
import { GlobalIconName } from './shared/shared-icons'
|
||||||
|
import { InstanceService } from './shared/shared-instance'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-app',
|
selector: 'my-app',
|
||||||
|
@ -221,7 +222,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||||
/* eslint-disable no-eval */
|
/* eslint-disable no-eval */
|
||||||
eval(this.serverConfig.instance.customizations.javascript)
|
eval(this.serverConfig.instance.customizations.javascript)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Cannot eval custom JavaScript.', err)
|
logger.error('Cannot eval custom JavaScript.', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import { Notifier } from '@app/core/notification/notifier.service'
|
import { Notifier } from '@app/core/notification/notifier.service'
|
||||||
import { objectToUrlEncoded, peertubeLocalStorage, UserTokens } from '@root-helpers/index'
|
import { logger, objectToUrlEncoded, peertubeLocalStorage, UserTokens } from '@root-helpers/index'
|
||||||
import { HttpStatusCode, MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models'
|
import { HttpStatusCode, MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
import { RestExtractor } from '../rest/rest-extractor.service'
|
import { RestExtractor } from '../rest/rest-extractor.service'
|
||||||
|
@ -90,7 +90,7 @@ export class AuthService {
|
||||||
peertubeLocalStorage.setItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID, this.clientId)
|
peertubeLocalStorage.setItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID, this.clientId)
|
||||||
peertubeLocalStorage.setItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_SECRET, this.clientSecret)
|
peertubeLocalStorage.setItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_SECRET, this.clientSecret)
|
||||||
|
|
||||||
console.log('Client credentials loaded.')
|
logger.info('Client credentials loaded.')
|
||||||
},
|
},
|
||||||
|
|
||||||
error: err => {
|
error: err => {
|
||||||
|
@ -177,7 +177,7 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
error: err => console.error(err)
|
error: err => logger.error(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.user = null
|
this.user = null
|
||||||
|
@ -190,7 +190,7 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
|
||||||
refreshAccessToken () {
|
refreshAccessToken () {
|
||||||
if (this.refreshingTokenObservable) return this.refreshingTokenObservable
|
if (this.refreshingTokenObservable) return this.refreshingTokenObservable
|
||||||
|
|
||||||
console.log('Refreshing token...')
|
logger.info('Refreshing token...')
|
||||||
|
|
||||||
const refreshToken = this.getRefreshToken()
|
const refreshToken = this.getRefreshToken()
|
||||||
|
|
||||||
|
@ -212,8 +212,8 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
|
||||||
catchError(err => {
|
catchError(err => {
|
||||||
this.refreshingTokenObservable = null
|
this.refreshingTokenObservable = null
|
||||||
|
|
||||||
console.error(err)
|
logger.error(err)
|
||||||
console.log('Cannot refresh token -> logout...')
|
logger.info('Cannot refresh token -> logout...')
|
||||||
this.logout()
|
this.logout()
|
||||||
this.router.navigate([ '/login' ])
|
this.router.navigate([ '/login' ])
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { MessageService } from 'primeng/api'
|
import { MessageService } from 'primeng/api'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Notifier {
|
export class Notifier {
|
||||||
|
@ -10,21 +11,21 @@ export class Notifier {
|
||||||
info (text: string, title?: string, timeout?: number, sticky?: boolean) {
|
info (text: string, title?: string, timeout?: number, sticky?: boolean) {
|
||||||
if (!title) title = $localize`Info`
|
if (!title) title = $localize`Info`
|
||||||
|
|
||||||
console.info(`${title}: ${text}`)
|
logger.info(`${title}: ${text}`)
|
||||||
return this.notify('info', text, title, timeout, sticky)
|
return this.notify('info', text, title, timeout, sticky)
|
||||||
}
|
}
|
||||||
|
|
||||||
error (text: string, title?: string, timeout?: number, sticky?: boolean) {
|
error (text: string, title?: string, timeout?: number, sticky?: boolean) {
|
||||||
if (!title) title = $localize`Error`
|
if (!title) title = $localize`Error`
|
||||||
|
|
||||||
console.error(`${title}: ${text}`)
|
logger.error(`${title}: ${text}`)
|
||||||
return this.notify('error', text, title, timeout, sticky)
|
return this.notify('error', text, title, timeout, sticky)
|
||||||
}
|
}
|
||||||
|
|
||||||
success (text: string, title?: string, timeout?: number, sticky?: boolean) {
|
success (text: string, title?: string, timeout?: number, sticky?: boolean) {
|
||||||
if (!title) title = $localize`Success`
|
if (!title) title = $localize`Success`
|
||||||
|
|
||||||
console.log(`${title}: ${text}`)
|
logger.info(`${title}: ${text}`)
|
||||||
return this.notify('success', text, title, timeout, sticky)
|
return this.notify('success', text, title, timeout, sticky)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { from, Observable } from 'rxjs'
|
||||||
import { mergeMap, switchMap } from 'rxjs/operators'
|
import { mergeMap, switchMap } from 'rxjs/operators'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { PluginService } from '@app/core/plugins/plugin.service'
|
import { PluginService } from '@app/core/plugins/plugin.service'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { ClientActionHookName, ClientFilterHookName, PluginClientScope } from '@shared/models'
|
import { ClientActionHookName, ClientFilterHookName, PluginClientScope } from '@shared/models'
|
||||||
import { AuthService, AuthStatus } from '../auth'
|
import { AuthService, AuthStatus } from '../auth'
|
||||||
|
|
||||||
|
@ -48,9 +49,12 @@ export class HooksService {
|
||||||
}
|
}
|
||||||
|
|
||||||
runAction<T, U extends ClientActionHookName> (hookName: U, scope: PluginClientScope, params?: T) {
|
runAction<T, U extends ClientActionHookName> (hookName: U, scope: PluginClientScope, params?: T) {
|
||||||
|
// Use setTimeout to give priority to Angular change detector
|
||||||
|
setTimeout(() => {
|
||||||
this.pluginService.ensurePluginsAreLoaded(scope)
|
this.pluginService.ensurePluginsAreLoaded(scope)
|
||||||
.then(() => this.pluginService.runHook(hookName, undefined, params))
|
.then(() => this.pluginService.runHook(hookName, undefined, params))
|
||||||
.catch((err: any) => console.error('Fatal hook error.', { err }))
|
.catch((err: any) => logger.error('Fatal hook error.', err))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async wrapObject<T, U extends ClientFilterHookName> (result: T, scope: PluginClientScope, hookName: U) {
|
async wrapObject<T, U extends ClientFilterHookName> (result: T, scope: PluginClientScope, hookName: U) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Injectable } from '@angular/core'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import { dateToHuman } from '@app/helpers'
|
import { dateToHuman } from '@app/helpers'
|
||||||
import { HttpStatusCode, ResultList } from '@shared/models'
|
import { HttpStatusCode, ResultList } from '@shared/models'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RestExtractor {
|
export class RestExtractor {
|
||||||
|
@ -64,7 +65,7 @@ export class RestExtractor {
|
||||||
if (err.error instanceof Error) {
|
if (err.error instanceof Error) {
|
||||||
// A client-side or network error occurred. Handle it accordingly.
|
// A client-side or network error occurred. Handle it accordingly.
|
||||||
const errorMessage = err.error.detail || err.error.title
|
const errorMessage = err.error.detail || err.error.title
|
||||||
console.error('An error occurred:', errorMessage)
|
logger.error('An error occurred:', errorMessage)
|
||||||
|
|
||||||
return errorMessage
|
return errorMessage
|
||||||
}
|
}
|
||||||
|
@ -75,12 +76,12 @@ export class RestExtractor {
|
||||||
|
|
||||||
if (err.status !== undefined) {
|
if (err.status !== undefined) {
|
||||||
const errorMessage = this.buildServerErrorMessage(err)
|
const errorMessage = this.buildServerErrorMessage(err)
|
||||||
console.error(`Backend returned code ${err.status}, errorMessage is: ${errorMessage}`)
|
logger.error(`Backend returned code ${err.status}, errorMessage is: ${errorMessage}`)
|
||||||
|
|
||||||
return errorMessage
|
return errorMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(err)
|
logger.error(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import * as debug from 'debug'
|
import debug from 'debug'
|
||||||
import { LazyLoadEvent, SortMeta } from 'primeng/api'
|
import { LazyLoadEvent, SortMeta } from 'primeng/api'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
|
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
|
||||||
import { RestPagination } from './rest-pagination'
|
import { RestPagination } from './rest-pagination'
|
||||||
|
|
||||||
const logger = debug('peertube:tables:RestTable')
|
const debugLogger = debug('peertube:tables:RestTable')
|
||||||
|
|
||||||
export abstract class RestTable {
|
export abstract class RestTable {
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ export abstract class RestTable {
|
||||||
try {
|
try {
|
||||||
this.sort = JSON.parse(result)
|
this.sort = JSON.parse(result)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Cannot load sort of local storage key ' + this.getSortLocalStorageKey(), err)
|
logger.error('Cannot load sort of local storage key ' + this.getSortLocalStorageKey(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +45,7 @@ export abstract class RestTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadLazy (event: LazyLoadEvent) {
|
loadLazy (event: LazyLoadEvent) {
|
||||||
logger('Load lazy %o.', event)
|
debugLogger('Load lazy %o.', event)
|
||||||
|
|
||||||
this.sort = {
|
this.sort = {
|
||||||
order: event.sortOrder,
|
order: event.sortOrder,
|
||||||
|
@ -65,6 +66,11 @@ export abstract class RestTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearch (search: string) {
|
onSearch (search: string) {
|
||||||
|
this.pagination = {
|
||||||
|
start: 0,
|
||||||
|
count: this.rowsPerPage
|
||||||
|
}
|
||||||
|
|
||||||
this.search = search
|
this.search = search
|
||||||
this.reloadData()
|
this.reloadData()
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { Injectable } from '@angular/core'
|
||||||
import { ComponentPaginationLight } from './component-pagination.model'
|
import { ComponentPaginationLight } from './component-pagination.model'
|
||||||
import { RestPagination } from './rest-pagination'
|
import { RestPagination } from './rest-pagination'
|
||||||
|
|
||||||
const logger = debug('peertube:rest')
|
const debugLogger = debug('peertube:rest')
|
||||||
|
|
||||||
interface QueryStringFilterPrefixes {
|
interface QueryStringFilterPrefixes {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
|
@ -88,7 +88,7 @@ export class RestService {
|
||||||
const prefixeStrings = Object.values(prefixes)
|
const prefixeStrings = Object.values(prefixes)
|
||||||
.map(p => p.prefix)
|
.map(p => p.prefix)
|
||||||
|
|
||||||
logger(`Built tokens "${tokens.join(', ')}" for prefixes "${prefixeStrings.join(', ')}"`)
|
debugLogger(`Built tokens "${tokens.join(', ')}" for prefixes "${prefixeStrings.join(', ')}"`)
|
||||||
|
|
||||||
// Search is the querystring minus defined filters
|
// Search is the querystring minus defined filters
|
||||||
const searchTokens = tokens.filter(t => {
|
const searchTokens = tokens.filter(t => {
|
||||||
|
@ -127,7 +127,7 @@ export class RestService {
|
||||||
|
|
||||||
const search = searchTokens.join(' ') || undefined
|
const search = searchTokens.join(' ') || undefined
|
||||||
|
|
||||||
logger('Built search: ' + search, additionalFilters)
|
debugLogger('Built search: ' + search, additionalFilters)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
search,
|
search,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { ComponentRef, Injectable } from '@angular/core'
|
import { ComponentRef, Injectable } from '@angular/core'
|
||||||
import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router'
|
import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { DisableForReuseHook } from './disable-for-reuse-hook'
|
import { DisableForReuseHook } from './disable-for-reuse-hook'
|
||||||
import { PeerTubeRouterService, RouterSetting } from './peertube-router.service'
|
import { PeerTubeRouterService, RouterSetting } from './peertube-router.service'
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ export class CustomReuseStrategy implements RouteReuseStrategy {
|
||||||
const key = this.generateKey(route)
|
const key = this.generateKey(route)
|
||||||
this.recentlyUsed = key
|
this.recentlyUsed = key
|
||||||
|
|
||||||
console.log('Storing component %s to reuse later.', key)
|
logger.info(`Storing component ${key} to reuse later.`)
|
||||||
|
|
||||||
const componentRef = (handle as any).componentRef as ComponentRef<DisableForReuseHook>
|
const componentRef = (handle as any).componentRef as ComponentRef<DisableForReuseHook>
|
||||||
componentRef.instance.disableForReuse()
|
componentRef.instance.disableForReuse()
|
||||||
|
@ -46,7 +47,7 @@ export class CustomReuseStrategy implements RouteReuseStrategy {
|
||||||
const key = this.generateKey(route)
|
const key = this.generateKey(route)
|
||||||
this.recentlyUsed = key
|
this.recentlyUsed = key
|
||||||
|
|
||||||
console.log('Reusing component %s.', key)
|
logger.info(`Reusing component ${key}.`)
|
||||||
|
|
||||||
const handle = this.storedRouteHandles.get(key)
|
const handle = this.storedRouteHandles.get(key)
|
||||||
if (!handle) return handle;
|
if (!handle) return handle;
|
||||||
|
@ -66,7 +67,7 @@ export class CustomReuseStrategy implements RouteReuseStrategy {
|
||||||
this.storedRouteHandles.forEach((r, key) => {
|
this.storedRouteHandles.forEach((r, key) => {
|
||||||
if (key === this.recentlyUsed) return
|
if (key === this.recentlyUsed) return
|
||||||
|
|
||||||
console.log('Removing stored component %s.', key);
|
logger.info(`Removing stored component ${key}`);
|
||||||
|
|
||||||
(r as any).componentRef.destroy()
|
(r as any).componentRef.destroy()
|
||||||
this.storedRouteHandles.delete(key)
|
this.storedRouteHandles.delete(key)
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import * as debug from 'debug'
|
import * as debug from 'debug'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { NavigationCancel, NavigationEnd, Router } from '@angular/router'
|
import { NavigationCancel, NavigationEnd, Router } from '@angular/router'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { ServerService } from '../server'
|
import { ServerService } from '../server'
|
||||||
import { SessionStorageService } from '../wrappers/storage.service'
|
import { SessionStorageService } from '../wrappers/storage.service'
|
||||||
|
|
||||||
const logger = debug('peertube:router:RedirectService')
|
const debugLogger = debug('peertube:router:RedirectService')
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RedirectService {
|
export class RedirectService {
|
||||||
|
@ -40,7 +41,7 @@ export class RedirectService {
|
||||||
this.latestSessionUrl = this.storage.getItem(RedirectService.SESSION_STORAGE_LATEST_SESSION_URL_KEY)
|
this.latestSessionUrl = this.storage.getItem(RedirectService.SESSION_STORAGE_LATEST_SESSION_URL_KEY)
|
||||||
this.storage.removeItem(RedirectService.SESSION_STORAGE_LATEST_SESSION_URL_KEY)
|
this.storage.removeItem(RedirectService.SESSION_STORAGE_LATEST_SESSION_URL_KEY)
|
||||||
|
|
||||||
logger('Loaded latest session URL %s', this.latestSessionUrl)
|
debugLogger('Loaded latest session URL %s', this.latestSessionUrl)
|
||||||
|
|
||||||
// Track previous url
|
// Track previous url
|
||||||
this.currentUrl = this.router.url
|
this.currentUrl = this.router.url
|
||||||
|
@ -51,8 +52,8 @@ export class RedirectService {
|
||||||
this.previousUrl = this.currentUrl
|
this.previousUrl = this.currentUrl
|
||||||
this.currentUrl = event.url
|
this.currentUrl = event.url
|
||||||
|
|
||||||
logger('Previous URL is %s, current URL is %s', this.previousUrl, this.currentUrl)
|
debugLogger('Previous URL is %s, current URL is %s', this.previousUrl, this.currentUrl)
|
||||||
logger('Setting %s as latest URL in session storage.', this.currentUrl)
|
debugLogger('Setting %s as latest URL in session storage.', this.currentUrl)
|
||||||
|
|
||||||
this.storage.setItem(RedirectService.SESSION_STORAGE_LATEST_SESSION_URL_KEY, this.currentUrl)
|
this.storage.setItem(RedirectService.SESSION_STORAGE_LATEST_SESSION_URL_KEY, this.currentUrl)
|
||||||
}
|
}
|
||||||
|
@ -84,18 +85,14 @@ export class RedirectService {
|
||||||
|
|
||||||
this.redirectingToHomepage = true
|
this.redirectingToHomepage = true
|
||||||
|
|
||||||
console.log('Redirecting to %s...', this.defaultRoute)
|
logger.info(`Redirecting to ${this.defaultRoute}...`)
|
||||||
|
|
||||||
this.router.navigateByUrl(this.defaultRoute, { skipLocationChange })
|
this.router.navigateByUrl(this.defaultRoute, { skipLocationChange })
|
||||||
.then(() => this.redirectingToHomepage = false)
|
.then(() => this.redirectingToHomepage = false)
|
||||||
.catch(() => {
|
.catch(err => {
|
||||||
this.redirectingToHomepage = false
|
this.redirectingToHomepage = false
|
||||||
|
|
||||||
console.error(
|
logger.error(`Cannot navigate to ${this.defaultRoute}, resetting default route to ${RedirectService.INIT_DEFAULT_ROUTE}`, err)
|
||||||
'Cannot navigate to %s, resetting default route to %s.',
|
|
||||||
this.defaultRoute,
|
|
||||||
RedirectService.INIT_DEFAULT_ROUTE
|
|
||||||
)
|
|
||||||
|
|
||||||
this.defaultRoute = RedirectService.INIT_DEFAULT_ROUTE
|
this.defaultRoute = RedirectService.INIT_DEFAULT_ROUTE
|
||||||
return this.router.navigateByUrl(this.defaultRoute, { skipLocationChange })
|
return this.router.navigateByUrl(this.defaultRoute, { skipLocationChange })
|
||||||
|
@ -104,18 +101,18 @@ export class RedirectService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private doRedirect (redirectUrl: string, fallbackRoute?: string) {
|
private doRedirect (redirectUrl: string, fallbackRoute?: string) {
|
||||||
logger('Redirecting on %s', redirectUrl)
|
debugLogger('Redirecting on %s', redirectUrl)
|
||||||
|
|
||||||
if (this.isValidRedirection(redirectUrl)) {
|
if (this.isValidRedirection(redirectUrl)) {
|
||||||
return this.router.navigateByUrl(redirectUrl)
|
return this.router.navigateByUrl(redirectUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger('%s is not a valid redirection, try fallback route %s', redirectUrl, fallbackRoute)
|
debugLogger('%s is not a valid redirection, try fallback route %s', redirectUrl, fallbackRoute)
|
||||||
if (fallbackRoute) {
|
if (fallbackRoute) {
|
||||||
return this.router.navigateByUrl(fallbackRoute)
|
return this.router.navigateByUrl(fallbackRoute)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger('There was no fallback route, redirecting to homepage')
|
debugLogger('There was no fallback route, redirecting to homepage')
|
||||||
return this.redirectToHomepage()
|
return this.redirectToHomepage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,9 @@ import { ViewportScroller } from '@angular/common'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { RouterSetting } from '../'
|
import { RouterSetting } from '../'
|
||||||
import { PeerTubeRouterService } from './peertube-router.service'
|
import { PeerTubeRouterService } from './peertube-router.service'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
|
|
||||||
const logger = debug('peertube:main:ScrollService')
|
const debugLogger = debug('peertube:main:ScrollService')
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ScrollService {
|
export class ScrollService {
|
||||||
|
@ -57,8 +58,8 @@ export class ScrollService {
|
||||||
if (nextSearchParams.toString() !== previousSearchParams.toString()) {
|
if (nextSearchParams.toString() !== previousSearchParams.toString()) {
|
||||||
this.resetScroll = true
|
this.resetScroll = true
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
console.error('Cannot parse URL to check next scroll.', e)
|
logger.error('Cannot parse URL to check next scroll.', err)
|
||||||
this.resetScroll = true
|
this.resetScroll = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -67,7 +68,7 @@ export class ScrollService {
|
||||||
private consumeScroll () {
|
private consumeScroll () {
|
||||||
// Handle anchors/restore position
|
// Handle anchors/restore position
|
||||||
this.peertubeRouter.getScrollEvents().subscribe(e => {
|
this.peertubeRouter.getScrollEvents().subscribe(e => {
|
||||||
logger('Will schedule scroll after router event %o.', { e, resetScroll: this.resetScroll })
|
debugLogger('Will schedule scroll after router event %o.', { e, resetScroll: this.resetScroll })
|
||||||
|
|
||||||
// scrollToAnchor first to preserve anchor position when using history navigation
|
// scrollToAnchor first to preserve anchor position when using history navigation
|
||||||
if (e.anchor) {
|
if (e.anchor) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators'
|
||||||
import { HttpClient } from '@angular/common/http'
|
import { HttpClient } from '@angular/common/http'
|
||||||
import { Inject, Injectable, LOCALE_ID } from '@angular/core'
|
import { Inject, Injectable, LOCALE_ID } from '@angular/core'
|
||||||
import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
|
import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n'
|
import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n'
|
||||||
import { HTMLServerConfig, ServerConfig, ServerStats, VideoConstant } from '@shared/models'
|
import { HTMLServerConfig, ServerConfig, ServerStats, VideoConstant } from '@shared/models'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
|
@ -43,7 +44,7 @@ export class ServerService {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Expected in dev mode since we can't inject the config in the HTML
|
// Expected in dev mode since we can't inject the config in the HTML
|
||||||
if (environment.production !== false) {
|
if (environment.production !== false) {
|
||||||
console.error('Cannot load config locally. Fallback to API.')
|
logger.error('Cannot load config locally. Fallback to API.')
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.getConfig()
|
return this.getConfig()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { capitalizeFirstLetter } from '@root-helpers/string'
|
import { capitalizeFirstLetter } from '@root-helpers/string'
|
||||||
import { UserLocalStorageKeys } from '@root-helpers/users'
|
import { UserLocalStorageKeys } from '@root-helpers/users'
|
||||||
import { HTMLServerConfig, ServerConfigTheme } from '@shared/models'
|
import { HTMLServerConfig, ServerConfigTheme } from '@shared/models'
|
||||||
|
@ -57,7 +58,7 @@ export class ThemeService {
|
||||||
private injectThemes (themes: ServerConfigTheme[], fromLocalStorage = false) {
|
private injectThemes (themes: ServerConfigTheme[], fromLocalStorage = false) {
|
||||||
this.themes = themes
|
this.themes = themes
|
||||||
|
|
||||||
console.log('Injecting %d themes.', this.themes.length)
|
logger.info(`Injecting ${this.themes.length} themes.`)
|
||||||
|
|
||||||
const head = this.getHeadElement()
|
const head = this.getHeadElement()
|
||||||
|
|
||||||
|
@ -117,13 +118,13 @@ export class ThemeService {
|
||||||
|
|
||||||
const currentTheme = this.getCurrentTheme()
|
const currentTheme = this.getCurrentTheme()
|
||||||
|
|
||||||
console.log('Enabling %s theme.', currentTheme)
|
logger.info(`Enabling ${currentTheme} theme.`)
|
||||||
|
|
||||||
this.loadTheme(currentTheme)
|
this.loadTheme(currentTheme)
|
||||||
|
|
||||||
const theme = this.getTheme(currentTheme)
|
const theme = this.getTheme(currentTheme)
|
||||||
if (theme) {
|
if (theme) {
|
||||||
console.log('Adding scripts of theme %s.', currentTheme)
|
logger.info(`Adding scripts of theme ${currentTheme}`)
|
||||||
|
|
||||||
this.pluginService.addPlugin(theme, true)
|
this.pluginService.addPlugin(theme, true)
|
||||||
|
|
||||||
|
@ -165,7 +166,7 @@ export class ThemeService {
|
||||||
this.injectThemes([ lastActiveTheme ], true)
|
this.injectThemes([ lastActiveTheme ], true)
|
||||||
this.updateCurrentTheme()
|
this.updateCurrentTheme()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Cannot parse last active theme.', err)
|
logger.error('Cannot parse last active theme.', err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,7 +174,7 @@ export class ThemeService {
|
||||||
private removeThemePlugins (themeName: string) {
|
private removeThemePlugins (themeName: string) {
|
||||||
const oldTheme = this.getTheme(themeName)
|
const oldTheme = this.getTheme(themeName)
|
||||||
if (oldTheme) {
|
if (oldTheme) {
|
||||||
console.log('Removing scripts of old theme %s.', themeName)
|
logger.info(`Removing scripts of old theme ${themeName}.`)
|
||||||
this.pluginService.removePlugin(oldTheme)
|
this.pluginService.removePlugin(oldTheme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
import { filter, throttleTime } from 'rxjs'
|
import { filter, throttleTime } from 'rxjs'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { AuthService, AuthStatus } from '@app/core/auth'
|
import { AuthService, AuthStatus } from '@app/core/auth'
|
||||||
import { UserLocalStorageKeys, UserTokens } from '@root-helpers/users'
|
|
||||||
import { getBoolOrDefault } from '@root-helpers/local-storage-utils'
|
import { getBoolOrDefault } from '@root-helpers/local-storage-utils'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
|
import { UserLocalStorageKeys, UserTokens } from '@root-helpers/users'
|
||||||
import { UserRole, UserUpdateMe } from '@shared/models'
|
import { UserRole, UserUpdateMe } from '@shared/models'
|
||||||
import { NSFWPolicyType } from '@shared/models/videos'
|
import { NSFWPolicyType } from '@shared/models/videos'
|
||||||
import { ServerService } from '../server'
|
import { ServerService } from '../server'
|
||||||
|
@ -95,7 +96,7 @@ export class UserLocalStorageService {
|
||||||
: null
|
: null
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
videoLanguages = null
|
videoLanguages = null
|
||||||
console.error('Cannot parse desired video languages from localStorage.', err)
|
logger.error('Cannot parse desired video languages from localStorage.', err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const htmlConfig = this.server.getHTMLConfig()
|
const htmlConfig = this.server.getHTMLConfig()
|
||||||
|
@ -142,7 +143,7 @@ export class UserLocalStorageService {
|
||||||
|
|
||||||
this.localStorageService.setItem(key, localStorageValue)
|
this.localStorageService.setItem(key, localStorageValue)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Cannot set ${key}->${value} in localStorage. Likely due to a value impossible to stringify.`, err)
|
logger.error(`Cannot set ${key}->${value} in localStorage. Likely due to a value impossible to stringify.`, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,8 @@ li.suggestion {
|
||||||
}
|
}
|
||||||
|
|
||||||
#typeahead-container {
|
#typeahead-container {
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
border: 1px solid pvar(--mainBackgroundColor) !important;
|
border: 1px solid pvar(--mainBackgroundColor) !important;
|
||||||
box-shadow: rgba(0, 0, 0, 0.1) 0 1px 20px 0;
|
box-shadow: rgba(0, 0, 0, 0.1) 0 1px 20px 0;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { ListKeyManager } from '@angular/cdk/a11y'
|
||||||
import { AfterViewChecked, Component, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core'
|
import { AfterViewChecked, Component, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core'
|
||||||
import { ActivatedRoute, Params, Router } from '@angular/router'
|
import { ActivatedRoute, Params, Router } from '@angular/router'
|
||||||
import { AuthService, ServerService } from '@app/core'
|
import { AuthService, ServerService } from '@app/core'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { HTMLServerConfig, SearchTargetType } from '@shared/models'
|
import { HTMLServerConfig, SearchTargetType } from '@shared/models'
|
||||||
import { SuggestionComponent, SuggestionPayload, SuggestionPayloadType } from './suggestion.component'
|
import { SuggestionComponent, SuggestionPayload, SuggestionPayloadType } from './suggestion.component'
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ export class SearchTypeaheadComponent implements OnInit, AfterViewChecked, OnDes
|
||||||
|
|
||||||
const activeIndex = this.suggestionItems.toArray().findIndex(i => i.result.default === true)
|
const activeIndex = this.suggestionItems.toArray().findIndex(i => i.result.default === true)
|
||||||
if (activeIndex === -1) {
|
if (activeIndex === -1) {
|
||||||
console.error('Cannot find active index.', { suggestionItems: this.suggestionItems })
|
logger.error('Cannot find active index.', { suggestionItems: this.suggestionItems })
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateItemsState(activeIndex)
|
this.updateItemsState(activeIndex)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { environment } from '../../environments/environment'
|
|
||||||
import IntlMessageFormat from 'intl-messageformat'
|
import IntlMessageFormat from 'intl-messageformat'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
|
import { environment } from '../../environments/environment'
|
||||||
|
|
||||||
function isOnDevLocale () {
|
function isOnDevLocale () {
|
||||||
return environment.production === false && window.location.search === '?lang=fr'
|
return environment.production === false && window.location.search === '?lang=fr'
|
||||||
|
@ -19,14 +20,14 @@ function prepareIcu (icu: string) {
|
||||||
try {
|
try {
|
||||||
return msg.format(context) as string
|
return msg.format(context) as string
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!alreadyWarned) console.warn('Cannot format ICU %s.', icu, err)
|
if (!alreadyWarned) logger.warn(`Cannot format ICU ${icu}.`, err)
|
||||||
|
|
||||||
alreadyWarned = true
|
alreadyWarned = true
|
||||||
return fallback
|
return fallback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('Cannot build intl message %s.', icu, err)
|
logger.warn(`Cannot build intl message ${icu}.`, err)
|
||||||
|
|
||||||
return (_context: unknown, fallback: string) => fallback
|
return (_context: unknown, fallback: string) => fallback
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,9 +134,7 @@
|
||||||
<div class="footer-bottom">
|
<div class="footer-bottom">
|
||||||
|
|
||||||
<div class="footer-links">
|
<div class="footer-links">
|
||||||
<div *ngIf="isLoggedIn === false">
|
<span *ngIf="isLoggedIn === false" role="button" (click)="openLanguageChooser()" class="c-hand" i18n>Interface: {{ currentInterfaceLanguage }}</span>
|
||||||
<span role="button" (click)="openLanguageChooser()" class="c-hand" i18n>Interface: {{ currentInterfaceLanguage }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<a i18n routerLink="/about/instance">Contact</a>
|
<a i18n routerLink="/about/instance">Contact</a>
|
||||||
|
|
|
@ -304,7 +304,8 @@ my-actor-avatar {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a,
|
||||||
|
span[role=button] {
|
||||||
@include margin-right(8px);
|
@include margin-right(8px);
|
||||||
@include disable-default-a-behaviour;
|
@include disable-default-a-behaviour;
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { PluginsManager } from '@root-helpers/plugins-manager'
|
import { PluginsManager } from '@root-helpers/plugins-manager'
|
||||||
import { HTMLServerConfig, ServerConfig, UserRight, VideoConstant } from '@shared/models'
|
import { HTMLServerConfig, ServerConfig, UserRight, VideoConstant } from '@shared/models'
|
||||||
|
|
||||||
const logger = debug('peertube:menu:MenuComponent')
|
const debugLogger = debug('peertube:menu:MenuComponent')
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-menu',
|
selector: 'my-menu',
|
||||||
|
@ -295,8 +295,8 @@ export class MenuComponent implements OnInit {
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap(() => this.user.computeCanSeeVideosLink(this.userService.getMyVideoQuotaUsed()))
|
switchMap(() => this.user.computeCanSeeVideosLink(this.userService.getMyVideoQuotaUsed()))
|
||||||
).subscribe(res => {
|
).subscribe(res => {
|
||||||
if (res === true) logger('User can see videos link.')
|
if (res === true) debugLogger('User can see videos link.')
|
||||||
else logger('User cannot see videos link.')
|
else debugLogger('User cannot see videos link.')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Component, ElementRef, ViewChild } from '@angular/core'
|
import { Component, ElementRef, ViewChild } from '@angular/core'
|
||||||
import { Notifier, ServerService, User, UserService } from '@app/core'
|
import { Notifier, ServerService, User, UserService } from '@app/core'
|
||||||
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
|
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -71,7 +72,7 @@ export class AccountSetupWarningModalComponent {
|
||||||
|
|
||||||
this.userService.updateMyProfile({ noAccountSetupWarningModal: true })
|
this.userService.updateMyProfile({ noAccountSetupWarningModal: true })
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: () => console.log('We will not open the account setup modal again.'),
|
next: () => logger.info('We will not open the account setup modal again.'),
|
||||||
|
|
||||||
error: err => this.notifier.error(err.message)
|
error: err => this.notifier.error(err.message)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Component, ElementRef, ViewChild } from '@angular/core'
|
import { Component, ElementRef, ViewChild } from '@angular/core'
|
||||||
import { Notifier, User, UserService } from '@app/core'
|
import { Notifier, User, UserService } from '@app/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
|
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -42,7 +43,7 @@ export class AdminWelcomeModalComponent {
|
||||||
|
|
||||||
this.userService.updateMyProfile({ noWelcomeModal: true })
|
this.userService.updateMyProfile({ noWelcomeModal: true })
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: () => console.log('We will not open the welcome modal again.'),
|
next: () => logger.info('We will not open the welcome modal again.'),
|
||||||
|
|
||||||
error: err => this.notifier.error(err.message)
|
error: err => this.notifier.error(err.message)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Component, ElementRef, ViewChild, Input } from '@angular/core'
|
import { Component, ElementRef, Input, ViewChild } from '@angular/core'
|
||||||
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-custom-modal',
|
selector: 'my-custom-modal',
|
||||||
|
@ -29,7 +30,7 @@ export class CustomModalComponent {
|
||||||
confirm?: { value: string, action?: () => void }
|
confirm?: { value: string, action?: () => void }
|
||||||
}) {
|
}) {
|
||||||
if (this.modalRef instanceof NgbModalRef && this.modalService.hasOpenModals()) {
|
if (this.modalRef instanceof NgbModalRef && this.modalService.hasOpenModals()) {
|
||||||
console.error('Cannot open another custom modal, one is already opened.')
|
logger.error('Cannot open another custom modal, one is already opened.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Location } from '@angular/common'
|
||||||
import { Component, ElementRef, ViewChild } from '@angular/core'
|
import { Component, ElementRef, ViewChild } from '@angular/core'
|
||||||
import { Notifier, User, UserService } from '@app/core'
|
import { Notifier, User, UserService } from '@app/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
|
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
|
||||||
import { About, ServerConfig } from '@shared/models/server'
|
import { About, ServerConfig } from '@shared/models/server'
|
||||||
|
|
||||||
|
@ -64,7 +65,7 @@ export class InstanceConfigWarningModalComponent {
|
||||||
|
|
||||||
this.userService.updateMyProfile({ noInstanceConfigWarningModal: true })
|
this.userService.updateMyProfile({ noInstanceConfigWarningModal: true })
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: () => console.log('We will not open the instance config warning modal again.'),
|
next: () => logger.info('We will not open the instance config warning modal again.'),
|
||||||
|
|
||||||
error: err => this.notifier.error(err.message)
|
error: err => this.notifier.error(err.message)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<p-table
|
<p-table
|
||||||
[value]="abuses" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
|
[value]="abuses" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
|
||||||
[sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true"
|
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true"
|
||||||
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
|
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
|
||||||
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
||||||
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} reports"
|
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} reports"
|
||||||
|
|
|
@ -8,13 +8,14 @@ import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable }
|
||||||
import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main'
|
import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main'
|
||||||
import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
|
import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
|
||||||
import { VideoCommentService } from '@app/shared/shared-video-comment'
|
import { VideoCommentService } from '@app/shared/shared-video-comment'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { AbuseState, AdminAbuse } from '@shared/models'
|
import { AbuseState, AdminAbuse } from '@shared/models'
|
||||||
import { AdvancedInputFilter } from '../shared-forms'
|
import { AdvancedInputFilter } from '../shared-forms'
|
||||||
import { AbuseMessageModalComponent } from './abuse-message-modal.component'
|
import { AbuseMessageModalComponent } from './abuse-message-modal.component'
|
||||||
import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
|
import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
|
||||||
import { ProcessedAbuse } from './processed-abuse.model'
|
import { ProcessedAbuse } from './processed-abuse.model'
|
||||||
|
|
||||||
const logger = debug('peertube:moderation:AbuseListTableComponent')
|
const debugLogger = debug('peertube:moderation:AbuseListTableComponent')
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-abuse-list-table',
|
selector: 'my-abuse-list-table',
|
||||||
|
@ -158,7 +159,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit {
|
||||||
const abuse = this.abuses.find(a => a.id === event.abuseId)
|
const abuse = this.abuses.find(a => a.id === event.abuseId)
|
||||||
|
|
||||||
if (!abuse) {
|
if (!abuse) {
|
||||||
console.error('Cannot find abuse %d.', event.abuseId)
|
logger.error(`Cannot find abuse ${event.abuseId}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +178,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected reloadData () {
|
protected reloadData () {
|
||||||
logger('Loading data.')
|
debugLogger('Loading data.')
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
pagination: this.pagination,
|
pagination: this.pagination,
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { AuthService, HtmlRendererService, Notifier } from '@app/core'
|
||||||
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
|
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { AbuseMessage, UserAbuse } from '@shared/models'
|
import { AbuseMessage, UserAbuse } from '@shared/models'
|
||||||
import { ABUSE_MESSAGE_VALIDATOR } from '../form-validators/abuse-validators'
|
import { ABUSE_MESSAGE_VALIDATOR } from '../form-validators/abuse-validators'
|
||||||
import { AbuseService } from '../shared-moderation'
|
import { AbuseService } from '../shared-moderation'
|
||||||
|
@ -72,7 +73,7 @@ export class AbuseMessageModalComponent extends FormReactive implements OnInit {
|
||||||
|
|
||||||
error: err => {
|
error: err => {
|
||||||
this.sendingMessage = false
|
this.sendingMessage = false
|
||||||
console.error(err)
|
logger.error(err)
|
||||||
this.notifier.error('Sorry but you cannot send this message. Please retry later')
|
this.notifier.error('Sorry but you cannot send this message. Please retry later')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -140,6 +140,6 @@ export class ActorAvatarComponent implements OnChanges {
|
||||||
const theme = Object.keys(themes)
|
const theme = Object.keys(themes)
|
||||||
.find(chars => chars.includes(initialLowercase))
|
.find(chars => chars.includes(initialLowercase))
|
||||||
|
|
||||||
return themes[theme]
|
return themes[theme] || 'blue'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import {
|
||||||
VideosListMarkupComponent
|
VideosListMarkupComponent
|
||||||
} from './peertube-custom-tags'
|
} from './peertube-custom-tags'
|
||||||
import { CustomMarkupComponent } from './peertube-custom-tags/shared'
|
import { CustomMarkupComponent } from './peertube-custom-tags/shared'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
|
|
||||||
type AngularBuilderFunction = (el: HTMLElement) => ComponentRef<CustomMarkupComponent>
|
type AngularBuilderFunction = (el: HTMLElement) => ComponentRef<CustomMarkupComponent>
|
||||||
type HTMLBuilderFunction = (el: HTMLElement) => HTMLElement
|
type HTMLBuilderFunction = (el: HTMLElement) => HTMLElement
|
||||||
|
@ -70,7 +71,7 @@ export class CustomMarkupService {
|
||||||
// Insert as first child
|
// Insert as first child
|
||||||
e.insertBefore(element, e.firstChild)
|
e.insertBefore(element, e.firstChild)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Cannot inject component %s.', selector, err)
|
logger.error(`Cannot inject component ${selector}`, err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -90,7 +91,7 @@ export class CustomMarkupService {
|
||||||
|
|
||||||
this.dynamicElementService.injectElement(e, component)
|
this.dynamicElementService.injectElement(e, component)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Cannot inject component %s.', selector, err)
|
logger.error(`Cannot inject component ${selector}`, err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ export type AdvancedInputFilterChild = {
|
||||||
value: string
|
value: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const logger = debug('peertube:AdvancedInputFilterComponent')
|
const debugLogger = debug('peertube:AdvancedInputFilterComponent')
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-advanced-input-filter',
|
selector: 'my-advanced-input-filter',
|
||||||
|
@ -98,7 +98,7 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
|
||||||
.subscribe(params => {
|
.subscribe(params => {
|
||||||
const search = params.search || ''
|
const search = params.search || ''
|
||||||
|
|
||||||
logger('On route search change "%s".', search)
|
debugLogger('On route search change "%s".', search)
|
||||||
|
|
||||||
if (this.searchValue === search) return
|
if (this.searchValue === search) return
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger('On search "%s".', this.searchValue)
|
debugLogger('On search "%s".', this.searchValue)
|
||||||
|
|
||||||
this.search.emit(this.searchValue)
|
this.search.emit(this.searchValue)
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ $input-border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.maximized {
|
&.maximized {
|
||||||
z-index: #{z(header) - 1};
|
z-index: #{z(root-header) - 1};
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: $header-height;
|
top: $header-height;
|
||||||
left: $menu-width;
|
left: $menu-width;
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import { SortMeta } from 'primeng/api'
|
import { SortMeta } from 'primeng/api'
|
||||||
import { Observable } from 'rxjs'
|
import { from, Observable } from 'rxjs'
|
||||||
import { catchError, map } from 'rxjs/operators'
|
import { catchError, concatMap, map, toArray } from 'rxjs/operators'
|
||||||
import { HttpClient, HttpParams } from '@angular/common/http'
|
import { HttpClient, HttpParams } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { RestExtractor, RestPagination, RestService } from '@app/core'
|
import { RestExtractor, RestPagination, RestService } from '@app/core'
|
||||||
|
import { arrayify } from '@shared/core-utils'
|
||||||
import { ActivityPubActorType, ActorFollow, FollowState, ResultList, ServerFollowCreate } from '@shared/models'
|
import { ActivityPubActorType, ActorFollow, FollowState, ResultList, ServerFollowCreate } from '@shared/models'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
|
import { AdvancedInputFilter } from '../shared-forms'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class InstanceFollowService {
|
export class InstanceFollowService {
|
||||||
|
@ -30,7 +32,10 @@ export class InstanceFollowService {
|
||||||
let params = new HttpParams()
|
let params = new HttpParams()
|
||||||
params = this.restService.addRestGetParams(params, pagination, sort)
|
params = this.restService.addRestGetParams(params, pagination, sort)
|
||||||
|
|
||||||
if (search) params = params.append('search', search)
|
if (search) {
|
||||||
|
params = this.restService.addObjectParams(params, this.parseFollowsListFilters(search))
|
||||||
|
}
|
||||||
|
|
||||||
if (state) params = params.append('state', state)
|
if (state) params = params.append('state', state)
|
||||||
if (actorType) params = params.append('actorType', actorType)
|
if (actorType) params = params.append('actorType', actorType)
|
||||||
|
|
||||||
|
@ -53,7 +58,10 @@ export class InstanceFollowService {
|
||||||
let params = new HttpParams()
|
let params = new HttpParams()
|
||||||
params = this.restService.addRestGetParams(params, pagination, sort)
|
params = this.restService.addRestGetParams(params, pagination, sort)
|
||||||
|
|
||||||
if (search) params = params.append('search', search)
|
if (search) {
|
||||||
|
params = this.restService.addObjectParams(params, this.parseFollowsListFilters(search))
|
||||||
|
}
|
||||||
|
|
||||||
if (state) params = params.append('state', state)
|
if (state) params = params.append('state', state)
|
||||||
if (actorType) params = params.append('actorType', actorType)
|
if (actorType) params = params.append('actorType', actorType)
|
||||||
|
|
||||||
|
@ -74,31 +82,93 @@ export class InstanceFollowService {
|
||||||
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
||||||
}
|
}
|
||||||
|
|
||||||
unfollow (follow: ActorFollow) {
|
unfollow (followsArg: ActorFollow[] | ActorFollow) {
|
||||||
|
const follows = arrayify(followsArg)
|
||||||
|
|
||||||
|
return from(follows)
|
||||||
|
.pipe(
|
||||||
|
concatMap(follow => {
|
||||||
const handle = follow.following.name + '@' + follow.following.host
|
const handle = follow.following.name + '@' + follow.following.host
|
||||||
|
|
||||||
return this.authHttp.delete(InstanceFollowService.BASE_APPLICATION_URL + '/following/' + handle)
|
return this.authHttp.delete(InstanceFollowService.BASE_APPLICATION_URL + '/following/' + handle)
|
||||||
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
}),
|
||||||
|
toArray(),
|
||||||
|
catchError(err => this.restExtractor.handleError(err))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
acceptFollower (follow: ActorFollow) {
|
acceptFollower (followsArg: ActorFollow[] | ActorFollow) {
|
||||||
|
const follows = arrayify(followsArg)
|
||||||
|
|
||||||
|
return from(follows)
|
||||||
|
.pipe(
|
||||||
|
concatMap(follow => {
|
||||||
const handle = follow.follower.name + '@' + follow.follower.host
|
const handle = follow.follower.name + '@' + follow.follower.host
|
||||||
|
|
||||||
return this.authHttp.post(`${InstanceFollowService.BASE_APPLICATION_URL}/followers/${handle}/accept`, {})
|
return this.authHttp.post(`${InstanceFollowService.BASE_APPLICATION_URL}/followers/${handle}/accept`, {})
|
||||||
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
}),
|
||||||
|
toArray(),
|
||||||
|
catchError(err => this.restExtractor.handleError(err))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
rejectFollower (follow: ActorFollow) {
|
rejectFollower (followsArg: ActorFollow[] | ActorFollow) {
|
||||||
|
const follows = arrayify(followsArg)
|
||||||
|
|
||||||
|
return from(follows)
|
||||||
|
.pipe(
|
||||||
|
concatMap(follow => {
|
||||||
const handle = follow.follower.name + '@' + follow.follower.host
|
const handle = follow.follower.name + '@' + follow.follower.host
|
||||||
|
|
||||||
return this.authHttp.post(`${InstanceFollowService.BASE_APPLICATION_URL}/followers/${handle}/reject`, {})
|
return this.authHttp.post(`${InstanceFollowService.BASE_APPLICATION_URL}/followers/${handle}/reject`, {})
|
||||||
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
}),
|
||||||
|
toArray(),
|
||||||
|
catchError(err => this.restExtractor.handleError(err))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFollower (follow: ActorFollow) {
|
removeFollower (followsArg: ActorFollow[] | ActorFollow) {
|
||||||
|
const follows = arrayify(followsArg)
|
||||||
|
|
||||||
|
return from(follows)
|
||||||
|
.pipe(
|
||||||
|
concatMap(follow => {
|
||||||
const handle = follow.follower.name + '@' + follow.follower.host
|
const handle = follow.follower.name + '@' + follow.follower.host
|
||||||
|
|
||||||
return this.authHttp.delete(`${InstanceFollowService.BASE_APPLICATION_URL}/followers/${handle}`)
|
return this.authHttp.delete(`${InstanceFollowService.BASE_APPLICATION_URL}/followers/${handle}`)
|
||||||
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
}),
|
||||||
|
toArray(),
|
||||||
|
catchError(err => this.restExtractor.handleError(err))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFollowsListFilters (): AdvancedInputFilter[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: $localize`Advanced filters`,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'state:accepted',
|
||||||
|
label: $localize`Accepted follows`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'state:rejected',
|
||||||
|
label: $localize`Rejected follows`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'state:pending',
|
||||||
|
label: $localize`Pending follows`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseFollowsListFilters (search: string) {
|
||||||
|
return this.restService.parseQueryStringFilter(search, {
|
||||||
|
state: {
|
||||||
|
prefix: 'state:'
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
ViewContainerRef
|
ViewContainerRef
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
|
|
||||||
const logger = debug('peertube:main:DeferLoadingDirective')
|
const debugLogger = debug('peertube:main:DeferLoadingDirective')
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[myDeferLoading]'
|
selector: '[myDeferLoading]'
|
||||||
|
@ -52,7 +52,7 @@ export class DeferLoadingDirective implements AfterViewInit, OnDestroy {
|
||||||
load () {
|
load () {
|
||||||
if (this.isLoaded()) return
|
if (this.isLoaded()) return
|
||||||
|
|
||||||
logger('Loading component')
|
debugLogger('Loading component')
|
||||||
|
|
||||||
this.viewContainer.clear()
|
this.viewContainer.clear()
|
||||||
this.view = this.viewContainer.createEmbeddedView(this.template, {}, 0)
|
this.view = this.viewContainer.createEmbeddedView(this.template, {}, 0)
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<ng-template #content>
|
<ng-template #content>
|
||||||
<my-loader size="sm" [loading]="loading"></my-loader>
|
<my-loader size="sm" [ngClass]="{ displayed: loading }" [loading]="loading"></my-loader>
|
||||||
<my-global-icon *ngIf="icon && !loading" [iconName]="icon"></my-global-icon>
|
<my-global-icon *ngIf="icon && !loading" [iconName]="icon"></my-global-icon>
|
||||||
|
|
||||||
<span *ngIf="label" class="button-label">{{ label }}</span>
|
<span *ngIf="label" class="button-label">{{ label }}</span>
|
||||||
|
|
|
@ -29,7 +29,7 @@ span[class$=-button] {
|
||||||
.action-button {
|
.action-button {
|
||||||
width: 100%; // useful for ellipsis, allow to define a max-width on host component
|
width: 100%; // useful for ellipsis, allow to define a max-width on host component
|
||||||
|
|
||||||
my-loader {
|
my-loader.displayed {
|
||||||
@include margin-right(3px);
|
@include margin-right(3px);
|
||||||
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { ScreenService } from '@app/core'
|
||||||
import { NgbDropdown, NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbDropdown, NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import * as debug from 'debug'
|
import * as debug from 'debug'
|
||||||
|
|
||||||
const logger = debug('peertube:main:ListOverflowItem')
|
const debugLogger = debug('peertube:main:ListOverflowItem')
|
||||||
|
|
||||||
export interface ListOverflowItem {
|
export interface ListOverflowItem {
|
||||||
label: string
|
label: string
|
||||||
|
@ -66,7 +66,7 @@ export class ListOverflowComponent<T extends ListOverflowItem> implements AfterV
|
||||||
let showItemsUntilIndexExcluded: number
|
let showItemsUntilIndexExcluded: number
|
||||||
let accWidth = 0
|
let accWidth = 0
|
||||||
|
|
||||||
logger('Parent width is %d', parentWidth)
|
debugLogger('Parent width is %d', parentWidth)
|
||||||
|
|
||||||
for (const [ index, el ] of this.itemsRendered.toArray().entries()) {
|
for (const [ index, el ] of this.itemsRendered.toArray().entries()) {
|
||||||
accWidth += el.nativeElement.getBoundingClientRect().width
|
accWidth += el.nativeElement.getBoundingClientRect().width
|
||||||
|
@ -79,7 +79,7 @@ export class ListOverflowComponent<T extends ListOverflowItem> implements AfterV
|
||||||
e.style.visibility = shouldBeVisible ? 'inherit' : 'hidden'
|
e.style.visibility = shouldBeVisible ? 'inherit' : 'hidden'
|
||||||
}
|
}
|
||||||
|
|
||||||
logger('Accumulated children width is %d so exclude index is %d', accWidth, showItemsUntilIndexExcluded)
|
debugLogger('Accumulated children width is %d so exclude index is %d', accWidth, showItemsUntilIndexExcluded)
|
||||||
|
|
||||||
this.showItemsUntilIndexExcluded = showItemsUntilIndexExcluded
|
this.showItemsUntilIndexExcluded = showItemsUntilIndexExcluded
|
||||||
this.cdr.markForCheck()
|
this.cdr.markForCheck()
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { AuthUser } from '@app/core'
|
||||||
import { Account } from '@app/shared/shared-main/account/account.model'
|
import { Account } from '@app/shared/shared-main/account/account.model'
|
||||||
import { Actor } from '@app/shared/shared-main/account/actor.model'
|
import { Actor } from '@app/shared/shared-main/account/actor.model'
|
||||||
import { VideoChannel } from '@app/shared/shared-main/video-channel/video-channel.model'
|
import { VideoChannel } from '@app/shared/shared-main/video-channel/video-channel.model'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import {
|
import {
|
||||||
AbuseState,
|
AbuseState,
|
||||||
ActorInfo,
|
ActorInfo,
|
||||||
|
@ -234,7 +235,7 @@ export class UserNotification implements UserNotificationServer {
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.type = null
|
this.type = null
|
||||||
console.error(err)
|
logger.error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ export interface VideoCaptionEdit {
|
||||||
|
|
||||||
action?: 'CREATE' | 'REMOVE' | 'UPDATE'
|
action?: 'CREATE' | 'REMOVE' | 'UPDATE'
|
||||||
captionfile?: any
|
captionfile?: any
|
||||||
|
updatedAt?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VideoCaptionWithPathEdit = VideoCaptionEdit & { captionPath?: string }
|
export type VideoCaptionWithPathEdit = VideoCaptionEdit & { captionPath?: string }
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { AuthService, ComponentPaginationLight, RestExtractor, RestService, ServerService, UserService } from '@app/core'
|
import { AuthService, ComponentPaginationLight, RestExtractor, RestService, ServerService, UserService } from '@app/core'
|
||||||
import { objectToFormData } from '@app/helpers'
|
import { objectToFormData } from '@app/helpers'
|
||||||
|
import { arrayify } from '@shared/core-utils'
|
||||||
import {
|
import {
|
||||||
BooleanBothQuery,
|
BooleanBothQuery,
|
||||||
FeedFormat,
|
FeedFormat,
|
||||||
|
@ -285,7 +286,7 @@ export class VideoService {
|
||||||
}
|
}
|
||||||
|
|
||||||
removeVideo (idArg: number | number[]) {
|
removeVideo (idArg: number | number[]) {
|
||||||
const ids = Array.isArray(idArg) ? idArg : [ idArg ]
|
const ids = arrayify(idArg)
|
||||||
|
|
||||||
return from(ids)
|
return from(ids)
|
||||||
.pipe(
|
.pipe(
|
||||||
|
@ -304,6 +305,11 @@ export class VideoService {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeFile (videoId: number | string, fileId: number, type: 'hls' | 'webtorrent') {
|
||||||
|
return this.authHttp.delete(VideoService.BASE_VIDEO_URL + '/' + videoId + '/' + type + '/' + fileId)
|
||||||
|
.pipe(catchError(err => this.restExtractor.handleError(err)))
|
||||||
|
}
|
||||||
|
|
||||||
runTranscoding (videoIds: (number | string)[], type: 'hls' | 'webtorrent') {
|
runTranscoding (videoIds: (number | string)[], type: 'hls' | 'webtorrent') {
|
||||||
const body: VideoTranscodingCreate = { transcodingType: type }
|
const body: VideoTranscodingCreate = { transcodingType: type }
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p-table
|
<p-table
|
||||||
[value]="blockedAccounts" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
|
[value]="blockedAccounts" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords"
|
||||||
|
[rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" [first]="pagination.start"
|
||||||
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)"
|
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)"
|
||||||
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
||||||
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted accounts"
|
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} muted accounts"
|
||||||
|
@ -20,7 +21,7 @@
|
||||||
<ng-template pTemplate="header">
|
<ng-template pTemplate="header">
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width: 150px;" i18n>Action</th> <!-- column for action buttons -->
|
<th style="width: 150px;" i18n>Action</th> <!-- column for action buttons -->
|
||||||
<th style="width: calc(100% - 300px);" i18n>Account</th>
|
<th i18n>Account</th>
|
||||||
<th style="width: 150px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th>
|
<th style="width: 150px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th>
|
||||||
</tr>
|
</tr>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { catchError, concatMap, map, toArray } from 'rxjs/operators'
|
||||||
import { HttpClient, HttpParams } from '@angular/common/http'
|
import { HttpClient, HttpParams } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { RestExtractor, RestPagination, RestService } from '@app/core'
|
import { RestExtractor, RestPagination, RestService } from '@app/core'
|
||||||
|
import { arrayify } from '@shared/core-utils'
|
||||||
import { AccountBlock as AccountBlockServer, BlockStatus, ResultList, ServerBlock } from '@shared/models'
|
import { AccountBlock as AccountBlockServer, BlockStatus, ResultList, ServerBlock } from '@shared/models'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
import { Account } from '../shared-main'
|
import { Account } from '../shared-main'
|
||||||
|
@ -122,7 +123,7 @@ export class BlocklistService {
|
||||||
}
|
}
|
||||||
|
|
||||||
blockAccountByInstance (accountsArg: Pick<Account, 'nameWithHost'> | Pick<Account, 'nameWithHost'>[]) {
|
blockAccountByInstance (accountsArg: Pick<Account, 'nameWithHost'> | Pick<Account, 'nameWithHost'>[]) {
|
||||||
const accounts = Array.isArray(accountsArg) ? accountsArg : [ accountsArg ]
|
const accounts = arrayify(accountsArg)
|
||||||
|
|
||||||
return from(accounts)
|
return from(accounts)
|
||||||
.pipe(
|
.pipe(
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p-table
|
<p-table
|
||||||
[value]="blockedServers" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
|
[value]="blockedServers" [paginator]="totalRecords > 0" [totalRecords]="totalRecords"
|
||||||
|
[rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions" [first]="pagination.start"
|
||||||
[sortField]="sort.field" [sortOrder]="sort.order"
|
[sortField]="sort.field" [sortOrder]="sort.order"
|
||||||
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
|
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
|
||||||
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
|
||||||
|
@ -28,7 +29,7 @@
|
||||||
<ng-template pTemplate="header">
|
<ng-template pTemplate="header">
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width: 150px;" i18n>Action</th> <!-- column for action buttons -->
|
<th style="width: 150px;" i18n>Action</th> <!-- column for action buttons -->
|
||||||
<th style="width: calc(100% - 300px);" i18n>Instance</th>
|
<th i18n>Instance</th>
|
||||||
<th style="width: 150px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th>
|
<th style="width: 150px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th>
|
||||||
</tr>
|
</tr>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { catchError, concatMap, map, toArray } from 'rxjs/operators'
|
||||||
import { HttpClient, HttpParams } from '@angular/common/http'
|
import { HttpClient, HttpParams } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { RestExtractor, RestPagination, RestService } from '@app/core'
|
import { RestExtractor, RestPagination, RestService } from '@app/core'
|
||||||
|
import { arrayify } from '@shared/core-utils'
|
||||||
import { ResultList, VideoBlacklist, VideoBlacklistType } from '@shared/models'
|
import { ResultList, VideoBlacklist, VideoBlacklistType } from '@shared/models'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
|
|
||||||
|
@ -53,7 +54,7 @@ export class VideoBlockService {
|
||||||
}
|
}
|
||||||
|
|
||||||
unblockVideo (videoIdArgs: number | number[]) {
|
unblockVideo (videoIdArgs: number | number[]) {
|
||||||
const videoIds = Array.isArray(videoIdArgs) ? videoIdArgs : [ videoIdArgs ]
|
const videoIds = arrayify(videoIdArgs)
|
||||||
|
|
||||||
return observableFrom(videoIds)
|
return observableFrom(videoIds)
|
||||||
.pipe(
|
.pipe(
|
||||||
|
|
|
@ -221,7 +221,6 @@ export class AdvancedSearch {
|
||||||
this.tagsAllOf !== undefined ||
|
this.tagsAllOf !== undefined ||
|
||||||
this.durationMin !== undefined ||
|
this.durationMin !== undefined ||
|
||||||
this.durationMax !== undefined ||
|
this.durationMax !== undefined ||
|
||||||
this.host !== undefined ||
|
|
||||||
this.isLive !== undefined
|
this.isLive !== undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { VideoPlaylist } from '../shared-video-playlist'
|
||||||
import { SearchService } from './search.service'
|
import { SearchService } from './search.service'
|
||||||
import { AdvancedSearch } from './advanced-search.model'
|
import { AdvancedSearch } from './advanced-search.model'
|
||||||
|
|
||||||
const logger = debug('peertube:search:FindInBulkService')
|
const debugLogger = debug('peertube:search:FindInBulkService')
|
||||||
|
|
||||||
type BulkObservables <P extends number | string, R> = {
|
type BulkObservables <P extends number | string, R> = {
|
||||||
notifier: Subject<P>
|
notifier: Subject<P>
|
||||||
|
@ -36,7 +36,7 @@ export class FindInBulkService {
|
||||||
}
|
}
|
||||||
|
|
||||||
getVideo (uuid: string): Observable<Video> {
|
getVideo (uuid: string): Observable<Video> {
|
||||||
logger('Schedule video fetch for uuid %s.', uuid)
|
debugLogger('Schedule video fetch for uuid %s.', uuid)
|
||||||
|
|
||||||
return this.getData({
|
return this.getData({
|
||||||
observableObject: this.getVideoInBulk,
|
observableObject: this.getVideoInBulk,
|
||||||
|
@ -46,7 +46,7 @@ export class FindInBulkService {
|
||||||
}
|
}
|
||||||
|
|
||||||
getChannel (handle: string): Observable<VideoChannel> {
|
getChannel (handle: string): Observable<VideoChannel> {
|
||||||
logger('Schedule channel fetch for handle %s.', handle)
|
debugLogger('Schedule channel fetch for handle %s.', handle)
|
||||||
|
|
||||||
return this.getData({
|
return this.getData({
|
||||||
observableObject: this.getChannelInBulk,
|
observableObject: this.getChannelInBulk,
|
||||||
|
@ -56,7 +56,7 @@ export class FindInBulkService {
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlaylist (uuid: string): Observable<VideoPlaylist> {
|
getPlaylist (uuid: string): Observable<VideoPlaylist> {
|
||||||
logger('Schedule playlist fetch for uuid %s.', uuid)
|
debugLogger('Schedule playlist fetch for uuid %s.', uuid)
|
||||||
|
|
||||||
return this.getData({
|
return this.getData({
|
||||||
observableObject: this.getPlaylistInBulk,
|
observableObject: this.getPlaylistInBulk,
|
||||||
|
@ -94,7 +94,7 @@ export class FindInBulkService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getVideosInBulk (uuids: string[]) {
|
private getVideosInBulk (uuids: string[]) {
|
||||||
logger('Fetching videos %s.', uuids.join(', '))
|
debugLogger('Fetching videos %s.', uuids.join(', '))
|
||||||
|
|
||||||
return this.searchService.searchVideos({
|
return this.searchService.searchVideos({
|
||||||
uuids,
|
uuids,
|
||||||
|
@ -104,7 +104,7 @@ export class FindInBulkService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getChannelsInBulk (handles: string[]) {
|
private getChannelsInBulk (handles: string[]) {
|
||||||
logger('Fetching channels %s.', handles.join(', '))
|
debugLogger('Fetching channels %s.', handles.join(', '))
|
||||||
|
|
||||||
return this.searchService.searchVideoChannels({
|
return this.searchService.searchVideoChannels({
|
||||||
handles,
|
handles,
|
||||||
|
@ -114,7 +114,7 @@ export class FindInBulkService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPlaylistsInBulk (uuids: string[]) {
|
private getPlaylistsInBulk (uuids: string[]) {
|
||||||
logger('Fetching playlists %s.', uuids.join(', '))
|
debugLogger('Fetching playlists %s.', uuids.join(', '))
|
||||||
|
|
||||||
return this.searchService.searchVideoPlaylists({
|
return this.searchService.searchVideoPlaylists({
|
||||||
uuids,
|
uuids,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Component, Input, OnInit } from '@angular/core'
|
import { Component, Input, OnInit } from '@angular/core'
|
||||||
import { Notifier } from '@app/core'
|
import { Notifier } from '@app/core'
|
||||||
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
||||||
|
import { logger } from '@root-helpers/logger'
|
||||||
import { USER_HANDLE_VALIDATOR } from '../form-validators/user-validators'
|
import { USER_HANDLE_VALIDATOR } from '../form-validators/user-validators'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -59,7 +60,7 @@ export class RemoteSubscribeComponent extends FormReactive implements OnInit {
|
||||||
})
|
})
|
||||||
.then(window.open)
|
.then(window.open)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error(err)
|
logger.error(err)
|
||||||
|
|
||||||
this.notifier.error($localize`Cannot fetch information of this remote account`)
|
this.notifier.error($localize`Cannot fetch information of this remote account`)
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/sha
|
||||||
import { ActorFollow, ResultList, VideoChannel as VideoChannelServer, VideoSortField } from '@shared/models'
|
import { ActorFollow, ResultList, VideoChannel as VideoChannelServer, VideoSortField } from '@shared/models'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
|
|
||||||
const logger = debug('peertube:subscriptions:UserSubscriptionService')
|
const debugLogger = debug('peertube:subscriptions:UserSubscriptionService')
|
||||||
|
|
||||||
type SubscriptionExistResult = { [ uri: string ]: boolean }
|
type SubscriptionExistResult = { [ uri: string ]: boolean }
|
||||||
type SubscriptionExistResultObservable = { [ uri: string ]: Observable<boolean> }
|
type SubscriptionExistResultObservable = { [ uri: string ]: Observable<boolean> }
|
||||||
|
@ -176,17 +176,17 @@ export class UserSubscriptionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
doesSubscriptionExist (nameWithHost: string) {
|
doesSubscriptionExist (nameWithHost: string) {
|
||||||
logger('Running subscription check for %d.', nameWithHost)
|
debugLogger('Running subscription check for %d.', nameWithHost)
|
||||||
|
|
||||||
if (nameWithHost in this.myAccountSubscriptionCache) {
|
if (nameWithHost in this.myAccountSubscriptionCache) {
|
||||||
logger('Found cache for %d.', nameWithHost)
|
debugLogger('Found cache for %d.', nameWithHost)
|
||||||
|
|
||||||
return of(this.myAccountSubscriptionCache[nameWithHost])
|
return of(this.myAccountSubscriptionCache[nameWithHost])
|
||||||
}
|
}
|
||||||
|
|
||||||
this.existsSubject.next(nameWithHost)
|
this.existsSubject.next(nameWithHost)
|
||||||
|
|
||||||
logger('Fetching from network for %d.', nameWithHost)
|
debugLogger('Fetching from network for %d.', nameWithHost)
|
||||||
return this.existsObservable.pipe(
|
return this.existsObservable.pipe(
|
||||||
filter(existsResult => existsResult[nameWithHost] !== undefined),
|
filter(existsResult => existsResult[nameWithHost] !== undefined),
|
||||||
map(existsResult => existsResult[nameWithHost]),
|
map(existsResult => existsResult[nameWithHost]),
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { HttpClient, HttpParams } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { RestExtractor, RestPagination, RestService, UserService } from '@app/core'
|
import { RestExtractor, RestPagination, RestService, UserService } from '@app/core'
|
||||||
import { getBytes } from '@root-helpers/bytes'
|
import { getBytes } from '@root-helpers/bytes'
|
||||||
|
import { arrayify } from '@shared/core-utils'
|
||||||
import { ResultList, User as UserServerModel, UserCreate, UserRole, UserUpdate } from '@shared/models'
|
import { ResultList, User as UserServerModel, UserCreate, UserRole, UserUpdate } from '@shared/models'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -65,7 +66,7 @@ export class UserAdminService {
|
||||||
}
|
}
|
||||||
|
|
||||||
removeUser (usersArg: UserServerModel | UserServerModel[]) {
|
removeUser (usersArg: UserServerModel | UserServerModel[]) {
|
||||||
const users = Array.isArray(usersArg) ? usersArg : [ usersArg ]
|
const users = arrayify(usersArg)
|
||||||
|
|
||||||
return from(users)
|
return from(users)
|
||||||
.pipe(
|
.pipe(
|
||||||
|
@ -77,7 +78,7 @@ export class UserAdminService {
|
||||||
|
|
||||||
banUsers (usersArg: UserServerModel | UserServerModel[], reason?: string) {
|
banUsers (usersArg: UserServerModel | UserServerModel[], reason?: string) {
|
||||||
const body = reason ? { reason } : {}
|
const body = reason ? { reason } : {}
|
||||||
const users = Array.isArray(usersArg) ? usersArg : [ usersArg ]
|
const users = arrayify(usersArg)
|
||||||
|
|
||||||
return from(users)
|
return from(users)
|
||||||
.pipe(
|
.pipe(
|
||||||
|
@ -88,7 +89,7 @@ export class UserAdminService {
|
||||||
}
|
}
|
||||||
|
|
||||||
unbanUsers (usersArg: UserServerModel | UserServerModel[]) {
|
unbanUsers (usersArg: UserServerModel | UserServerModel[]) {
|
||||||
const users = Array.isArray(usersArg) ? usersArg : [ usersArg ]
|
const users = arrayify(usersArg)
|
||||||
|
|
||||||
return from(users)
|
return from(users)
|
||||||
.pipe(
|
.pipe(
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user