pull/5817/head
Chocobozzz 2023-05-24 15:27:15 +02:00
parent 25ea1d85e1
commit d0fbc9fd0a
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
100 changed files with 357 additions and 390 deletions

View File

@ -18,8 +18,7 @@
},
"extends": [
"../.eslintrc.json",
"plugin:@angular-eslint/ng-cli-compat",
"plugin:@angular-eslint/ng-cli-compat--formatting-add-on",
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
@ -155,6 +154,10 @@
"object-shorthand": [
"error",
"properties"
],
"quote-props": [
"error",
"consistent-as-needed"
]
}
},
@ -163,7 +166,8 @@
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
"plugin:@angular-eslint/template/recommended",
"plugin:@angular-eslint/template/accessibility"
],
"rules": {}
}

View File

@ -25,15 +25,15 @@ module.exports = {
capabilities: [
{
browserName: 'chrome',
acceptInsecureCerts: true,
'browserName': 'chrome',
'acceptInsecureCerts': true,
'goog:chromeOptions': {
args: [ '--disable-gpu', windowSizeArg ],
prefs
}
},
{
browserName: 'firefox',
'browserName': 'firefox',
'moz:firefoxOptions': {
binary: '/usr/bin/firefox-developer-edition',
args: [ '--headless', windowSizeArg ],

View File

@ -20,14 +20,14 @@ module.exports = {
capabilities: [
{
browserName: 'chrome',
'browserName': 'chrome',
'goog:chromeOptions': {
args: [ '--headless', '--disable-gpu', windowSizeArg ],
prefs
}
},
{
browserName: 'firefox',
'browserName': 'firefox',
'moz:firefoxOptions': {
binary: '/usr/bin/firefox-developer-edition',
args: [ '--headless', windowSizeArg ],

View File

@ -16,7 +16,7 @@ export type ResolverData = {
}
@Injectable()
export class AboutInstanceResolver {
export class AboutInstanceResolver {
constructor (
private instanceService: InstanceService,

View File

@ -48,12 +48,12 @@
<div class="description-html" [innerHTML]="accountDescriptionHTML"></div>
</div>
<div *ngIf="hasShowMoreDescription()" class="show-more d-md-none d-block" role="button"
<button *ngIf="hasShowMoreDescription()" class="show-more d-md-none d-block button-unstyle"
(click)="accountDescriptionExpanded = !accountDescriptionExpanded"
title="Show the complete description" i18n-title i18n
>
Show more...
</div>
</button>
<div class="buttons">
<a *ngIf="isManageable()" routerLink="/my-account" class="peertube-button-link orange-button" i18n>

View File

@ -3,7 +3,7 @@
<div class="row mt-5"> <!-- cache grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">CACHE</div>
<h2 i18n class="inner-form-title">CACHE</h2>
<div i18n class="inner-form-description">
Some files are not federated, and fetched when necessary. Define their caching policies.
</div>
@ -60,7 +60,7 @@
<div class="row mt-4"> <!-- cache grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div class="anchor" id="customizations"></div> <!-- customizations anchor -->
<div i18n class="inner-form-title">CUSTOMIZATIONS</div>
<h2 i18n class="inner-form-title">CUSTOMIZATIONS</h2>
<div i18n class="inner-form-description">
Slight modifications to your PeerTube instance for when creating a plugin or theme is overkill.
</div>

View File

@ -1,7 +1,7 @@
<ng-container [formGroup]="form">
<div class="row mt-5"> <!-- appearance grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">APPEARANCE</div>
<h2 i18n class="inner-form-title">APPEARANCE</h2>
<div i18n class="inner-form-description">
Use <a class="link-orange" routerLink="/admin/plugins">plugins & themes</a> for more involved changes, or add slight <a class="link-orange" routerLink="/admin/config/edit-custom" fragment="advanced-configuration">customizations</a>.
</div>
@ -91,7 +91,7 @@
<div class="row mt-4"> <!-- broadcast grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">BROADCAST MESSAGE</div>
<h2 i18n class="inner-form-title">BROADCAST MESSAGE</h2>
<div i18n class="inner-form-description">
Display a message on your instance
</div>
@ -147,7 +147,7 @@
<div class="row mt-4"> <!-- new users grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">NEW USERS</div>
<h2 i18n class="inner-form-title">NEW USERS</h2>
<div i18n class="inner-form-description">
Manage <a class="link-orange" routerLink="/admin/users">users</a> to set their quota individually.
</div>
@ -264,7 +264,7 @@
<div class="row mt-4"> <!-- videos grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">VIDEOS</div>
<h2 i18n class="inner-form-title">VIDEOS</h2>
</div>
<div class="col-12 col-lg-8 col-xl-9">
@ -350,7 +350,7 @@
<div class="row mt-4"> <!-- video channels grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">VIDEO CHANNELS</div>
<h2 i18n class="inner-form-title">VIDEO CHANNELS</h2>
</div>
<div class="col-12 col-lg-8 col-xl-9">
@ -372,7 +372,7 @@
<div class="row mt-4"> <!-- search grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">SEARCH</div>
<h2 i18n class="inner-form-title">SEARCH</h2>
</div>
<div class="col-12 col-lg-8 col-xl-9">
@ -461,7 +461,7 @@
<div class="row mt-4"> <!-- federation grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">FEDERATION</div>
<h2 i18n class="inner-form-title">FEDERATION</h2>
<div i18n class="inner-form-description">
Manage <a class="link-orange" routerLink="/admin/follows">relations</a> with other instances.
</div>
@ -540,7 +540,7 @@
<div class="row mt-4"> <!-- administrators grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">ADMINISTRATORS</div>
<h2 i18n class="inner-form-title">ADMINISTRATORS</h2>
</div>
<div class="col-12 col-lg-8 col-xl-9">
@ -568,7 +568,7 @@
<div class="row mt-4"> <!-- Twitter grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">TWITTER</div>
<h2 i18n class="inner-form-title">TWITTER</h2>
<div i18n class="inner-form-description">
Provide the Twitter account representing your instance to improve link previews.
If you don't have a Twitter account, just leave the default value.

View File

@ -4,7 +4,7 @@
<div class="homepage row mt-5"> <!-- homepage grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">INSTANCE HOMEPAGE</div>
<h2 i18n class="inner-form-title">INSTANCE HOMEPAGE</h2>
</div>
<div class="col-12 col-lg-8 col-xl-9">

View File

@ -4,7 +4,7 @@
<div class="row mt-5"> <!-- instance grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">INSTANCE</div>
<h2 i18n class="inner-form-title">INSTANCE</h2>
</div>
<div class="col-12 col-lg-8 col-xl-9">
@ -76,7 +76,7 @@
<div class="row mt-4"> <!-- moderation & nsfw grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">MODERATION & NSFW</div>
<h2 i18n class="inner-form-title">MODERATION & NSFW</h2>
<div i18n class="inner-form-description">
Manage <a class="link-orange" routerLink="/admin/users">users</a> to build a moderation team.
</div>
@ -154,7 +154,7 @@
<div class="row mt-4"> <!-- you and your instance grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">YOU AND YOUR INSTANCE</div>
<h2 i18n class="inner-form-title">YOU AND YOUR INSTANCE</h2>
</div>
<div class="col-12 col-lg-8 col-xl-9">
@ -204,7 +204,7 @@
<div class="row mt-4"> <!-- other information grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">OTHER INFORMATION</div>
<h2 i18n class="inner-form-title">OTHER INFORMATION</h2>
</div>
<div class="col-12 col-lg-8 col-xl-9">

View File

@ -2,7 +2,7 @@
<div class="row mt-5">
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">LIVE</div>
<h2 i18n class="inner-form-title">LIVE</h2>
<div i18n class="inner-form-description">
Enable users of your instance to stream live.
</div>
@ -89,7 +89,7 @@
<div class="row"> <!-- transcoding live streams grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">TRANSCODING</div>
<h2 i18n class="inner-form-title">TRANSCODING</h2>
<div i18n class="inner-form-description">
Same as VOD transcoding, transcoding live streams so that they are in a streamable form that any device can play. Requires a beefy CPU, and then some.
</div>
@ -111,7 +111,7 @@
</div>
<div class="callout callout-light pt-2 mt-2 pb-0">
<label i18n>Output formats</label>
<h3 class="callout-title" i18n>Output formats</h3>
<div class="form-group" [ngClass]="getDisabledLiveTranscodingClass()">
<label i18n for="liveTranscodingThreads">Live resolutions to generate</label>

View File

@ -18,7 +18,7 @@
<div class="row mt-4"> <!-- transcoding grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">TRANSCODING</div>
<h2 i18n class="inner-form-title">TRANSCODING</h2>
<div i18n class="inner-form-description">
Process uploaded videos so that they are in a streamable form that any device can play. Though costly in
resources, this is a critical part of PeerTube, so tread carefully.
@ -38,7 +38,7 @@
<ng-container ngProjectAs="extra">
<div class="callout callout-light pt-2 pb-0">
<label i18n>Input formats</label>
<h3 class="callout-title" i18n>Input formats</h3>
<div class="form-group" [ngClass]="getTranscodingDisabledClass()">
<my-peertube-checkbox
@ -65,7 +65,7 @@
</div>
<div class="callout callout-light pt-2 mt-2 pb-0">
<label i18n>Output formats</label>
<h3 class="callout-title" i18n>Output formats</h3>
<ng-container formGroupName="webtorrent">
<div class="form-group" [ngClass]="getTranscodingDisabledClass()">
@ -108,7 +108,7 @@
</ng-container>
<div class="form-group" [ngClass]="getTranscodingDisabledClass()">
<label i18n>Resolutions to generate</label>
<div class="mb-2 fw-bold" i18n>Resolutions to generate</div>
<div class="ms-2 d-flex flex-column">
<my-peertube-checkbox
@ -211,7 +211,7 @@
<div class="row mt-2"> <!-- video studio grid -->
<div class="col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">VIDEO STUDIO</div>
<h2 i18n class="inner-form-title">VIDEO STUDIO</h2>
<div i18n class="inner-form-description">
Allows your users to edit their video (cut, add intro/outro, add a watermark etc)
</div>

View File

@ -20,10 +20,10 @@
>
</my-action-dropdown>
<a *ngIf="!isInSelectionMode()" class="follow-button" (click)="openFollowModal()" (key.enter)="openFollowModal()">
<button *ngIf="!isInSelectionMode()" class="peertube-create-button" (click)="openFollowModal()">
<my-global-icon iconName="following" aria-hidden="true"></my-global-icon>
<ng-container i18n>Follow</ng-container>
</a>
</button>
</div>
<div class="ms-auto">

View File

@ -16,10 +16,6 @@ a {
}
}
.follow-button {
@include create-button;
}
my-delete-button {
max-width: 130px;
}

View File

@ -219,17 +219,17 @@
<div class="danger-zone">
<div class="form-group">
<label i18n>Send a link to reset the password by email to the user</label>
<div class="mb-1 fw-bold" i18n>Send a link to reset the password by email to the user</div>
<button (click)="resetPassword()" i18n>Ask for new password</button>
</div>
<div class="form-group">
<label i18n>Manually set the user password</label>
<div class="mb-1 fw-bold" i18n>Manually set the user password</div>
<my-user-password [userId]="user.id" [username]="user.username"></my-user-password>
</div>
<div *ngIf="user.twoFactorEnabled" class="form-group">
<label i18n>This user has two factor authentication enabled</label>
<div class="mb-1 fw-bold" i18n>This user has two factor authentication enabled</div>
<button (click)="disableTwoFactorAuth()" i18n>Disable two factor authentication</button>
</div>
</div>

View File

@ -20,7 +20,7 @@
>
</my-action-dropdown>
<a *ngIf="!isInSelectionMode()" class="add-button" routerLink="/admin/users/create">
<a *ngIf="!isInSelectionMode()" class="peertube-create-button" routerLink="/admin/users/create">
<my-global-icon iconName="user-add" aria-hidden="true"></my-global-icon>
<ng-container i18n>Create user</ng-container>
</a>

View File

@ -2,10 +2,6 @@
@use '_mixins' as *;
@use 'bootstrap/scss/functions' as *;
.add-button {
@include create-button;
}
tr.banned > td {
background-color: lighten($color: $red, $amount: 40) !important;
}

View File

@ -81,10 +81,12 @@
<input type="submit" class="peertube-button orange-button w-100" i18n-value value="Login" [disabled]="!form.valid">
<div *ngIf="!otpStep" class="additional-links d-flex justify-content-center mt-4 mb-5">
<a i18n role="button" class="link-orange mx-3" (click)="openForgotPasswordModal()" i18n-title title="Click here to reset your password">I forgot my password</a>
<button i18n class="button-unstyle link-orange mx-3" (click)="openForgotPasswordModal()" i18n-title title="Click here to reset your password">
I forgot my password
</button>
<ng-container *ngIf="signupAllowed">
<span>·</span>
<span class="lh-1">·</span>
<a i18n routerLink="/signup" class="link-orange mx-3">Create an account</a>
</ng-container>
</div>

View File

@ -85,8 +85,8 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
// Avoid undefined errors when accessing form error properties
this.buildForm({
username: LOGIN_USERNAME_VALIDATOR,
password: LOGIN_PASSWORD_VALIDATOR,
'username': LOGIN_USERNAME_VALIDATOR,
'password': LOGIN_PASSWORD_VALIDATOR,
'otp-token': {
VALIDATORS: [], // Will be set dynamically
MESSAGES: USER_OTP_TOKEN_VALIDATOR.MESSAGES

View File

@ -38,10 +38,10 @@ export class VideoChannelCreateComponent extends VideoChannelEdit implements OnI
ngOnInit () {
this.buildForm({
name: VIDEO_CHANNEL_NAME_VALIDATOR,
'name': VIDEO_CHANNEL_NAME_VALIDATOR,
'display-name': VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR,
description: VIDEO_CHANNEL_DESCRIPTION_VALIDATOR,
support: VIDEO_CHANNEL_SUPPORT_VALIDATOR
'description': VIDEO_CHANNEL_DESCRIPTION_VALIDATOR,
'support': VIDEO_CHANNEL_SUPPORT_VALIDATOR
})
}

View File

@ -10,8 +10,6 @@
</div>
<div class="col-12 col-lg-8 col-xl-9">
<label i18n>Banner image of the channel</label>
<my-actor-banner-edit
*ngIf="videoChannel" [previewImage]="isCreation()" class="d-block mb-4"
[actor]="videoChannel" (bannerChange)="onBannerChange($event)" (bannerDelete)="onBannerDelete()"

View File

@ -46,9 +46,9 @@ export class VideoChannelUpdateComponent extends VideoChannelEdit implements OnI
this.buildForm({
'display-name': VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR,
description: VIDEO_CHANNEL_DESCRIPTION_VALIDATOR,
support: VIDEO_CHANNEL_SUPPORT_VALIDATOR,
bulkVideosSupportUpdate: null
'description': VIDEO_CHANNEL_DESCRIPTION_VALIDATOR,
'support': VIDEO_CHANNEL_SUPPORT_VALIDATOR,
'bulkVideosSupportUpdate': null
})
this.paramsSub = this.route.params.subscribe(routeParams => {
@ -65,8 +65,8 @@ export class VideoChannelUpdateComponent extends VideoChannelEdit implements OnI
this.form.patchValue({
'display-name': videoChannelToUpdate.displayName,
description: videoChannelToUpdate.description,
support: videoChannelToUpdate.support
'description': videoChannelToUpdate.description,
'support': videoChannelToUpdate.support
})
},

View File

@ -29,7 +29,7 @@ export class MyAccountChangeEmailComponent extends FormReactive implements OnIni
ngOnInit () {
this.buildForm({
'new-email': USER_EMAIL_VALIDATOR,
password: USER_PASSWORD_VALIDATOR
'password': USER_PASSWORD_VALIDATOR
})
this.user = this.authService.getUser()

View File

@ -25,17 +25,17 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit {
ngOnInit () {
this.buildForm({
username: null,
'username': null,
'display-name': USER_DISPLAY_NAME_REQUIRED_VALIDATOR,
description: USER_DESCRIPTION_VALIDATOR
'description': USER_DESCRIPTION_VALIDATOR
})
this.form.controls['username'].disable()
this.userInformationLoaded.subscribe(() => {
this.form.patchValue({
username: this.user.username,
'username': this.user.username,
'display-name': this.user.account.displayName,
description: this.user.account.description
'description': this.user.account.description
})
})
}

View File

@ -18,7 +18,7 @@
<div class="video-channels-header d-flex justify-content-between gap-2">
<my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
<a class="create-button" routerLink="/manage/create">
<a class="peertube-create-button" routerLink="/manage/create">
<my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
<ng-container i18n>Create video channel</ng-container>
</a>

View File

@ -17,10 +17,6 @@ h1 {
}
}
.create-button {
@include create-button;
}
input[type=text] {
@include peertube-input-text(300px);
}

View File

@ -10,8 +10,8 @@
</div>
<div class="history-switch">
<my-input-switch [(ngModel)]="videosHistoryEnabled" (ngModelChange)="onVideosHistoryChange()"></my-input-switch>
<label i18n>Track watch history</label>
<my-input-switch inputName="track-watch-history" [(ngModel)]="videosHistoryEnabled" (ngModelChange)="onVideosHistoryChange()"></my-input-switch>
<label for="track-watch-history" i18n>Track watch history</label>
</div>
<button class="delete-history" (click)="clearAllHistory()" i18n>

View File

@ -44,7 +44,7 @@
<a [href]="videoChangeOwnership.video.url" class="video-table-video-link" [title]="videoChangeOwnership.video.name" target="_blank" rel="noopener noreferrer">
<div class="video-table-video">
<div class="video-table-video-image">
<img [src]="videoChangeOwnership.video.thumbnailPath">
<img [src]="videoChangeOwnership.video.thumbnailPath" alt="">
</div>
<div class="video-table-video-text">
<div>

View File

@ -20,7 +20,7 @@
<ng-template pTemplate="caption">
<div class="caption">
<div class="left-buttons">
<a class="add-sync" routerLink="{{ getSyncCreateLink() }}">
<a class="peertube-create-button" routerLink="{{ getSyncCreateLink() }}">
<my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
<ng-container i18n>Add synchronization</ng-container>
</a>

View File

@ -2,12 +2,9 @@
@use '_mixins' as *;
@use '_actor' as *;
.add-sync {
@include create-button;
}
.actor {
@include actor-row($min-height: auto, $separator: true);
margin-bottom: 0;
padding-bottom: 0;
border: 0;

View File

@ -61,7 +61,7 @@
</div>
<div class="form-group">
<label i18n>Channel</label>
<label for="videoChannelIdl" i18n>Channel</label>
<my-select-channel
labelForId="videoChannelIdl" [items]="userVideoChannels" formControlName="videoChannelId"
@ -73,7 +73,7 @@
</div>
<div class="form-group">
<label i18n>Playlist thumbnail</label>
<label for="thumbnailfile" i18n>Playlist thumbnail</label>
<my-preview-upload
i18n-inputLabel inputLabel="Edit" inputName="thumbnailfile" formControlName="thumbnailfile"

View File

@ -9,7 +9,7 @@
<div class="video-playlists-header d-flex justify-content-between gap-2">
<my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
<a class="create-button" routerLink="create">
<a class="peertube-create-button" routerLink="create">
<my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
<ng-container i18n>Create playlist</ng-container>
</a>

View File

@ -5,10 +5,6 @@ h1 {
display: flex;
}
.create-button {
@include create-button;
}
input[type=text] {
@include peertube-input-text(300px);
}

View File

@ -46,10 +46,10 @@
#videosSelection
>
<ng-template ptTemplate="globalButtons">
<span class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()">
<button class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()">
<my-global-icon iconName="delete" aria-hidden="true"></my-global-icon>
<ng-container i18n>Delete</ng-container>
</span>
</button>
</ng-template>
<ng-template ptTemplate="rowButtons" let-video>

View File

@ -27,7 +27,7 @@ export class ResetPasswordComponent extends FormReactive implements OnInit {
ngOnInit () {
this.buildForm({
password: USER_PASSWORD_VALIDATOR,
'password': USER_PASSWORD_VALIDATOR,
'password-confirm': RESET_PASSWORD_CONFIRM_VALIDATOR
})

View File

@ -4,7 +4,7 @@
<div class="col-lg-4 col-md-6 col-xs-12">
<div class="form-group">
<div class="radio-label label-container">
<label i18n>Sort</label>
<label for="sort" i18n>Sort</label>
<button i18n class="reset-button reset-button-small" (click)="resetField('sort', '-match')" *ngIf="advancedSearch.sort !== '-match'">
Reset
</button>
@ -18,7 +18,7 @@
<div class="form-group">
<div class="radio-label label-container">
<label i18n>Display only</label>
<label for="isLive" i18n>Display only</label>
<button i18n class="reset-button reset-button-small" (click)="resetField('isLive')" *ngIf="advancedSearch.isLive !== undefined">
Reset
</button>
@ -37,7 +37,7 @@
<div class="form-group">
<div class="radio-label label-container">
<label i18n>Display sensitive content</label>
<label for="sensitiveContent" i18n>Display sensitive content</label>
<button i18n class="reset-button reset-button-small" (click)="resetField('nsfw')" *ngIf="advancedSearch.nsfw !== undefined">
Reset
</button>
@ -56,7 +56,7 @@
<div class="form-group">
<div class="radio-label label-container">
<label i18n>Published date</label>
<label for="publishedDateRange" i18n>Published date</label>
<button i18n class="reset-button reset-button-small" (click)="resetLocalField('publishedDateRange')" *ngIf="publishedDateRange !== undefined">
Reset
</button>
@ -105,7 +105,7 @@
<div class="col-lg-4 col-md-6 col-xs-12">
<div class="form-group">
<div class="radio-label label-container">
<label i18n>Duration</label>
<label for="durationRange" i18n>Duration</label>
<button i18n class="reset-button reset-button-small" (click)="resetLocalField('durationRange')" *ngIf="durationRange !== undefined">
Reset
</button>
@ -184,7 +184,7 @@
<div class="form-group">
<div class="radio-label label-container">
<label i18n>Result types</label>
<label for="resultType" i18n>Result types</label>
<button i18n class="reset-button reset-button-small" (click)="resetField('resultType')" *ngIf="advancedSearch.resultType !== undefined">
Reset
</button>
@ -209,7 +209,7 @@
<div class="form-group" *ngIf="isSearchTargetEnabled()">
<div class="radio-label label-container">
<label i18n>Search target</label>
<label for="searchTarget" i18n>Search target</label>
</div>
<div class="peertube-radio-container">

View File

@ -10,8 +10,8 @@
<span *ngIf="currentSearch" i18n>for <span class="search-value">{{ currentSearch }}</span></span>
</div>
<div
class="results-filter-button ms-auto" (click)="isSearchFilterCollapsed = !isSearchFilterCollapsed" role="button"
<button
class="results-filter-button button-unstyle ms-auto" (click)="isSearchFilterCollapsed = !isSearchFilterCollapsed"
[attr.aria-expanded]="!isSearchFilterCollapsed" aria-controls="collapseBasic"
>
<span class="icon icon-filter"></span>
@ -19,7 +19,7 @@
Filters
<span *ngIf="numberOfFilters() > 0" class="pt-badge badge-secondary">{{ numberOfFilters() }}</span>
</ng-container>
</div>
</button>
</div>
<div class="results-filter" [ngbCollapse]="isSearchFilterCollapsed" [animation]="true">

View File

@ -4,7 +4,7 @@ import { ActivatedRouteSnapshot, Router } from '@angular/router'
import { logger } from '@root-helpers/logger'
import { ResultList } from '@shared/models'
export abstract class AbstractLazyLoadResolver <T> {
export abstract class AbstractLazyLoadResolver <T> {
protected router: Router
resolve (route: ActivatedRouteSnapshot) {

View File

@ -2,9 +2,10 @@
<header *ngIf="steps.length > 2">
<div class="header-steps">
<ng-container *ngFor="let step of steps; let i = index; let isLast = last;">
<div
class="step-info" [ngClass]="{ active: selectedIndex === i, completed: isCompleted(step), 'c-hand': isAccessible(step) }" [attr.aria-current]="selectedIndex === i"
(click)="onClick(i)"
<button
class="step-info button-unstyle" [ngClass]="{ active: selectedIndex === i, completed: isCompleted(step) }"
[disabled]="!isAccessible(step)"
[attr.aria-current]="selectedIndex === i" (click)="onClick(i)"
>
<div class="step-index">
<span class="visually-hidden" i18n>Step</span> {{ i + 1 }}
@ -15,7 +16,7 @@
</div>
<div class="step-label">{{ step.label }}</div>
</div>
</button>
<!-- Do no display if this is the last child -->
<div *ngIf="!isLast" class="connector"></div>

View File

@ -72,11 +72,6 @@ header {
align-items: center;
width: $index-block-height;
opacity: 0.5;
cursor: default;
&.c-hand {
cursor: pointer;
}
&.active,
&.completed {

View File

@ -92,9 +92,9 @@
<button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button>
<div class="skip-step">
<span class="underline-orange" role="button" (click)="skipChannelCreation()">
<button class="underline-orange button-unstyle" (click)="skipChannelCreation()">
<strong i18n>I don't want to create a channel</strong>
</span>
</button>
<div class="skip-step-description" i18n>You will be able to create a channel later</div>
</div>
@ -120,7 +120,7 @@
[requiresEmailVerification]="requiresEmailVerification" [requiresApproval]="requiresApproval" [instanceName]="instanceName"
></my-signup-success-before-email>
<div *ngIf="signupError" class="steps-button">
<div *ngIf="signupError" class="step-buttons">
<button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button>
</div>
</cdk-step>

View File

@ -44,7 +44,19 @@ my-instance-about-accordion {
}
}
button,
> button {
@include peertube-button-big;
&[cdkStepperNext] {
@include orange-button;
}
&[cdkStepperPrevious] {
@include grey-button;
}
}
> button,
.skip-step {
margin-top: 20px;
margin-bottom: 20px;
@ -60,18 +72,6 @@ my-instance-about-accordion {
}
}
button {
@include peertube-button-big;
&[cdkStepperNext] {
@include orange-button;
}
&[cdkStepperPrevious] {
@include grey-button;
}
}
.done-loader {
display: flex;
justify-content: center;

View File

@ -91,12 +91,12 @@
<div class="description-html" [innerHTML]="channelDescriptionHTML"></div>
</div>
<div *ngIf="hasShowMoreDescription()" class="show-more" role="button"
<button *ngIf="hasShowMoreDescription()" class="show-more button-unstyle"
(click)="channelDescriptionExpanded = !channelDescriptionExpanded"
title="Show the complete description" i18n-title i18n
>
Show more...
</div>
</button>
<div class="channel-buttons bottom">
<ng-template *ngTemplateOutlet="buttonsTemplate"></ng-template>

View File

@ -72,12 +72,12 @@
<div class="information">
<div>
<label i18n>Video before edition</label>
<div class="mb-1 fw-bold" i18n>Video before edition</div>
<my-embed [video]="video"></my-embed>
</div>
<div *ngIf="!noEdition()">
<label i18n>Edition tasks:</label>
<div class="mb-1 fw-bold" i18n>Edition tasks:</div>
<ol>
<li *ngFor="let task of getTasksSummary()">{{ task }}</li>

View File

@ -43,7 +43,7 @@ export class VideoStudioEditComponent extends FormReactive implements OnInit {
}
this.buildForm({
cut: {
'cut': {
start: null,
end: null
},

View File

@ -22,7 +22,7 @@
</div>
<div class="form-group">
<label i18n class="label-tags">Tags</label>
<label for="label-tags" i18n class="label-tags">Tags</label>
<my-help>
<ng-template ptTemplate="customHtml">
@ -170,10 +170,10 @@
<div class="captions">
<div class="captions-header">
<a (click)="openAddCaptionModal()" class="create-caption">
<button (click)="openAddCaptionModal()" class="peertube-create-button">
<my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
<ng-container i18n>Add another caption</ng-container>
</a>
</button>
</div>
<div class="form-group" *ngFor="let videoCaption of videoCaptions">
@ -187,8 +187,8 @@
<div i18n class="caption-entry-state">Already uploaded on {{ videoCaption.updatedAt | date }} &#10004;</div>
<span i18n class="caption-entry-edit" (click)="openEditCaptionModal(videoCaption)">Edit</span>
<span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Delete</span>
<button i18n class="caption-entry-edit" (click)="openEditCaptionModal(videoCaption)">Edit</button>
<button i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Delete</button>
</ng-container>
<ng-container *ngIf="videoCaption.action === 'CREATE'">
@ -196,7 +196,7 @@
<div i18n class="caption-entry-state caption-entry-state-create">Will be created on update</div>
<span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel create</span>
<button i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel create</button>
</ng-container>
<ng-container *ngIf="videoCaption.action === 'UPDATE'">
@ -204,7 +204,7 @@
<div i18n class="caption-entry-state caption-entry-state-create">Will be edited on update</div>
<span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel edition</span>
<button i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel edition</button>
</ng-container>
<ng-container *ngIf="videoCaption.action === 'REMOVE'">
@ -212,7 +212,7 @@
<div i18n class="caption-entry-state caption-entry-state-delete">Will be deleted on update</div>
<span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel deletion</span>
<button i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel deletion</button>
</ng-container>
</div>
</div>

View File

@ -29,10 +29,6 @@ my-peertube-checkbox {
margin-bottom: 1rem;
}
.create-caption {
@include create-button;
}
.caption-entry {
display: flex;
height: 40px;

View File

@ -64,7 +64,7 @@
(timestampClicked)="handleTimestampClicked($event)"
[redraftValue]="commentReplyRedraftValue"
>
<div *ngIf="comment.totalReplies !== 0 && !threadComments[comment.id]" (click)="viewReplies(comment.id)" class="view-replies mb-2">
<button *ngIf="comment.totalReplies !== 0 && !threadComments[comment.id]" (click)="viewReplies(comment.id)" class="view-replies mb-2 button-unstyle">
<span class="chevron-down"></span>
<ng-container *ngIf="comment.totalRepliesFromVideoAuthor > 0; then hasAuthorComments; else noAuthorComments"></ng-container>
@ -81,7 +81,7 @@
<ng-template i18n #noAuthorComments>View {comment.totalReplies, plural, =1 {1 reply} other {{{ comment.totalReplies }} replies}}</ng-template>
<my-loader size="sm" class="ms-1" [loading]="threadLoading[comment.id]"></my-loader>
</div>
</button>
</my-video-comment>
</div>

View File

@ -24,16 +24,11 @@
@include margin-left(5px);
display: inline-block;
opacity: 0;
transition: ease-in .2s opacity;
width: 12px;
position: relative;
top: -3px;
}
&:hover my-feed {
opacity: 1;
}
}
#dropdown-sort-comments {

View File

@ -6,10 +6,11 @@
the sharing system used for this video implies that some technical information about your system (such as a public IP address) can be sent to other peers.
</ng-container>
</span>
<a i18n i18n-title title="Get more information" target="_blank" rel="noopener noreferrer" href="/about/peertube#privacy">More information</a>
</div>
<div i18n class="privacy-concerns-button privacy-concerns-okay" (click)="acceptedPrivacyConcern()">
<button i18n class="ms-2 peertube-button orange-button" (click)="acceptedPrivacyConcern()">
OK
</div>
</button>
</div>

View File

@ -50,31 +50,10 @@ a {
transition: color 0.3s;
&:hover {
color: #fff;
color: pvar(--mainBackgroundColor);
}
}
.privacy-concerns-button {
@include margin-left(auto);
padding: 5px 8px 5px 7px;
border-radius: 3px;
white-space: nowrap;
cursor: pointer;
transition: background-color 0.3s;
font-weight: $font-semibold;
&:hover {
background-color: #000;
}
}
.privacy-concerns-okay {
@include margin-left(10px);
background-color: pvar(--mainColor);
}
@media screen and (max-width: 1300px) {
.privacy-concerns {
font-size: 12px;

View File

@ -6,14 +6,20 @@
myTimestampRouteTransformer
></div>
<div class="video-info-description-more" *ngIf="completeDescriptionShown === false && video.description?.length >= 250" (click)="showMoreDescription()">
<button
*ngIf="completeDescriptionShown === false && video.description?.length >= 250"
(click)="showMoreDescription()" class="video-info-description-more button-unstyle"
>
<ng-container i18n>Show more</ng-container>
<span *ngIf="descriptionLoading === false" class="chevron-down"></span>
<my-loader size="sm" class="description-loading" [loading]="descriptionLoading"></my-loader>
</div>
</button>
<div *ngIf="completeDescriptionShown === true" (click)="showLessDescription()" class="video-info-description-more">
<button
*ngIf="completeDescriptionShown === true"
(click)="showLessDescription()" class="video-info-description-more button-unstyle"
>
<ng-container i18n>Show less</ng-container>
<span *ngIf="descriptionLoading === false" class="chevron-up"></span>
</div>
</button>
</div>

View File

@ -13,6 +13,6 @@
</tbody>
</table>
</div>
<div class="cfp-hotkeys-close" (click)="toggleCheatSheet()">&#215;</div>
<button class="button-unstyle cfp-hotkeys-close" (click)="toggleCheatSheet()">&#215;</button>
</div>
</div>
</div>

View File

@ -14,7 +14,8 @@
<ul [hidden]="!search || !areSuggestionsOpened" role="listbox" class="p-0 m-0">
<li
*ngFor="let result of results; let i = index" class="suggestion d-flex flex-justify-start flex-items-center p-0 f5"
role="option" aria-selected="true" (mouseenter)="onSuggestionHover(i)" (click)="onSuggestionClicked(result)"
role="option" aria-selected="true" tabindex="0"
(mouseenter)="onSuggestionHover(i)" (click)="onSuggestionClicked(result)" (keyup.enter)="onSuggestionClicked(result)"
>
<my-suggestion [result]="result" [highlight]="search"></my-suggestion>
</li>
@ -23,7 +24,7 @@
<!-- suggestion help, not shown until one of the suggestions is selected and specific to that suggestion -->
<div *ngIf="showSearchGlobalHelp()" id="typeahead-help" class="overflow-hidden">
<div class="d-flex justify-content-between">
<label class="small-title" i18n>GLOBAL SEARCH</label>
<div class="small-title" i18n>GLOBAL SEARCH</div>
<div class="advanced-search-status muted">
<span *ngIf="serverConfig" class="me-1" i18n>using {{ serverConfig.search.searchIndex.url }}</span>
</div>
@ -35,7 +36,7 @@
<div *ngIf="areInstructionsDisplayed()" id="typeahead-instructions" class="overflow-hidden">
<span class="muted" i18n>Your query will be matched against video names or descriptions, channel names.</span>
<div class="d-flex justify-content-between mt-3">
<label class="small-title" i18n>ADVANCED SEARCH</label>
<div class="small-title" i18n>ADVANCED SEARCH</div>
<div class="advanced-search-status c-help">
<span [ngClass]="canSearchAnyURI ? 'text-success' : 'muted'" i18n-title title="Determines whether you can resolve any distant content, or if this instance only allows doing so for instances it follows.">
<span *ngIf="canSearchAnyURI()" class="me-1" i18n>any instance</span>

View File

@ -5,7 +5,7 @@
<div>
<div
class="logged-in-more" ngbDropdown #dropdown="ngbDropdown" placement="bottom-left auto"
container="body" (openChange)="onDropdownOpenChange($event)" autoClose="outside"
container="body" (openChange)="onDropdownOpenChange($event)"
>
<button class="border-0 text-start" ngbDropdownToggle>
<my-actor-avatar [actor]="user.account" actorType="account" size="34"></my-actor-avatar>
@ -33,21 +33,21 @@
<button
myPluginSelector pluginSelectorId="menu-user-dropdown-language-item"
ngbDropdownItem ngbDropdownToggle class="dropdown-item" (click)="openLanguageChooser()"
ngbDropdownItem class="dropdown-item" (click)="openLanguageChooser()"
>
<my-global-icon iconName="language" aria-hidden="true"></my-global-icon>
<span i18n>Interface:</span>
<span class="ms-auto muted">{{ currentInterfaceLanguage }}</span>
</button>
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings" fragment="video-languages-subtitles"
<a ngbDropdownItem class="dropdown-item" routerLink="/my-account/settings" fragment="video-languages-subtitles"
#settingsLanguagesSubtitles (click)="onActiveLinkScrollToAnchor(settingsLanguagesSubtitles)">
<my-global-icon iconName="video-lang" aria-hidden="true"></my-global-icon>
<span i18n>Videos:</span>
<span class="ms-auto muted">{{ videoLanguages.join(', ') }}</span>
</a>
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item settings-sensitive" routerLink="/my-account/settings"
<a ngbDropdownItem class="dropdown-item settings-sensitive" routerLink="/my-account/settings"
fragment="video-sensitive-content-policy" #settingsSensitiveContentPolicy
(click)="onActiveLinkScrollToAnchor(settingsSensitiveContentPolicy)"
>
@ -57,7 +57,7 @@
<span class="ms-auto muted">{{ nsfwPolicy }}</span>
</a>
<button ngbDropdownItem class="dropdown-item" (click)="toggleUseP2P()">
<button class="dropdown-item" (click)="toggleUseP2P()" (mousedown)="$event.stopPropagation()" ngbDropdownItem>
<my-global-icon iconName="p2p" aria-hidden="true"></my-global-icon>
<ng-container i18n>Help share videos</ng-container>
@ -66,15 +66,15 @@
<div class="dropdown-divider"></div>
<a *ngIf="!isInMobileView" ngbDropdownItem ngbDropdownToggle class="dropdown-item" (click)="openHotkeysCheatSheet()">
<button *ngIf="!isInMobileView" ngbDropdownItem class="dropdown-item" (click)="openHotkeysCheatSheet()">
<my-global-icon iconName="command" aria-hidden="true"></my-global-icon>
<ng-container i18n>Keyboard shortcuts</ng-container>
</a>
</button>
<a ngbDropdownItem ngbDropdownToggle (click)="logout($event)" class="dropdown-item" href="#">
<button ngbDropdownItem (click)="logout($event)" class="dropdown-item">
<my-global-icon iconName="sign-out" aria-hidden="true"></my-global-icon>
<ng-container i18n>Log out</ng-container>
</a>
</button>
</div>
</div>
@ -121,10 +121,10 @@
<div class="footer">
<div class="footer-block">
<a *ngIf="!isLoggedIn" class="menu-link" (click)="openQuickSettings()">
<button *ngIf="!isLoggedIn" class="menu-link button-unstyle" (click)="openQuickSettings()">
<my-global-icon iconName="cog" aria-hidden="true"></my-global-icon>
<ng-container i18n>My settings</ng-container>
</a>
</button>
<a class="menu-link" routerLink="/about" routerLinkActive="active">
<my-global-icon iconName="help" aria-hidden="true"></my-global-icon>
@ -135,7 +135,7 @@
<div class="footer-bottom">
<div class="footer-links">
<span *ngIf="isLoggedIn === false" role="button" (click)="openLanguageChooser()" class="c-hand" i18n>Interface: {{ currentInterfaceLanguage }}</span>
<button *ngIf="isLoggedIn === false" (click)="openLanguageChooser()" class="button-unstyle" i18n>Interface: {{ currentInterfaceLanguage }}</button>
<div>
<a i18n routerLink="/about/instance">Contact</a>
@ -143,7 +143,7 @@
<a i18n href="https://joinpeertube.org/faq" i18n-title title="Frequently asked questions about PeerTube" target="_blank" rel="noopener noreferrer">FAQ</a>
<a i18n routerLink="/about/instance" fragment="statistics">Stats</a>
<a i18n href="https://docs.joinpeertube.org/api/rest-reference.html" i18n-title title="API documentation" target="_blank" rel="noopener noreferrer">API</a>
<a role="button" (click)="openHotkeysCheatSheet()" class="c-hand" i18n>Keyboard shortcuts</a>
<button (click)="openHotkeysCheatSheet()" class="button-unstyle" i18n>Keyboard shortcuts</button>
</div>
</div>

View File

@ -20,6 +20,7 @@ $footer-links-base-opacity: .8;
word-break: break-word;
transition: background-color .1s ease-in-out;
line-height: $line-height-normal;
width: 100%;
&.active {
background-color: rgba(255, 255, 255, 0.15);
@ -113,16 +114,13 @@ my-notification {
.dropdown-toggle-indicator {
display: inherit !important;
}
&.dropdown-toggle {
max-width: 88% !important;
}
}
}
@include margin-left(13px);
flex: 1;
min-width: 1px;
border-radius: 25px;
transition: all .1s ease-in-out;
cursor: pointer;
@ -140,22 +138,24 @@ my-notification {
/* smartphones and touchscreens */
@media (hover: none) and (pointer: coarse) {
@include display-hints($is-mobile: true);
}
/* fill space when on mobile */
max-width: calc(100% - 80px);
> .dropdown-toggle {
display: flex;
align-items: center;
width: 100%;
padding: 5px 7px;
&.dropdown-toggle {
max-width: 100%;
}
.logged-in-info {
max-width: calc(100% - 45px) !important;
&::after {
// Disable bootstrap toggle
border: 0;
}
}
.dropdown-toggle-indicator {
position: relative;
display: none;
width: 17px;
span {
position: absolute;
@ -163,17 +163,6 @@ my-notification {
color: #808080;
}
}
.dropdown-toggle::after {
border: 0;
}
> .dropdown-toggle:first-child {
display: flex;
align-items: center;
width: 100%;
padding: 5px 25px 5px 7px;
}
}
my-actor-avatar {
@ -181,9 +170,8 @@ my-actor-avatar {
}
.logged-in-info {
max-width: 105px;
flex-grow: 1;
flex-shrink: 1;
min-width: 1px;
}
.logged-in-display-name,
@ -286,7 +274,8 @@ my-actor-avatar {
margin-bottom: 25px;
}
a {
a,
button {
min-height: 40px;
}
}
@ -306,7 +295,7 @@ my-actor-avatar {
}
a,
span[role=button] {
button {
@include margin-right(8px);
@include disable-default-a-behaviour;

View File

@ -99,7 +99,7 @@ export const VIDEO_ORIGINALLY_PUBLISHED_AT_VALIDATOR: BuildFormValidator = {
function arrayTagLengthValidator (min = 2, max = 30): ValidatorFn {
return (control: AbstractControl): ValidationErrors => {
const array = control.value as Array<string>
const array = control.value as string[]
if (array.every(e => e.length >= min && e.length <= max)) {
return null

View File

@ -120,12 +120,12 @@
<my-global-icon *ngIf="isAbuseRejected(abuse)" [title]="abuse.state.label" iconName="cross"></my-global-icon>
</td>
<td class="c-hand abuse-messages" (click)="openAbuseMessagesModal(abuse)">
<ng-container *ngIf="isLocalAbuse(abuse)">
<td class="abuse-messages">
<button class="button-unstyle" *ngIf="isLocalAbuse(abuse)" (click)="openAbuseMessagesModal(abuse)">
{{ abuse.countMessages }}
<my-global-icon iconName="message-circle"></my-global-icon>
</ng-container>
</button>
</td>
<td *ngIf="isAdminView()" class="internal-note" container="body" placement="left auto" [ngbTooltip]="abuse.moderationComment">

View File

@ -2,29 +2,29 @@
<div class="position-relative me-3">
<my-actor-avatar [actor]="actor" [actorType]="getActorType()" [previewImage]="preview" size="100"></my-actor-avatar>
<div *ngIf="editable && !hasAvatar()" class="actor-img-edit-button" [ngbTooltip]="avatarFormat" placement="right" container="body">
<div *ngIf="editable && !hasAvatar()" class="actor-img-edit-button button-focus-within" [ngbTooltip]="avatarFormat" placement="right" container="body">
<my-global-icon iconName="upload"></my-global-icon>
<label class="visually-hidden" for="avatarfile" i18n>Upload a new avatar</label>
<input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
</div>
<div *ngIf="editable && hasAvatar()" ngbDropdown placement="right">
<div class="actor-img-edit-button" ngbDropdownToggle>
<button type="button" class="actor-img-edit-button" ngbDropdownToggle>
<my-global-icon iconName="edit"></my-global-icon>
<label class="visually-hidden" for="avatarMenu" i18n>Change your avatar</label>
</div>
</button>
<div ngbDropdownMenu>
<div class="dropdown-item c-hand dropdown-file" [ngbTooltip]="avatarFormat">
<div class="dropdown-item dropdown-file button-focus-within" [ngbTooltip]="avatarFormat">
<my-global-icon iconName="upload"></my-global-icon>
<span for="avatarfile" i18n>Upload a new avatar</span>
<input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
</div>
<div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()">
<button type="button" class="dropdown-item" (click)="deleteAvatar()" (key.enter)="deleteAvatar()">
<my-global-icon iconName="delete"></my-global-icon>
<span i18n>Remove avatar</span>
</div>
</button>
</div>
</div>

View File

@ -39,6 +39,10 @@
right: 5px;
}
.button-focus-within:focus-within {
box-shadow: #{$focus-box-shadow-form} pvar(--mainColorLightest);
}
.dropdown-item {
@include dropdown-with-icon-item;
}

View File

@ -4,25 +4,25 @@
<img *ngIf="hasBanner()" [src]="preview || actor.bannerUrl" alt="Banner" />
</div>
<div *ngIf="!hasBanner()" class="actor-img-edit-button" [ngbTooltip]="bannerFormat" placement="right" container="body">
<div *ngIf="!hasBanner()" class="actor-img-edit-button button-focus-within" [ngbTooltip]="bannerFormat" placement="right" container="body">
<ng-container *ngTemplateOutlet="uploadNewBanner"></ng-container>
</div>
<div *ngIf="hasBanner()" ngbDropdown placement="right">
<div class="actor-img-edit-button" ngbDropdownToggle>
<button type="button" class="actor-img-edit-button" ngbDropdownToggle>
<my-global-icon iconName="edit"></my-global-icon>
<label for="bannerMenu" i18n>Change your banner</label>
</div>
</button>
<div ngbDropdownMenu>
<div class="dropdown-item c-hand dropdown-file" [ngbTooltip]="bannerFormat">
<div class="dropdown-item dropdown-file button-focus-within" [ngbTooltip]="bannerFormat">
<ng-container *ngTemplateOutlet="uploadNewBanner"></ng-container>
</div>
<div class="dropdown-item c-hand" (click)="deleteBanner()" (key.enter)="deleteBanner()">
<button type="button" class="dropdown-item" (click)="deleteBanner()">
<my-global-icon iconName="delete"></my-global-icon>
<span i18n>Remove banner</span>
</div>
</button>
</div>
</div>
</div>

View File

@ -34,6 +34,10 @@
}
}
.button-focus-within:focus-within {
box-shadow: #{$focus-box-shadow-form} pvar(--mainColorLightest);
}
.dropdown-item {
@include dropdown-with-icon-item;
}

View File

@ -145,13 +145,13 @@ export class ActorAvatarComponent implements OnInit, OnChanges {
// Keep consistency with CSS
const themes = {
'0123456789abc': 'blue',
def: 'green',
ghi: 'purple',
jkl: 'gray',
mno: 'yellow',
pqr: 'orange',
stvu: 'red',
wxyz: 'dark-blue'
'def': 'green',
'ghi': 'purple',
'jkl': 'gray',
'mno': 'yellow',
'pqr': 'orange',
'stvu': 'red',
'wxyz': 'dark-blue'
}
const theme = Object.keys(themes)

View File

@ -1,4 +1,2 @@
<div (click)="update()">
<input type="checkbox" [checked]="checked"/>
<label class="ms-auto">Toggle</label>
</div>
<input type="checkbox" [checked]="checked" [name]="inputName" [id]="inputName" (change)="update()" />
<label [for]="inputName" class="ms-auto">Toggle</label>

View File

@ -5,7 +5,7 @@
icon="edit" (fileChanged)="onFileChanged($event)" [buttonTooltip]="getReactiveFileButtonTooltip()"
></my-reactive-file>
<img *ngIf="imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" [src]="imageSrc" class="preview" />
<img *ngIf="imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" [src]="imageSrc" class="preview" alt="Preview" i18n-alt />
<div *ngIf="!imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" class="preview no-image"></div>
</div>
</div>

View File

@ -3,81 +3,81 @@ import { HooksService } from '@app/core/plugins/hooks.service'
const icons = {
// misc icons
npm: require('!!raw-loader?!../../../assets/images/misc/npm.svg').default,
markdown: require('!!raw-loader?!../../../assets/images/misc/markdown.svg').default,
language: require('!!raw-loader?!../../../assets/images/misc/language.svg').default,
'npm': require('!!raw-loader?!../../../assets/images/misc/npm.svg').default,
'markdown': require('!!raw-loader?!../../../assets/images/misc/markdown.svg').default,
'language': require('!!raw-loader?!../../../assets/images/misc/language.svg').default,
'video-lang': require('!!raw-loader?!../../../assets/images/misc/video-lang.svg').default,
support: require('!!raw-loader?!../../../assets/images/misc/support.svg').default,
'support': require('!!raw-loader?!../../../assets/images/misc/support.svg').default,
'peertube-x': require('!!raw-loader?!../../../assets/images/misc/peertube-x.svg').default,
robot: require('!!raw-loader?!../../../assets/images/misc/miscellaneous-services.svg').default, // material ui
videos: require('!!raw-loader?!../../../assets/images/misc/video-library.svg').default, // material ui
history: require('!!raw-loader?!../../../assets/images/misc/history.svg').default, // material ui
subscriptions: require('!!raw-loader?!../../../assets/images/misc/subscriptions.svg').default, // material ui
'robot': require('!!raw-loader?!../../../assets/images/misc/miscellaneous-services.svg').default, // material ui
'videos': require('!!raw-loader?!../../../assets/images/misc/video-library.svg').default, // material ui
'history': require('!!raw-loader?!../../../assets/images/misc/history.svg').default, // material ui
'subscriptions': require('!!raw-loader?!../../../assets/images/misc/subscriptions.svg').default, // material ui
'playlist-add': require('!!raw-loader?!../../../assets/images/misc/playlist-add.svg').default, // material ui
follower: require('!!raw-loader?!../../../assets/images/misc/account-arrow-left.svg').default, // material ui
following: require('!!raw-loader?!../../../assets/images/misc/account-arrow-right.svg').default, // material ui
tip: require('!!raw-loader?!../../../assets/images/misc/tip.svg').default, // material ui
flame: require('!!raw-loader?!../../../assets/images/misc/flame.svg').default,
local: require('!!raw-loader?!../../../assets/images/misc/local.svg').default,
'follower': require('!!raw-loader?!../../../assets/images/misc/account-arrow-left.svg').default, // material ui
'following': require('!!raw-loader?!../../../assets/images/misc/account-arrow-right.svg').default, // material ui
'tip': require('!!raw-loader?!../../../assets/images/misc/tip.svg').default, // material ui
'flame': require('!!raw-loader?!../../../assets/images/misc/flame.svg').default,
'local': require('!!raw-loader?!../../../assets/images/misc/local.svg').default,
// feather icons
copy: require('!!raw-loader?!../../../assets/images/feather/copy.svg').default,
flag: require('!!raw-loader?!../../../assets/images/feather/flag.svg').default,
playlists: require('!!raw-loader?!../../../assets/images/feather/list.svg').default,
syndication: require('!!raw-loader?!../../../assets/images/feather/syndication.svg').default,
help: require('!!raw-loader?!../../../assets/images/feather/help.svg').default,
alert: require('!!raw-loader?!../../../assets/images/feather/alert.svg').default,
globe: require('!!raw-loader?!../../../assets/images/feather/globe.svg').default,
home: require('!!raw-loader?!../../../assets/images/feather/home.svg').default,
trending: require('!!raw-loader?!../../../assets/images/feather/trending.svg').default,
search: require('!!raw-loader?!../../../assets/images/feather/search.svg').default,
upload: require('!!raw-loader?!../../../assets/images/feather/upload.svg').default,
dislike: require('!!raw-loader?!../../../assets/images/feather/dislike.svg').default,
like: require('!!raw-loader?!../../../assets/images/feather/like.svg').default,
no: require('!!raw-loader?!../../../assets/images/feather/no.svg').default,
'copy': require('!!raw-loader?!../../../assets/images/feather/copy.svg').default,
'flag': require('!!raw-loader?!../../../assets/images/feather/flag.svg').default,
'playlists': require('!!raw-loader?!../../../assets/images/feather/list.svg').default,
'syndication': require('!!raw-loader?!../../../assets/images/feather/syndication.svg').default,
'help': require('!!raw-loader?!../../../assets/images/feather/help.svg').default,
'alert': require('!!raw-loader?!../../../assets/images/feather/alert.svg').default,
'globe': require('!!raw-loader?!../../../assets/images/feather/globe.svg').default,
'home': require('!!raw-loader?!../../../assets/images/feather/home.svg').default,
'trending': require('!!raw-loader?!../../../assets/images/feather/trending.svg').default,
'search': require('!!raw-loader?!../../../assets/images/feather/search.svg').default,
'upload': require('!!raw-loader?!../../../assets/images/feather/upload.svg').default,
'dislike': require('!!raw-loader?!../../../assets/images/feather/dislike.svg').default,
'like': require('!!raw-loader?!../../../assets/images/feather/like.svg').default,
'no': require('!!raw-loader?!../../../assets/images/feather/no.svg').default,
'cloud-download': require('!!raw-loader?!../../../assets/images/feather/cloud-download.svg').default,
clock: require('!!raw-loader?!../../../assets/images/feather/clock.svg').default,
cog: require('!!raw-loader?!../../../assets/images/feather/cog.svg').default,
delete: require('!!raw-loader?!../../../assets/images/feather/delete.svg').default,
bell: require('!!raw-loader?!../../../assets/images/feather/bell.svg').default,
'clock': require('!!raw-loader?!../../../assets/images/feather/clock.svg').default,
'cog': require('!!raw-loader?!../../../assets/images/feather/cog.svg').default,
'delete': require('!!raw-loader?!../../../assets/images/feather/delete.svg').default,
'bell': require('!!raw-loader?!../../../assets/images/feather/bell.svg').default,
'sign-out': require('!!raw-loader?!../../../assets/images/feather/log-out.svg').default,
'sign-in': require('!!raw-loader?!../../../assets/images/feather/log-in.svg').default,
download: require('!!raw-loader?!../../../assets/images/feather/download.svg').default,
'download': require('!!raw-loader?!../../../assets/images/feather/download.svg').default,
'ownership-change': require('!!raw-loader?!../../../assets/images/feather/share.svg').default,
share: require('!!raw-loader?!../../../assets/images/feather/share-2.svg').default,
channel: require('!!raw-loader?!../../../assets/images/feather/tv.svg').default,
user: require('!!raw-loader?!../../../assets/images/feather/user.svg').default,
'share': require('!!raw-loader?!../../../assets/images/feather/share-2.svg').default,
'channel': require('!!raw-loader?!../../../assets/images/feather/tv.svg').default,
'user': require('!!raw-loader?!../../../assets/images/feather/user.svg').default,
'user-x': require('!!raw-loader?!../../../assets/images/feather/user-x.svg').default,
users: require('!!raw-loader?!../../../assets/images/feather/users.svg').default,
'users': require('!!raw-loader?!../../../assets/images/feather/users.svg').default,
'user-add': require('!!raw-loader?!../../../assets/images/feather/user-plus.svg').default,
add: require('!!raw-loader?!../../../assets/images/feather/plus-circle.svg').default,
'add': require('!!raw-loader?!../../../assets/images/feather/plus-circle.svg').default,
'cloud-error': require('!!raw-loader?!../../../assets/images/feather/cloud-off.svg').default,
undo: require('!!raw-loader?!../../../assets/images/feather/corner-up-left.svg').default,
'undo': require('!!raw-loader?!../../../assets/images/feather/corner-up-left.svg').default,
'circle-tick': require('!!raw-loader?!../../../assets/images/feather/check-circle.svg').default,
'more-horizontal': require('!!raw-loader?!../../../assets/images/feather/more-horizontal.svg').default,
'more-vertical': require('!!raw-loader?!../../../assets/images/feather/more-vertical.svg').default,
play: require('!!raw-loader?!../../../assets/images/feather/play.svg').default,
p2p: require('!!raw-loader?!../../../assets/images/feather/airplay.svg').default,
fullscreen: require('!!raw-loader?!../../../assets/images/feather/maximize.svg').default,
'play': require('!!raw-loader?!../../../assets/images/feather/play.svg').default,
'p2p': require('!!raw-loader?!../../../assets/images/feather/airplay.svg').default,
'fullscreen': require('!!raw-loader?!../../../assets/images/feather/maximize.svg').default,
'exit-fullscreen': require('!!raw-loader?!../../../assets/images/feather/minimize.svg').default,
film: require('!!raw-loader?!../../../assets/images/feather/film.svg').default,
edit: require('!!raw-loader?!../../../assets/images/feather/edit-2.svg').default,
'film': require('!!raw-loader?!../../../assets/images/feather/film.svg').default,
'edit': require('!!raw-loader?!../../../assets/images/feather/edit-2.svg').default,
'external-link': require('!!raw-loader?!../../../assets/images/feather/external-link.svg').default,
'eye-open': require('!!raw-loader?!../../../assets/images/feather/eye.svg').default,
'eye-close': require('!!raw-loader?!../../../assets/images/feather/eye-off.svg').default,
refresh: require('!!raw-loader?!../../../assets/images/feather/refresh-cw.svg').default,
command: require('!!raw-loader?!../../../assets/images/feather/command.svg').default,
go: require('!!raw-loader?!../../../assets/images/feather/arrow-up-right.svg').default,
cross: require('!!raw-loader?!../../../assets/images/feather/x.svg').default,
tick: require('!!raw-loader?!../../../assets/images/feather/check.svg').default,
columns: require('!!raw-loader?!../../../assets/images/feather/columns.svg').default,
live: require('!!raw-loader?!../../../assets/images/feather/live.svg').default,
repeat: require('!!raw-loader?!../../../assets/images/feather/repeat.svg').default,
'refresh': require('!!raw-loader?!../../../assets/images/feather/refresh-cw.svg').default,
'command': require('!!raw-loader?!../../../assets/images/feather/command.svg').default,
'go': require('!!raw-loader?!../../../assets/images/feather/arrow-up-right.svg').default,
'cross': require('!!raw-loader?!../../../assets/images/feather/x.svg').default,
'tick': require('!!raw-loader?!../../../assets/images/feather/check.svg').default,
'columns': require('!!raw-loader?!../../../assets/images/feather/columns.svg').default,
'live': require('!!raw-loader?!../../../assets/images/feather/live.svg').default,
'repeat': require('!!raw-loader?!../../../assets/images/feather/repeat.svg').default,
'chevrons-up': require('!!raw-loader?!../../../assets/images/feather/chevrons-up.svg').default,
'message-circle': require('!!raw-loader?!../../../assets/images/feather/message-circle.svg').default,
codesandbox: require('!!raw-loader?!../../../assets/images/feather/codesandbox.svg').default,
award: require('!!raw-loader?!../../../assets/images/feather/award.svg').default,
stats: require('!!raw-loader?!../../../assets/images/feather/stats.svg').default
'codesandbox': require('!!raw-loader?!../../../assets/images/feather/codesandbox.svg').default,
'award': require('!!raw-loader?!../../../assets/images/feather/award.svg').default,
'stats': require('!!raw-loader?!../../../assets/images/feather/stats.svg').default
}
export type GlobalIconName = keyof typeof icons

View File

@ -11,7 +11,7 @@
<tr>
<th i18n class="label" scope="row">
<div>Default NSFW/sensitive videos policy</div>
<div class="c-hand more-info" (click)="openQuickSettingsHighlight()">can be redefined by the users</div>
<span class="fs-7 fw-normal fst-italic">can be redefined by the users</span>
</th>
<td class="value">{{ buildNSFWLabel() }}</td>

View File

@ -2,7 +2,6 @@ import { Component, OnInit } from '@angular/core'
import { ServerService } from '@app/core'
import { prepareIcu } from '@app/helpers'
import { ServerConfig } from '@shared/models'
import { PeertubeModalService } from '../shared-main/peertube-modal/peertube-modal.service'
@Component({
selector: 'my-instance-features-table',
@ -14,8 +13,7 @@ export class InstanceFeaturesTableComponent implements OnInit {
serverConfig: ServerConfig
constructor (
private serverService: ServerService,
private modalService: PeertubeModalService
private serverService: ServerService
) { }
get initialUserVideoQuota () {
@ -69,10 +67,6 @@ export class InstanceFeaturesTableComponent implements OnInit {
return this.serverService.getServerVersionAndCommit()
}
openQuickSettingsHighlight () {
this.modalService.openQuickSettingsSubject.next()
}
private getApproximateTime (seconds: number) {
const hours = Math.floor(seconds / 3600)

View File

@ -17,7 +17,7 @@ export class NumberFormatterPipe implements PipeTransform {
return +f
}
private dictionary: Array<{ max: number, type: string }> = [
private dictionary: { max: number, type: string }[] = [
{ max: 1000, type: '' },
{ max: 1000000, type: 'K' },
{ max: 1000000000, type: 'M' }

View File

@ -40,7 +40,7 @@
<h6
*ngIf="!action.linkBuilder && action.isHeader && areActionsDisplayed(actions, entry)" [ngClass]="{ 'with-icon': !!action.iconName }"
class="dropdown-header" [title]="action.title || ''" (click)="action.handler(entry)" (keyup.enter)="action.handler(entry)"
class="dropdown-header" [title]="action.title || ''"
>
<ng-container *ngTemplateOutlet="templateActionLabel; context:{ $implicit: action }"></ng-container>
</h6>

View File

@ -55,7 +55,7 @@ export class ActionDropdownComponent<T> {
return {}
}
areActionsDisplayed (actions: Array<DropdownAction<T> | DropdownAction<T>[]>, entry: T): boolean {
areActionsDisplayed (actions: (DropdownAction<T> | DropdownAction<T>[])[], entry: T): boolean {
return actions.some(a => {
if (Array.isArray(a)) return this.areActionsDisplayed(a, entry)

View File

@ -31,7 +31,7 @@ export class ButtonComponent implements OnInit, OnChanges {
private buildClasses () {
this.classes = {
[this.className]: true,
disabled: this.disabled,
'disabled': this.disabled,
'icon-only': !this.label,
'has-icon': !!this.icon,
'responsive-label': this.responsiveLabel

View File

@ -1,7 +1,10 @@
<span
class="date-toggle"
[title]="getTitle()"
role="button"
tabindex="0"
(click)="toggle()"
(keyup.enter)="toggle()"
>
{{ getContent() }}
</span>

View File

@ -10,8 +10,8 @@ export class LoaderComponent {
private readonly sizes = {
sm: {
width: '1rem',
height: '1rem',
'width': '1rem',
'height': '1rem',
'border-width': '0.15rem'
},
xl: {

View File

@ -1,6 +1,7 @@
<div *ngIf="componentPagination.totalItems === 0" class="no-notification" i18n>You don't have notifications.</div>
<div class="notifications" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->
<div *ngFor="let notification of notifications" class="notification" [ngClass]="{ unread: !notification.read }" (click)="markAsRead(notification)">
<ng-container [ngSwitch]="notification.type">

View File

@ -1,6 +1,7 @@
<div class="user-quota mb-3">
<div>
<label class="user-quota-title" tabindex="0" i18n>Total video quota</label>
<div class="mb-2 fw-bold" i18n>Total video quota</div>
<div class="progress" tabindex="0" [ngbTooltip]="titleVideoQuota()">
<div class="progress-bar" tabindex="0" role="progressbar" [style]="{ width: userVideoQuotaPercentage + '%' }"
[attr.aria-valuenow]="userVideoQuotaUsed" aria-valuemin="0" [attr.aria-valuemax]="user.videoQuota"></div>
@ -10,7 +11,8 @@
</div>
<div *ngIf="hasDailyQuota()" class="mt-3">
<label class="user-quota-title" tabindex="0" i18n>Daily video quota</label>
<div class="mb-2 fw-bold" i18n>Daily video quota</div>
<div class="progress" tabindex="0" [ngbTooltip]="titleVideoQuotaDaily()">
<div class="progress-bar" role="progressbar" [style]="{ width: userVideoQuotaDailyPercentage + '%' }"
[attr.aria-valuenow]="userVideoQuotaUsedDaily" aria-valuemin="0" [attr.aria-valuemax]="user.videoQuotaDaily"></div>

View File

@ -14,10 +14,10 @@
<ng-template pTemplate="caption">
<div class="caption">
<div class="left-buttons">
<a class="block-button" (click)="addServersToBlock()" (key.enter)="addServersToBlock()">
<button class="peertube-create-button" (click)="addServersToBlock()" (key.enter)="addServersToBlock()">
<my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
<ng-container i18n>Mute domain</ng-container>
</a>
</button>
</div>
<div class="ms-auto">

View File

@ -20,7 +20,3 @@ a {
@include peertube-button;
@include grey-button;
}
.block-button {
@include create-button;
}

View File

@ -1,7 +1,7 @@
<a [href]="getVideoUrl()" class="table-video-link" [title]="video.name" target="_blank" rel="noopener noreferrer">
<div class="table-video">
<div class="table-video-image">
<img [src]="video.thumbnailPath">
<img [src]="video.thumbnailPath" alt="">
<ng-content select="[image]"></ng-content>
</div>

View File

@ -11,15 +11,15 @@
<div *ngIf="displayWatchLaterPlaylist" class="video-thumbnail-actions-overlay">
<ng-container *ngIf="inWatchLaterPlaylist !== true">
<div class="video-thumbnail-watch-later-overlay" placement="left" [ngbTooltip]="addToWatchLaterText" container="body" (click)="onWatchLaterClick($event)">
<button class="video-thumbnail-watch-later-overlay button-unstyle" placement="left" [ngbTooltip]="addToWatchLaterText" container="body" (click)="onWatchLaterClick($event)">
<my-global-icon iconName="clock" [attr.aria-label]="addToWatchLaterText" role="button"></my-global-icon>
</div>
</button>
</ng-container>
<ng-container *ngIf="inWatchLaterPlaylist === true">
<div class="video-thumbnail-watch-later-overlay" placement="left" [ngbTooltip]="addedToWatchLaterText" container="body" (click)="onWatchLaterClick($event)">
<button class="video-thumbnail-watch-later-overlay button-unstyle" placement="left" [ngbTooltip]="addedToWatchLaterText" container="body" (click)="onWatchLaterClick($event)">
<my-global-icon iconName="tick" [attr.aria-label]="addedToWatchLaterText" role="button"></my-global-icon>
</div>
</button>
</ng-container>
</div>

View File

@ -33,7 +33,7 @@
</div>
<div class="journal" *ngIf="latestLiveSessions.length !== 0">
<label i18n>Latest live sessions</label>
<div class="mb-2 fw-bold" i18n>Latest live sessions</div>
<div class="journal-session" *ngFor="let session of latestLiveSessions">
<span i18n class="pt-badge badge-success" *ngIf="!getErrorLabel(session)">Success</span>

View File

@ -111,9 +111,9 @@
</div>
</div>
<div
<button
(click)="isAdvancedCustomizationCollapsed = !isAdvancedCustomizationCollapsed"
role="button" class="advanced-filters-button"
class="advanced-filters-button button-unstyle"
[attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic"
>
<ng-container *ngIf="isAdvancedCustomizationCollapsed">
@ -131,7 +131,7 @@
Simple
</ng-container>
</ng-container>
</div>
</button>
</ng-container>
</div>

View File

@ -2,7 +2,7 @@
<div class="label-description muted" i18n>
Update
<a routerLink="/my-account/settings" [fragment]="fragment">
<span (click)="onAccountSettingsClick($event)">your settings</span>
<button class="button-unstyle" (click)="onAccountSettingsClick($event)">your settings</button>
</a
></div>
</ng-template>
@ -23,9 +23,9 @@
<my-global-icon iconName="chevrons-up"></my-global-icon>
</button>
<div
<button
*ngFor="let activeFilter of filters.getActiveFilters()" (click)="resetFilter(activeFilter.key, activeFilter.canRemove)"
class="active-filter pastille" [ngClass]="{ 'can-remove': activeFilter.canRemove }" [title]="getFilterTitle(activeFilter.canRemove)"
class="active-filter pastille button-unstyle" [ngClass]="{ 'can-remove': activeFilter.canRemove }" [title]="getFilterTitle(activeFilter.canRemove)"
>
<span>
{{ activeFilter.label }}
@ -34,7 +34,7 @@
</span>
<my-global-icon *ngIf="activeFilter.canRemove" iconName="cross"></my-global-icon>
</div>
</button>
</div>
<ng-select

View File

@ -37,7 +37,7 @@ export class VideoFilters {
private activeFilters: { key: string, canRemove: boolean, label: string, value?: string }[] = []
private defaultNSFWPolicy: NSFWPolicyType
private onChangeCallbacks: Array<() => void> = []
private onChangeCallbacks: (() => void)[] = []
private oldFormObjectString: string
private readonly hiddenFields: string[] = []

View File

@ -16,9 +16,9 @@
<ng-container *ngTemplateOutlet="actionContent; context:{ $implicit: action }"></ng-container>
</a>
<a *ngIf="!action.routerLink && !action.href && action.click" class="ms-2" (click)="action.click($event)" (key.enter)="action.click($event)">
<button *ngIf="!action.routerLink && !action.href && action.click" class="ms-2 button-unstyle" (click)="action.click($event)" (key.enter)="action.click($event)">
<ng-container *ngTemplateOutlet="actionContent; context:{ $implicit: action }"></ng-container>
</a>
</button>
<a *ngIf="!action.routerLink && action.href && action.click" class="ms-2" (click)="action.click($event)" (key.enter)="action.click($event)" [href]="action.href">
<ng-container *ngTemplateOutlet="actionContent; context:{ $implicit: action }"></ng-container>

View File

@ -20,9 +20,9 @@
<!-- Display only once -->
<div class="action-selection-mode" *ngIf="isInSelectionMode() === true && i === 0">
<div class="action-selection-mode-child">
<span i18n class="action-button action-button-cancel-selection" (click)="abortSelectionMode()">
<button i18n class="action-button action-button-cancel-selection" (click)="abortSelectionMode()">
Cancel
</span>
</button>
<ng-container *ngTemplateOutlet="globalButtonsTemplate"></ng-container>
</div>

View File

@ -14,20 +14,20 @@
*ngFor="let playlist of videoPlaylists"
class="playlist dropdown-item" [ngClass]="{ 'has-optional-row': playlist.optionalRowDisplayed }"
>
<div class="primary-row" (click)="toggleMainPlaylist($event, playlist)">
<button class="primary-row button-unstyle" (click)="toggleMainPlaylist($event, playlist)">
<my-peertube-checkbox
[disabled]="isPresentMultipleTimes(playlist) || playlist.optionalRowDisplayed" [inputName]="getPrimaryInputName(playlist)"
[ngModel]="isPrimaryCheckboxChecked(playlist)" [onPushWorkaround]="true"
></my-peertube-checkbox>
<label class="display-name">
<label [for]="getPrimaryInputName(playlist)" class="display-name">
{{ playlist.displayName }}
</label>
<div class="optional-row-icon" *ngIf="isPrimaryCheckboxChecked(playlist)" (click)="$event.stopPropagation(); toggleOptionalRow(playlist)">
<button class="optional-row-icon button-unstyle" *ngIf="isPrimaryCheckboxChecked(playlist)" (click)="$event.stopPropagation(); toggleOptionalRow(playlist)">
<my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
</div>
</div>
</button>
</button>
<div class="optional-rows" *ngIf="playlist.optionalRowDisplayed">
<div class="header-label" i18n>Start at</div>
@ -58,11 +58,11 @@
</div>
</div>
<div class="new-playlist-button dropdown-item" (click)="openCreateBlock($event)" [hidden]="isNewPlaylistBlockOpened">
<button class="new-playlist-button dropdown-item" (click)="openCreateBlock($event)" [hidden]="isNewPlaylistBlockOpened">
<my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
<span i18n>Create a private playlist</span>
</div>
</button>
<form class="new-playlist-block dropdown-item" *ngIf="isNewPlaylistBlockOpened" (ngSubmit)="createPlaylist()" [formGroup]="form">
<div class="form-group">

View File

@ -67,6 +67,8 @@
}
.optional-row-icon {
@include margin-left(5px);
display: flex;
align-items: center;
font-size: 14px;
@ -79,6 +81,10 @@
width: 19px;
height: 19px;
}
&:hover {
opacity: 0.8;
}
}
}

View File

@ -59,10 +59,10 @@
<div ngbDropdownMenu>
<ng-container *ngIf="playlistElement.video">
<div class="dropdown-item" (click)="toggleDisplayTimestampsOptions($event, playlistElement)">
<button ngbDropdownItem (click)="toggleDisplayTimestampsOptions($event, playlistElement)">
<my-global-icon iconName="edit" aria-hidden="true"></my-global-icon>
<ng-container i18n>Edit starts/stops at</ng-container>
</div>
</button>
<div class="timestamp-options" *ngIf="displayTimestampOptions">
<div>
@ -97,10 +97,10 @@
</div>
</ng-container>
<span class="dropdown-item" (click)="removeFromPlaylist(playlistElement)">
<button ngbDropdownItem (click)="removeFromPlaylist(playlistElement)">
<my-global-icon iconName="delete" aria-hidden="true"></my-global-icon>
<ng-container i18n>Delete from {{ playlist?.displayName }}</ng-container>
</span>
</button>
</div>
</div>
</div>

View File

@ -156,6 +156,7 @@ my-video-thumbnail,
@include padding-left(35px);
padding-top: 0;
margin-top: 5px;
margin-bottom: 15px;
> div {

View File

@ -23,7 +23,7 @@ class SettingsDialog extends Component {
innerHTML: '',
tabIndex: -1
}, {
role: 'dialog',
'role': 'dialog',
'aria-labelledby': dialogLabelId,
'aria-describedby': dialogDescriptionId
})

View File

@ -32,7 +32,7 @@ function isSafari () {
// https://github.com/danrevah/ngx-pipes/blob/master/src/pipes/math/bytes.ts
// Don't import all Angular stuff, just copy the code with shame
const dictionaryBytes: Array<{ max: number, type: string }> = [
const dictionaryBytes: { max: number, type: string }[] = [
{ max: 1024, type: 'B' },
{ max: 1048576, type: 'KB' },
{ max: 1073741824, type: 'MB' },

View File

@ -1,4 +1,4 @@
const dictionary: Array<{ max: number, type: string }> = [
const dictionary: { max: number, type: string }[] = [
{ max: 1024, type: 'B' },
{ max: 1048576, type: 'KB' },
{ max: 1073741824, type: 'MB' },

View File

@ -59,7 +59,7 @@ class Logger {
if (!payload) return
const headers = new Headers({
Accept: 'application/json',
'Accept': 'application/json',
'Content-Type': 'application/json'
})

View File

@ -60,14 +60,14 @@ class PluginsManager {
private loadingScopes: { [id in PluginClientScope]?: boolean } = {}
private pluginsLoaded: { [ scope in PluginClientScope ]: ReplaySubject<boolean> } = {
common: new ReplaySubject<boolean>(1),
'common': new ReplaySubject<boolean>(1),
'admin-plugin': new ReplaySubject<boolean>(1),
search: new ReplaySubject<boolean>(1),
'search': new ReplaySubject<boolean>(1),
'video-watch': new ReplaySubject<boolean>(1),
signup: new ReplaySubject<boolean>(1),
login: new ReplaySubject<boolean>(1),
'signup': new ReplaySubject<boolean>(1),
'login': new ReplaySubject<boolean>(1),
'video-edit': new ReplaySubject<boolean>(1),
embed: new ReplaySubject<boolean>(1),
'embed': new ReplaySubject<boolean>(1),
'my-library': new ReplaySubject<boolean>(1),
'video-channel': new ReplaySubject<boolean>(1)
}

View File

@ -39,3 +39,14 @@
.peertube-button-icon {
@include button-with-icon(18px, 3px, -1px);
}
.peertube-create-button {
@include peertube-button-link;
@include orange-button;
@include button-with-icon(20px, 5px, -1px);
}
.button-unstyle {
padding: 0;
border: 0;
}

View File

@ -48,11 +48,15 @@
border-radius: 0.25rem;
position: relative;
>label {
> .callout-title {
position: relative;
top: -5px;
left: -10px;
color: #6c757d !important;
font-size: 1rem;
font-weight: $font-bold;
margin-bottom: 0.5rem;
line-height: inherit;
}
&:not(.callout-light) {

View File

@ -604,12 +604,6 @@
margin-bottom: 10px;
}
@mixin create-button {
@include peertube-button-link;
@include orange-button;
@include button-with-icon(20px, 5px, -1px);
}
@mixin row-blocks ($column-responsive: true, $min-height: 130px, $separator: true) {
display: flex;
min-height: $min-height;

View File

@ -35,6 +35,7 @@
<body id="custom-css" class="standalone-video-embed">
<div id="error-block">
<!-- eslint-disable-next-line @angular-eslint/template/elements-content -->
<h1 id="error-title"></h1>
<div id="error-content"></div>

View File

@ -6,7 +6,7 @@
<header>
<div class="logo">
<div class="icon">
<img src="../../assets/images/logo.svg">
<img src="../../assets/images/logo.svg" alt="">
</div>
<div>
PeerTube

View File

@ -164,7 +164,7 @@ describe('Save replay setting', function () {
})
it('Should correctly have updated the live and federated it when streaming in the live', async function () {
this.timeout(60000)
this.timeout(120000)
ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
@ -179,7 +179,7 @@ describe('Save replay setting', function () {
})
it('Should correctly delete the video files after the stream ended', async function () {
this.timeout(60000)
this.timeout(120000)
sessionEndDateMin = new Date()
await stopFfmpeg(ffmpegCommand)
@ -217,7 +217,7 @@ describe('Save replay setting', function () {
})
it('Should correctly terminate the stream on blacklist and delete the live', async function () {
this.timeout(60000)
this.timeout(120000)
await publishLiveAndBlacklist({ permanent: false, replay: false })
@ -241,7 +241,7 @@ describe('Save replay setting', function () {
})
it('Should correctly terminate the stream on delete and delete the video', async function () {
this.timeout(60000)
this.timeout(120000)
await publishLiveAndDelete({ permanent: false, replay: false })
@ -253,7 +253,7 @@ describe('Save replay setting', function () {
describe('With save replay enabled on non permanent live', function () {
it('Should correctly create and federate the "waiting for stream" live', async function () {
this.timeout(60000)
this.timeout(120000)
liveVideoUUID = await createLiveWrapper({ permanent: false, replay: true, replaySettings: { privacy: VideoPrivacy.UNLISTED } })
@ -265,7 +265,7 @@ describe('Save replay setting', function () {
})
it('Should correctly have updated the live and federated it when streaming in the live', async function () {
this.timeout(60000)
this.timeout(120000)
ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
@ -278,7 +278,7 @@ describe('Save replay setting', function () {
})
it('Should correctly have saved the live and federated it after the streaming', async function () {
this.timeout(60000)
this.timeout(120000)
const session = await servers[0].live.findLatestSession({ videoId: liveVideoUUID })
expect(session.endDate).to.not.exist
@ -319,7 +319,7 @@ describe('Save replay setting', function () {
})
it('Should update the saved live and correctly federate the updated attributes', async function () {
this.timeout(60000)
this.timeout(120000)
await servers[0].videos.update({ id: liveVideoUUID, attributes: { name: 'video updated', privacy: VideoPrivacy.PUBLIC } })
await waitJobs(servers)
@ -352,7 +352,7 @@ describe('Save replay setting', function () {
})
it('Should correctly terminate the stream on delete and delete the video', async function () {
this.timeout(60000)
this.timeout(120000)
await publishLiveAndDelete({ permanent: false, replay: true, replaySettings: { privacy: VideoPrivacy.PUBLIC } })
@ -367,7 +367,7 @@ describe('Save replay setting', function () {
describe('With a first live and its replay', function () {
it('Should correctly create and federate the "waiting for stream" live', async function () {
this.timeout(60000)
this.timeout(120000)
liveVideoUUID = await createLiveWrapper({ permanent: true, replay: true, replaySettings: { privacy: VideoPrivacy.UNLISTED } })
@ -379,7 +379,7 @@ describe('Save replay setting', function () {
})
it('Should correctly have updated the live and federated it when streaming in the live', async function () {
this.timeout(60000)
this.timeout(120000)
ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
@ -392,7 +392,7 @@ describe('Save replay setting', function () {
})
it('Should correctly have saved the live and federated it after the streaming', async function () {
this.timeout(60000)
this.timeout(120000)
const liveDetails = await servers[0].videos.get({ id: liveVideoUUID })
@ -457,7 +457,7 @@ describe('Save replay setting', function () {
})
it('Should correctly have updated the live and federated it when streaming in the live', async function () {
this.timeout(60000)
this.timeout(120000)
ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
@ -470,7 +470,7 @@ describe('Save replay setting', function () {
})
it('Should correctly have saved the live and federated it after the streaming', async function () {
this.timeout(60000)
this.timeout(120000)
const liveDetails = await servers[0].videos.get({ id: liveVideoUUID })
@ -547,7 +547,7 @@ describe('Save replay setting', function () {
})
it('Should correctly terminate the stream on delete and not save the video', async function () {
this.timeout(60000)
this.timeout(120000)
const { liveDetails } = await publishLiveAndDelete({
permanent: true,