Improve frontend accessibility

In particular checkboxes, likes/dislikes, share button, video thumbnails
and help buttons
This commit is contained in:
Chocobozzz 2018-07-17 14:44:19 +02:00
parent 4bdd9473fd
commit 0f7fedc398
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
22 changed files with 160 additions and 117 deletions

View File

@ -85,12 +85,10 @@
<div i18n class="inner-form-title">Signup</div>
<div class="form-group">
<input type="checkbox" id="signupEnabled" formControlName="signupEnabled">
<label for="signupEnabled"></label>
<label i18n for="signupEnabled">Signup enabled</label>
</div>
<my-peertube-checkbox
inputName="signupEnabled" formControlName="signupEnabled"
i18n-labelText labelText="Signup enabled"
></my-peertube-checkbox>
<div *ngIf="isSignupEnabled()" class="form-group">
<label i18n for="signupLimit">Signup limit</label>
@ -152,33 +150,24 @@
</div>
</div>
<div class="form-group">
<input type="checkbox" id="servicesTwitterWhitelisted" formControlName="servicesTwitterWhitelisted">
<label for="servicesTwitterWhitelisted"></label>
<label i18n for="servicesTwitterWhitelisted">Instance whitelisted by Twitter</label>
<my-help
helpType="custom" i18n-customHtml
customHtml="If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
<my-peertube-checkbox
inputName="servicesTwitterWhitelisted" formControlName="servicesTwitterWhitelisted"
i18n-labelText labelText="Instance whitelisted by Twitter"
i18n-helpHtml helpHtml="If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a> to see if you instance is whitelisted."
></my-help>
</div>
></my-peertube-checkbox>
</tab>
<tab i18n-heading heading="Advanced configuration">
<div i18n class="inner-form-title">Transcoding</div>
<div class="form-group">
<input type="checkbox" id="transcodingEnabled" formControlName="transcodingEnabled">
<label for="transcodingEnabled"></label>
<label i18n for="transcodingEnabled">Transcoding enabled</label>
<my-help helpType="custom" i18n-customHtml customHtml="If you disable transcoding, many videos from your users will not work!"></my-help>
</div>
<my-peertube-checkbox
inputName="transcodingEnabled" formControlName="transcodingEnabled"
i18n-labelText labelText="Transcoding enabled"
i18n-helpHtml helpHtml="If you disable transcoding, many videos from your users will not work!"
></my-peertube-checkbox>
<ng-template [ngIf]="isTranscodingEnabled()">
@ -197,12 +186,11 @@ Check this checkbox, save the configuration and test with a video URL of your in
</div>
<div class="form-group" *ngFor="let resolution of resolutions">
<input
type="checkbox" [id]="getResolutionKey(resolution)"
[formControlName]="getResolutionKey(resolution)"
>
<label [for]="getResolutionKey(resolution)"></label>
<label i18n [for]="getResolutionKey(resolution)">Resolution {{ resolution }} enabled</label>
<my-peertube-checkbox
[inputName]="getResolutionKey(resolution)" [formControlName]="getResolutionKey(resolution)"
i18n-labelText labelText="Resolution {{resolution}} enabled"
></my-peertube-checkbox>
</div>
</ng-template>

View File

@ -15,14 +15,10 @@
</div>
</div>
<div class="form-group">
<input
type="checkbox" id="autoPlayVideo"
formControlName="autoPlayVideo"
>
<label for="autoPlayVideo"></label>
<label i18n for="autoPlayVideo">Automatically plays video</label>
</div>
<my-peertube-checkbox
inputName="autoPlayVideo" formControlName="autoPlayVideo"
i18n-labelText labelText="Automatically plays video"
></my-peertube-checkbox>
<input type="submit" i18n-value value="Save" [disabled]="!form.valid">
</form>

View File

