Refactor admin plugins
This commit is contained in:
parent
9744bb2ae8
commit
2accfdd8ec
|
@ -302,11 +302,13 @@
|
||||||
"defaultProject": "PeerTube",
|
"defaultProject": "PeerTube",
|
||||||
"schematics": {
|
"schematics": {
|
||||||
"@schematics/angular:component": {
|
"@schematics/angular:component": {
|
||||||
"prefix": "app",
|
"prefix": "my",
|
||||||
"style": "scss"
|
"style": "scss",
|
||||||
|
"skipTests": true,
|
||||||
|
"flat": true
|
||||||
},
|
},
|
||||||
"@schematics/angular:directive": {
|
"@schematics/angular:directive": {
|
||||||
"prefix": "app"
|
"prefix": "my"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { ChartModule } from 'primeng/chart'
|
import { ChartModule } from 'primeng/chart'
|
||||||
import { SelectButtonModule } from 'primeng/selectbutton'
|
|
||||||
import { TableModule } from 'primeng/table'
|
import { TableModule } from 'primeng/table'
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { SharedAbuseListModule } from '@app/shared/shared-abuse-list'
|
import { SharedAbuseListModule } from '@app/shared/shared-abuse-list'
|
||||||
|
@ -45,7 +44,7 @@ import {
|
||||||
PluginApiService,
|
PluginApiService,
|
||||||
PluginCardComponent,
|
PluginCardComponent,
|
||||||
PluginListInstalledComponent,
|
PluginListInstalledComponent,
|
||||||
PluginsComponent,
|
PluginNavigationComponent,
|
||||||
PluginSearchComponent,
|
PluginSearchComponent,
|
||||||
PluginShowInstalledComponent
|
PluginShowInstalledComponent
|
||||||
} from './plugins'
|
} from './plugins'
|
||||||
|
@ -70,7 +69,6 @@ import { JobsComponent } from './system/jobs/jobs.component'
|
||||||
SharedTablesModule,
|
SharedTablesModule,
|
||||||
|
|
||||||
TableModule,
|
TableModule,
|
||||||
SelectButtonModule,
|
|
||||||
ChartModule
|
ChartModule
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -98,11 +96,11 @@ import { JobsComponent } from './system/jobs/jobs.component'
|
||||||
InstanceServerBlocklistComponent,
|
InstanceServerBlocklistComponent,
|
||||||
InstanceAccountBlocklistComponent,
|
InstanceAccountBlocklistComponent,
|
||||||
|
|
||||||
PluginsComponent,
|
|
||||||
PluginListInstalledComponent,
|
PluginListInstalledComponent,
|
||||||
PluginSearchComponent,
|
PluginSearchComponent,
|
||||||
PluginShowInstalledComponent,
|
PluginShowInstalledComponent,
|
||||||
PluginCardComponent,
|
PluginCardComponent,
|
||||||
|
PluginNavigationComponent,
|
||||||
|
|
||||||
JobsComponent,
|
JobsComponent,
|
||||||
LogsComponent,
|
LogsComponent,
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
export * from './plugins.component'
|
|
||||||
export * from './shared'
|
export * from './shared'
|
||||||
export * from './plugin-list-installed'
|
export * from './plugin-list-installed'
|
||||||
export * from './plugin-search'
|
export * from './plugin-search'
|
||||||
export * from './plugin-show-installed'
|
export * from './plugin-show-installed'
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
<div class="toggle-plugin-type">
|
<my-plugin-navigation [pluginType]="pluginType"></my-plugin-navigation>
|
||||||
<p-selectButton [options]="pluginTypeOptions" [(ngModel)]="pluginType" (ngModelChange)="reloadPlugins()"></p-selectButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="no-results" *ngIf="pagination.totalItems === 0">
|
<div class="no-results" *ngIf="pagination.totalItems === 0">
|
||||||
{{ getNoResultMessage() }}
|
{{ getNoResultMessage() }}
|
||||||
|
|
|
@ -10,14 +10,10 @@ import { PeerTubePlugin, PluginType } from '@shared/models'
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-plugin-list-installed',
|
selector: 'my-plugin-list-installed',
|
||||||
templateUrl: './plugin-list-installed.component.html',
|
templateUrl: './plugin-list-installed.component.html',
|
||||||
styleUrls: [
|
styleUrls: [ './plugin-list-installed.component.scss' ]
|
||||||
'../shared/toggle-plugin-type.scss',
|
|
||||||
'./plugin-list-installed.component.scss'
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
export class PluginListInstalledComponent implements OnInit {
|
export class PluginListInstalledComponent implements OnInit {
|
||||||
pluginTypeOptions: { label: string, value: PluginType }[] = []
|
pluginType: PluginType
|
||||||
pluginType: PluginType = PluginType.PLUGIN
|
|
||||||
|
|
||||||
pagination: ComponentPagination = {
|
pagination: ComponentPagination = {
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
|
@ -39,22 +35,28 @@ export class PluginListInstalledComponent implements OnInit {
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private route: ActivatedRoute
|
private route: ActivatedRoute
|
||||||
) {
|
) {
|
||||||
this.pluginTypeOptions = this.pluginApiService.getPluginTypeOptions()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
const query = this.route.snapshot.queryParams
|
if (!this.route.snapshot.queryParams['pluginType']) {
|
||||||
if (query['pluginType']) this.pluginType = parseInt(query['pluginType'], 10)
|
const queryParams = { pluginType: PluginType.PLUGIN }
|
||||||
|
|
||||||
this.reloadPlugins()
|
this.router.navigate([], { queryParams })
|
||||||
|
}
|
||||||
|
|
||||||
|
this.route.queryParams.subscribe(query => {
|
||||||
|
if (!query['pluginType']) return
|
||||||
|
|
||||||
|
this.pluginType = parseInt(query['pluginType'], 10)
|
||||||
|
|
||||||
|
this.reloadPlugins()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadPlugins () {
|
reloadPlugins () {
|
||||||
this.pagination.currentPage = 1
|
this.pagination.currentPage = 1
|
||||||
this.plugins = []
|
this.plugins = []
|
||||||
|
|
||||||
this.router.navigate([], { queryParams: { pluginType: this.pluginType } })
|
|
||||||
|
|
||||||
this.loadMorePlugins()
|
this.loadMorePlugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,27 @@
|
||||||
<div class="toggle-plugin-type">
|
<my-plugin-navigation [pluginType]="pluginType"></my-plugin-navigation>
|
||||||
<p-selectButton [options]="pluginTypeOptions" [(ngModel)]="pluginType" (ngModelChange)="reloadPlugins()"></p-selectButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="search-bar">
|
|
||||||
<input type="text" (input)="onSearchChange($event)" i18n-placeholder placeholder="Search..." myAutofocus />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-info" i18n *ngIf="pluginInstalled">
|
<div class="alert alert-info" i18n *ngIf="pluginInstalled">
|
||||||
To load your new installed plugins or themes, refresh the page.
|
To load your new installed plugins or themes, refresh the page.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="result-title" *ngIf="!isSearching">
|
<div class="result-and-search">
|
||||||
<ng-container *ngIf="!search">
|
<ng-container *ngIf="!search">
|
||||||
<my-global-icon iconName="trending" aria-hidden="true"></my-global-icon>
|
<my-global-icon iconName="trending" aria-hidden="true"></my-global-icon>
|
||||||
<ng-container i18n>Popular</ng-container>
|
<ng-container *ngIf="!isThemeSearch()" i18n>Popular plugins</ng-container>
|
||||||
|
<ng-container *ngIf="isThemeSearch()" i18n>Popular themes</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="!!search">
|
<ng-container *ngIf="search && !isSearching">
|
||||||
<my-global-icon iconName="search"></my-global-icon>
|
<my-global-icon iconName="search"></my-global-icon>
|
||||||
|
|
||||||
<ng-container i18n>
|
<ng-container i18n>
|
||||||
{{ pagination.totalItems }} {pagination.totalItems, plural, =1 {result} other {results}} for "{{ search }}"
|
{{ pagination.totalItems }} {pagination.totalItems, plural, =1 {result} other {results}} for "{{ search }}"
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<div class="search-bar">
|
||||||
|
<input type="text" (input)="onSearchChange($event)" i18n-placeholder placeholder="Search..." myAutofocus />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="no-results" i18n *ngIf="pagination.totalItems === 0">
|
<div class="no-results" i18n *ngIf="pagination.totalItems === 0">
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
@use '_variables' as *;
|
@use '_variables' as *;
|
||||||
@use '_mixins' as *;
|
@use '_mixins' as *;
|
||||||
|
|
||||||
.search-bar {
|
.result-and-search {
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 30px 0;
|
|
||||||
|
|
||||||
input {
|
|
||||||
@include peertube-input-text(60%);
|
|
||||||
height: 35px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-title {
|
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-bottom: 15px;
|
margin: 30px 0 15px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
my-global-icon {
|
my-global-icon {
|
||||||
@include margin-right(5px);
|
@include margin-right(5px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
|
input {
|
||||||
|
@include peertube-input-text(500px);
|
||||||
|
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
@include margin-left(15px);
|
@include margin-left(15px);
|
||||||
|
|
||||||
|
|
|
@ -9,14 +9,10 @@ import { PeerTubePluginIndex, PluginType } from '@shared/models'
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-plugin-search',
|
selector: 'my-plugin-search',
|
||||||
templateUrl: './plugin-search.component.html',
|
templateUrl: './plugin-search.component.html',
|
||||||
styleUrls: [
|
styleUrls: [ './plugin-search.component.scss' ]
|
||||||
'../shared/toggle-plugin-type.scss',
|
|
||||||
'./plugin-search.component.scss'
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
export class PluginSearchComponent implements OnInit {
|
export class PluginSearchComponent implements OnInit {
|
||||||
pluginTypeOptions: { label: string, value: PluginType }[] = []
|
pluginType: PluginType
|
||||||
pluginType: PluginType = PluginType.PLUGIN
|
|
||||||
|
|
||||||
pagination: ComponentPagination = {
|
pagination: ComponentPagination = {
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
|
@ -44,24 +40,30 @@ export class PluginSearchComponent implements OnInit {
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private route: ActivatedRoute
|
private route: ActivatedRoute
|
||||||
) {
|
) {
|
||||||
this.pluginTypeOptions = this.pluginApiService.getPluginTypeOptions()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
const query = this.route.snapshot.queryParams
|
if (!this.route.snapshot.queryParams['pluginType']) {
|
||||||
if (query['pluginType']) this.pluginType = parseInt(query['pluginType'], 10)
|
const queryParams = { pluginType: PluginType.PLUGIN }
|
||||||
|
|
||||||
|
this.router.navigate([], { queryParams })
|
||||||
|
}
|
||||||
|
|
||||||
|
this.route.queryParams.subscribe(query => {
|
||||||
|
if (!query['pluginType']) return
|
||||||
|
|
||||||
|
this.pluginType = parseInt(query['pluginType'], 10)
|
||||||
|
this.search = query['search'] || ''
|
||||||
|
|
||||||
|
this.reloadPlugins()
|
||||||
|
})
|
||||||
|
|
||||||
this.searchSubject.asObservable()
|
this.searchSubject.asObservable()
|
||||||
.pipe(
|
.pipe(
|
||||||
debounceTime(400),
|
debounceTime(400),
|
||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
)
|
)
|
||||||
.subscribe(search => {
|
.subscribe(search => this.router.navigate([], { queryParams: { search }, queryParamsHandling: 'merge' }))
|
||||||
this.search = search
|
|
||||||
this.reloadPlugins()
|
|
||||||
})
|
|
||||||
|
|
||||||
this.reloadPlugins()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearchChange (event: Event) {
|
onSearchChange (event: Event) {
|
||||||
|
@ -74,8 +76,6 @@ export class PluginSearchComponent implements OnInit {
|
||||||
this.pagination.currentPage = 1
|
this.pagination.currentPage = 1
|
||||||
this.plugins = []
|
this.plugins = []
|
||||||
|
|
||||||
this.router.navigate([], { queryParams: { pluginType: this.pluginType } })
|
|
||||||
|
|
||||||
this.loadMorePlugins()
|
this.loadMorePlugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
<div class="admin-sub-header">
|
|
||||||
<div class="admin-sub-nav">
|
|
||||||
<a i18n routerLink="list-installed" routerLinkActive="active">Installed</a>
|
|
||||||
|
|
||||||
<a i18n routerLink="search" routerLinkActive="active">Search</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
|
|
@ -1,7 +0,0 @@
|
||||||
import { Component } from '@angular/core'
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
templateUrl: './plugins.component.html'
|
|
||||||
})
|
|
||||||
export class PluginsComponent {
|
|
||||||
}
|
|
|
@ -2,14 +2,12 @@ import { Routes } from '@angular/router'
|
||||||
import { PluginListInstalledComponent } from '@app/+admin/plugins/plugin-list-installed/plugin-list-installed.component'
|
import { PluginListInstalledComponent } from '@app/+admin/plugins/plugin-list-installed/plugin-list-installed.component'
|
||||||
import { PluginSearchComponent } from '@app/+admin/plugins/plugin-search/plugin-search.component'
|
import { PluginSearchComponent } from '@app/+admin/plugins/plugin-search/plugin-search.component'
|
||||||
import { PluginShowInstalledComponent } from '@app/+admin/plugins/plugin-show-installed/plugin-show-installed.component'
|
import { PluginShowInstalledComponent } from '@app/+admin/plugins/plugin-show-installed/plugin-show-installed.component'
|
||||||
import { PluginsComponent } from '@app/+admin/plugins/plugins.component'
|
|
||||||
import { UserRightGuard } from '@app/core'
|
import { UserRightGuard } from '@app/core'
|
||||||
import { UserRight } from '@shared/models'
|
import { UserRight } from '@shared/models'
|
||||||
|
|
||||||
export const PluginsRoutes: Routes = [
|
export const PluginsRoutes: Routes = [
|
||||||
{
|
{
|
||||||
path: 'plugins',
|
path: 'plugins',
|
||||||
component: PluginsComponent,
|
|
||||||
canActivate: [ UserRightGuard ],
|
canActivate: [ UserRightGuard ],
|
||||||
data: {
|
data: {
|
||||||
userRight: UserRight.MANAGE_PLUGINS
|
userRight: UserRight.MANAGE_PLUGINS
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
export * from './plugin-api.service'
|
export * from './plugin-api.service'
|
||||||
export * from './plugin-card.component'
|
export * from './plugin-card.component'
|
||||||
|
export * from './plugin-navigation.component'
|
||||||
|
|
|
@ -25,19 +25,6 @@ export class PluginApiService {
|
||||||
private pluginService: PluginService
|
private pluginService: PluginService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
getPluginTypeOptions () {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
label: $localize`Plugins`,
|
|
||||||
value: PluginType.PLUGIN
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: $localize`Themes`,
|
|
||||||
value: PluginType.THEME
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
getPluginTypeLabel (type: PluginType) {
|
getPluginTypeLabel (type: PluginType) {
|
||||||
if (type === PluginType.PLUGIN) {
|
if (type === PluginType.PLUGIN) {
|
||||||
return $localize`plugin`
|
return $localize`plugin`
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<div class="root">
|
||||||
|
<div class="btn-group" role="group" i18n-aria-label aria-label="Navigate between installed plugins and themes or find new ones">
|
||||||
|
<a i18n routerLink="/admin/plugins/list-installed" [queryParams]="{ pluginType: pluginType }" routerLinkActive="active">Installed</a>
|
||||||
|
<a i18n routerLink="/admin/plugins/search" [queryParams]="{ pluginType: pluginType }" routerLinkActive="active">Search</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-group" role="group" i18n-aria-label aria-label="Navigate between plugins and themes">
|
||||||
|
<a [ngClass]="{ active: pluginType === 1 }" routerLink="." [queryParams]="{ pluginType: 1 }" queryParamsHandling="merge" class="">Plugins</a>
|
||||||
|
<a [ngClass]="{ active: pluginType === 2 }" routerLink="." [queryParams]="{ pluginType: 2 }" queryParamsHandling="merge" class="">Themes</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,8 +1,11 @@
|
||||||
@use '_variables' as *;
|
@use '_variables' as *;
|
||||||
@use '_mixins' as *;
|
@use '_mixins' as *;
|
||||||
|
|
||||||
.toggle-plugin-type {
|
.root {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-bottom: 30px;
|
}
|
||||||
|
|
||||||
|
.btn-group:not(:last-child) {
|
||||||
|
@include margin-right(15px);
|
||||||
}
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { Component, Input } from '@angular/core'
|
||||||
|
import { PluginType } from '@shared/models/plugins'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-plugin-navigation',
|
||||||
|
templateUrl: './plugin-navigation.component.html',
|
||||||
|
styleUrls: [ './plugin-navigation.component.scss' ]
|
||||||
|
})
|
||||||
|
export class PluginNavigationComponent {
|
||||||
|
@Input() pluginType: PluginType
|
||||||
|
}
|
|
@ -261,28 +261,6 @@ my-input-toggle-hidden ::ng-deep input {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
|
|
||||||
.admin-sub-nav a {
|
|
||||||
@include disable-default-a-behaviour;
|
|
||||||
|
|
||||||
font-size: 16px;
|
|
||||||
color: pvar(--mainForegroundColor);
|
|
||||||
padding: 5px 15px;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
font-weight: $font-semibold;
|
|
||||||
opacity: 0.6;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background-color: pvar(--submenuBackgroundColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active,
|
|
||||||
&:hover,
|
|
||||||
&:active,
|
|
||||||
&:focus {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// In tables, don't have a hover different background
|
// In tables, don't have a hover different background
|
||||||
|
@ -402,19 +380,6 @@ ngx-loading-bar {
|
||||||
|
|
||||||
.admin-sub-header {
|
.admin-sub-header {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.admin-sub-nav {
|
|
||||||
display: block;
|
|
||||||
overflow-x: auto;
|
|
||||||
white-space: nowrap;
|
|
||||||
height: 50px;
|
|
||||||
padding: 10px 0;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
a {
|
|
||||||
@include margin-left(5px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
my-markdown-textarea {
|
my-markdown-textarea {
|
||||||
|
|
|
@ -334,6 +334,34 @@ ngb-tooltip-window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-group {
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
|
||||||
|
.active {
|
||||||
|
@include orange-button;
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(.active) {
|
||||||
|
@include grey-button;
|
||||||
|
}
|
||||||
|
|
||||||
|
> * {
|
||||||
|
@include peertube-button-link;
|
||||||
|
|
||||||
|
box-shadow: none !important;
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
border-top-left-radius: 0 !important;
|
||||||
|
border-bottom-left-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-top-right-radius: 0 !important;
|
||||||
|
border-bottom-right-radius: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// input box-shadow on focus
|
// input box-shadow on focus
|
||||||
.form-control {
|
.form-control {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
|
Loading…
Reference in New Issue