@ -1,10 +1,6 @@
@import '_variables';
@import '_mixins';
input[type=checkbox] {
@include peertube-checkbox(1px);
}
input[type=submit] {
@include peertube-button;
@include orange-button;

View File

@ -9,8 +9,7 @@
<div *ngFor="let videos of videoPages; let i = index" class="videos-page">
<div class="video" *ngFor="let video of videos; let j = index">
<div class="checkbox-container">
<input [id]="'video-check-' + video.id" type="checkbox" [(ngModel)]="checkedVideos[video.id]" />
<label [for]="'video-check-' + video.id"></label>
<my-peertube-checkbox [inputName]="'video-check-' + video.id" [(ngModel)]="checkedVideos[video.id]"></my-peertube-checkbox>
</div>
<my-video-thumbnail [video]="video"></my-video-thumbnail>

View File

@ -52,17 +52,6 @@
margin-top: 47px;
}
.checkbox-container {
display: flex;
align-items: center;
margin-right: 20px;
margin-left: 12px;
input[type=checkbox] {
@include peertube-checkbox(2px);
}
}
my-video-thumbnail {
margin-right: 10px;
}

View File

@ -0,0 +1,9 @@
<div class="form-group">
<label class="form-group-checkbox">
<input type="checkbox" [(ngModel)]="checked" (ngModelChange)="onModelChange()" [id]="inputName" [disabled]="isDisabled" />
<span role="checkbox" [attr.aria-checked]="checked"></span>
<span *ngIf="labelText">{{ labelText }}</span>
</label>
<my-help *ngIf="helpHtml" tooltipPlacement="top" helpType="custom" i18n-customHtml [customHtml]="helpHtml"></my-help>
</div>

View File

@ -0,0 +1,23 @@
@import '_variables';
@import '_mixins';
.form-group {
display: flex;
align-items: center;
.form-group-checkbox {
display: flex;
span {
font-weight: $font-regular;
margin: 0;
}
input {
@include peertube-checkbox(1px);
width: 10px;
margin-right: 10px;
}
}
}

View File

@ -0,0 +1,45 @@
import { Component, forwardRef, Input } from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
@Component({
selector: 'my-peertube-checkbox',
styleUrls: [ './peertube-checkbox.component.scss' ],
templateUrl: './peertube-checkbox.component.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => PeertubeCheckboxComponent),
multi: true
}
]
})
export class PeertubeCheckboxComponent implements ControlValueAccessor {
@Input() checked = false
@Input() inputName: string
@Input() labelText: string
@Input() helpHtml: string
isDisabled = false
propagateChange = (_: any) => { /* empty */ }
writeValue (checked: boolean) {
this.checked = checked
}
registerOnChange (fn: (_: any) => void) {
this.propagateChange = fn
}
registerOnTouched () {
// Unused
}
onModelChange () {
this.propagateChange(this.checked)
}
setDisabledState (isDisabled: boolean) {
this.isDisabled = isDisabled
}
}

View File

@ -13,10 +13,14 @@
</ng-template>
<span
role="button"
class="help-tooltip-button"
title="Get help"
i18n-title
[attr.aria-pressed]="isPopoverOpened"
[popover]="tooltipTemplate"
[placement]="tooltipPlacement"
[outsideClick]="true"
(onHidden)="onPopoverHidden()"
(onShown)="onPopoverShown()"
></span>

View File

@ -15,6 +15,7 @@ export class HelpComponent implements OnInit, OnChanges {
@Input() helpType: 'custom' | 'markdownText' | 'markdownEnhanced' = 'custom'
@Input() tooltipPlacement = 'right'
isPopoverOpened = false
mainHtml = ''
constructor (private i18n: I18n) { }
@ -27,6 +28,14 @@ export class HelpComponent implements OnInit, OnChanges {
this.init()
}
onPopoverHidden () {
this.isPopoverOpened = false
}
onPopoverShown () {
this.isPopoverOpened = true
}
private init () {
if (this.helpType === 'custom') {
this.mainHtml = this.customHtml

View File

@ -45,6 +45,7 @@ import { I18nPrimengCalendarService } from '@app/shared/i18n/i18n-primeng-calend
import { ScreenService } from '@app/shared/misc/screen.service'
import { VideoCaptionsValidatorsService } from '@app/shared/forms/form-validators/video-captions-validators.service'
import { VideoCaptionService } from '@app/shared/video-caption'
import { PeertubeCheckboxComponent } from '@app/shared/forms/peertube-checkbox.component'
@NgModule({
imports: [
@ -77,7 +78,8 @@ import { VideoCaptionService } from '@app/shared/video-caption'
MarkdownTextareaComponent,
InfiniteScrollerDirective,
HelpComponent,
ReactiveFileComponent
ReactiveFileComponent,
PeertubeCheckboxComponent
],
exports: [
@ -106,6 +108,7 @@ import { VideoCaptionService } from '@app/shared/video-caption'
InfiniteScrollerDirective,
HelpComponent,
ReactiveFileComponent,
PeertubeCheckboxComponent,
NumberFormatterPipe,
ObjectLengthPipe,

View File

@ -42,8 +42,6 @@ export class VideoCaptionService {
}
updateCaptions (videoId: number | string, videoCaptions: VideoCaptionEdit[]) {
if (videoCaptions.length === 0) return of(true)
const observables: Observable<any>[] = []
for (const videoCaption of videoCaptions) {
@ -58,6 +56,8 @@ export class VideoCaptionService {
}
}
if (observables.length === 0) return of(true)
return forkJoin(observables)
}
}

View File

@ -3,7 +3,7 @@
<div class="video-miniature-information">
<a
class="video-miniature-name"
class="video-miniature-name" alt=""
[routerLink]="[ '/videos/watch', video.uuid ]" [attr.title]="video.name" [ngClass]="{ 'blur-filter': isVideoBlur() }"
>
{{ video.name }}

View File

@ -110,31 +110,22 @@
</div>
</div>
<div class="form-group form-group-checkbox">
<input type="checkbox" id="nsfw" formControlName="nsfw" />
<label for="nsfw"></label>
<label i18n for="nsfw">This video contains mature or explicit content</label>
<my-help
tooltipPlacement="top" helpType="custom" i18n-customHtml
customHtml="Some instances do not list videos containing mature or explicit content by default."
></my-help>
</div>
<my-peertube-checkbox
inputName="nsfw" formControlName="nsfw"
i18n-labelText labelText="This video contains mature or explicit content"
i18n-helpHtml helpHtml="Some instances do not list videos containing mature or explicit content by default."
></my-peertube-checkbox>
<div class="form-group form-group-checkbox">
<input type="checkbox" id="commentsEnabled" formControlName="commentsEnabled" />
<label for="commentsEnabled"></label>
<label i18n for="commentsEnabled">Enable video comments</label>
</div>
<my-peertube-checkbox
inputName="commentsEnabled" formControlName="commentsEnabled"
i18n-labelText labelText="Enable video comments"
></my-peertube-checkbox>
<div class="form-group form-group-checkbox">
<input type="checkbox" id="waitTranscoding" formControlName="waitTranscoding" />
<label for="waitTranscoding"></label>
<label i18n for="waitTranscoding">Wait transcoding before publishing the video</label>
<my-help
tooltipPlacement="top" helpType="custom" i18n-customHtml
customHtml="If you decide not to wait for transcoding before publishing the video, it could be unplayable until transcoding ends."
></my-help>
</div>
<my-peertube-checkbox
inputName="waitTranscoding" formControlName="waitTranscoding"
i18n-labelText labelText="Wait transcoding before publishing the video"
i18n-helpHtml helpHtml="If you decide not to wait for transcoding before publishing the video, it could be unplayable until transcoding ends."
></my-peertube-checkbox>
</div>
</tab>

View File

@ -16,31 +16,12 @@
input {
@include peertube-input-text(100%);
display: block;
&[type=checkbox] {
@include peertube-checkbox(1px);
}
}
input, select {
font-size: 15px
}
.form-group-checkbox {
display: flex;
align-items: center;
label {
font-weight: $font-regular;
margin: 0;
}
input {
width: 10px;
margin-right: 10px;
}
}
.label-tags + span {
font-size: 15px;
}

View File

@ -49,6 +49,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
calendarDateFormat: string
private schedulerInterval
private firstPatchDone = false
constructor (
private formValidatorService: FormValidatorService,
@ -167,6 +168,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
.pipe(map(res => parseInt(res.toString(), 10)))
.subscribe(
newPrivacyId => {
this.schedulePublicationEnabled = newPrivacyId === this.SPECIAL_SCHEDULED_PRIVACY
// Value changed
@ -182,11 +184,18 @@ export class VideoEditComponent implements OnInit, OnDestroy {
scheduleControl.clearValidators()
waitTranscodingControl.enable()
waitTranscodingControl.setValue(true)
// Do not update the control value on first patch (values come from the server)
if (this.firstPatchDone === true) {
waitTranscodingControl.setValue(true)
}
}
scheduleControl.updateValueAndValidity()
waitTranscodingControl.updateValueAndValidity()
this.firstPatchDone = true
}
)
}

View File

@ -107,6 +107,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
},
err => {
this.loadingBar.complete()
this.isUpdatingVideo = false
this.notificationsService.error(this.i18n('Error'), err.message)
console.error(err)

View File

@ -48,15 +48,15 @@
<div class="video-actions-rates">
<div class="video-actions">
<div
*ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()"
class="action-button action-button-like"
*ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()"
class="action-button action-button-like" role="button" [attr.aria-pressed]="userRating === 'like'"
>
<span class="icon icon-like" i18n-title title="Like this video" ></span>
</div>
<div
*ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'dislike' }" (click)="setDislike()"
class="action-button action-button-dislike"
*ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'dislike' }" (click)="setDislike()"
class="action-button action-button-dislike" role="button" [attr.aria-pressed]="userRating === 'dislike'"
>
<span class="icon icon-dislike" i18n-title title="Dislike this video"></span>
</div>
@ -66,12 +66,12 @@
<span class="icon-text" i18n>Support</span>
</div>
<div (click)="showShareModal()" class="action-button action-button-share">
<div (click)="showShareModal()" class="action-button action-button-share" role="button">
<span class="icon icon-share"></span>
<span class="icon-text" i18n>Share</span>
</div>
<div class="action-more" dropdown dropup="true" placement="right">
<div class="action-more" dropdown dropup="true" placement="right" role="button">
<div class="action-button" dropdownToggle>
<span class="icon icon-more"></span>
</div>

View File

@ -314,7 +314,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
if (!errorMessage) return
// Display a message in the video player instead of a notification
if (errorMessage.indexOf('http error') !== -1) {
if (errorMessage.indexOf('from xs param') !== -1) {
this.flushPlayer()
this.remoteServerDown = true
return

View File

@ -269,7 +269,7 @@ class PeerTubePlugin extends Plugin {
}
// Remote instance is down
if (err.message.indexOf('http error from xs param') !== -1) {
if (err.message.indexOf('from xs param') !== -1) {
this.handleError(err)
}

View File

@ -240,7 +240,7 @@
@mixin peertube-checkbox ($border-width) {
display: none;
& + label {
& + span {
position: relative;
width: 18px;
height: 18px;
@ -263,7 +263,7 @@
}
}
&:checked + label {
&:checked + span {
border-color: transparent;
background: $orange-color;
animation: jelly 0.6s ease;
@ -274,7 +274,7 @@
}
}
& + label + label {
& + span + span {
font-size: 15px;
font-weight: $font-regular;
margin-left: 5px;
@ -282,8 +282,8 @@
display: inline;
}
&[disabled] + label,
&[disabled] + label + label{
&[disabled] + span,
&[disabled] + span + span{
opacity: 0.5;
cursor: default;
}

View File

@ -321,7 +321,7 @@ class PeerTubeEmbed {
}
private handleError (err: Error) {
if (err.message.indexOf('http error') !== -1) {
if (err.message.indexOf('from xs param') !== -1) {
this.player.dispose()
this.videoElement = null
this.displayError('This video is not available because the remote instance is not responding.')