Support playlists in share modal

This commit is contained in:
Chocobozzz 2019-12-05 10:05:00 +01:00
parent 689a4f6946
commit 3a1fed11c5
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
9 changed files with 205 additions and 142 deletions

View File

@ -0,0 +1,9 @@
<div class="input-group">
<input #urlInput (click)="urlInput.select()" type="text" class="form-control readonly" readonly [value]="value" />
<div class="input-group-append">
<button [ngxClipboard]="urlInput" (click)="activateCopiedMessage()" type="button" class="btn btn-outline-secondary">
<span class="glyphicon glyphicon-copy"></span>
</button>
</div>
</div>

View File

@ -0,0 +1,3 @@
input.readonly {
font-size: 15px;
}

View File

@ -0,0 +1,21 @@
import { Component, Input } from '@angular/core'
import { Notifier } from '@app/core'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({
selector: 'my-input-readonly-copy',
templateUrl: './input-readonly-copy.component.html',
styleUrls: [ './input-readonly-copy.component.scss' ]
})
export class InputReadonlyCopyComponent {
@Input() value = ''
constructor (
private notifier: Notifier,
private i18n: I18n
) { }
activateCopiedMessage () {
this.notifier.success(this.i18n('Copied'))
}
}

View File

@ -95,6 +95,7 @@ import { ClipboardModule } from 'ngx-clipboard'
import { FollowService } from '@app/shared/instance/follow.service' import { FollowService } from '@app/shared/instance/follow.service'
import { MultiSelectModule } from 'primeng/multiselect' import { MultiSelectModule } from 'primeng/multiselect'
import { FeatureBooleanComponent } from '@app/shared/instance/feature-boolean.component' import { FeatureBooleanComponent } from '@app/shared/instance/feature-boolean.component'
import { InputReadonlyCopyComponent } from '@app/shared/forms/input-readonly-copy.component'
@NgModule({ @NgModule({
imports: [ imports: [
@ -155,6 +156,7 @@ import { FeatureBooleanComponent } from '@app/shared/instance/feature-boolean.co
ReactiveFileComponent, ReactiveFileComponent,
PeertubeCheckboxComponent, PeertubeCheckboxComponent,
TimestampInputComponent, TimestampInputComponent,
InputReadonlyCopyComponent,
SubscribeButtonComponent, SubscribeButtonComponent,
RemoteSubscribeComponent, RemoteSubscribeComponent,
@ -220,6 +222,7 @@ import { FeatureBooleanComponent } from '@app/shared/instance/feature-boolean.co
InfiniteScrollerDirective, InfiniteScrollerDirective,
TextareaAutoResizeDirective, TextareaAutoResizeDirective,
HelpComponent, HelpComponent,
InputReadonlyCopyComponent,
ReactiveFileComponent, ReactiveFileComponent,
PeertubeCheckboxComponent, PeertubeCheckboxComponent,

View File

@ -4,167 +4,176 @@
<my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon> <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<ngb-tabset class="root-tabset bootstrap" (tabChange)="onTabChange($event)"> <div class="playlist" *ngIf="hasPlaylist()">
<div class="title-page title-page-single" i18n>Share the playlist</div>
<ngb-tab i18n-title title="URL" id="url"> <my-input-readonly-copy [value]="getPlaylistUrl()"></my-input-readonly-copy>
<ng-template ngbTabContent>
<div class="tab-content"> <div class="filters">
<div class="input-group">
<input #urlInput (click)="urlInput.select()" type="text" class="form-control readonly" readonly [value]="getVideoUrl()" />
<div class="input-group-append">
<button [ngxClipboard]="urlInput" (click)="activateCopiedMessage()" type="button" class="btn btn-outline-secondary">
<span class="glyphicon glyphicon-copy"></span>
</button>
</div>
</div>
</div>
</ng-template> <div class="form-group">
</ngb-tab>
<ngb-tab i18n-title title="QR-Code" id="qrcode">
<ng-template ngbTabContent>
<div class="tab-content">
<qrcode [qrdata]="getVideoUrl()" size="256" level="Q"></qrcode>
</div>
</ng-template>
</ngb-tab>
<ngb-tab i18n-title title="Embed" id="embed">
<ng-template ngbTabContent>
<div class="tab-content">
<div class="input-group">
<input #shareInput (click)="shareInput.select()" type="text" class="form-control readonly" readonly [value]="getVideoIframeCode()" />
<div class="input-group-append">
<button [ngxClipboard]="shareInput" (click)="activateCopiedMessage()" type="button" class="btn btn-outline-secondary">
<span class="glyphicon glyphicon-copy"></span>
</button>
</div>
</div>
<div i18n *ngIf="notSecure()" class="alert alert-warning">
The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites).
</div>
</div>
</ng-template>
</ngb-tab>
</ngb-tabset>
<div class="filters">
<div>
<div class="form-group start-at">
<my-peertube-checkbox <my-peertube-checkbox
inputName="startAt" [(ngModel)]="customizations.startAtCheckbox" inputName="includeVideoInPlaylist" [(ngModel)]="includeVideoInPlaylist"
i18n-labelText labelText="Start at" i18n-labelText labelText="Share the playlist at this video position"
></my-peertube-checkbox> ></my-peertube-checkbox>
<my-timestamp-input
[timestamp]="customizations.startAt"
[maxTimestamp]="video.duration"
[disabled]="!customizations.startAtCheckbox"
[(ngModel)]="customizations.startAt"
>
</my-timestamp-input>
</div> </div>
<div *ngIf="videoCaptions.length !== 0" class="form-group video-caption-block">
<my-peertube-checkbox
inputName="subtitleCheckbox" [(ngModel)]="customizations.subtitleCheckbox"
i18n-labelText labelText="Auto select subtitle"
></my-peertube-checkbox>
<div class="peertube-select-container" [ngClass]="{ disabled: !customizations.subtitleCheckbox }">
<select [(ngModel)]="customizations.subtitle" [disabled]="!customizations.subtitleCheckbox">
<option *ngFor="let caption of videoCaptions" [value]="caption.language.id">{{ caption.language.label }}</option>
</select>
</div>
</div>
</div> </div>
</div>
<div (click)="isAdvancedCustomizationCollapsed = !isAdvancedCustomizationCollapsed" role="button" class="advanced-filters-button"
[attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic">
<ng-container *ngIf="isAdvancedCustomizationCollapsed"> <div class="video">
<span class="glyphicon glyphicon-menu-down"></span> <div class="title-page title-page-single" *ngIf="hasPlaylist()" i18n>Share the video</div>
<ng-container i18n> <ngb-tabset class="root-tabset bootstrap" (tabChange)="onTabChange($event)">
More customization
</ng-container>
</ng-container>
<ng-container *ngIf="!isAdvancedCustomizationCollapsed"> <ngb-tab i18n-title title="URL" id="url">
<span class="glyphicon glyphicon-menu-up"></span> <ng-template ngbTabContent>
<ng-container i18n> <div class="tab-content">
Less customization <my-input-readonly-copy [value]="getVideoUrl()"></my-input-readonly-copy>
</ng-container> </div>
</ng-container>
</div>
<div class="advanced-filters collapse-transition" [ngbCollapse]="isAdvancedCustomizationCollapsed"> </ng-template>
</ngb-tab>
<ngb-tab i18n-title title="QR-Code" id="qrcode">
<ng-template ngbTabContent>
<div class="tab-content">
<qrcode [qrdata]="getVideoUrl()" size="256" level="Q"></qrcode>
</div>
</ng-template>
</ngb-tab>
<ngb-tab i18n-title title="Embed" id="embed">
<ng-template ngbTabContent>
<div class="tab-content">
<my-input-readonly-copy [value]="getVideoIframeCode()"></my-input-readonly-copy>
<div i18n *ngIf="notSecure()" class="alert alert-warning">
The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites).
</div>
</div>
</ng-template>
</ngb-tab>
</ngb-tabset>
<div class="filters">
<div> <div>
<div class="form-group stop-at"> <div class="form-group start-at">
<my-peertube-checkbox <my-peertube-checkbox
inputName="stopAt" [(ngModel)]="customizations.stopAtCheckbox" inputName="startAt" [(ngModel)]="customizations.startAtCheckbox"
i18n-labelText labelText="Stop at" i18n-labelText labelText="Start at"
></my-peertube-checkbox> ></my-peertube-checkbox>
<my-timestamp-input <my-timestamp-input
[timestamp]="customizations.stopAt" [timestamp]="customizations.startAt"
[maxTimestamp]="video.duration" [maxTimestamp]="video.duration"
[disabled]="!customizations.stopAtCheckbox" [disabled]="!customizations.startAtCheckbox"
[(ngModel)]="customizations.stopAt" [(ngModel)]="customizations.startAt"
> >
</my-timestamp-input> </my-timestamp-input>
</div> </div>
<div class="form-group"> <div *ngIf="videoCaptions.length !== 0" class="form-group video-caption-block">
<my-peertube-checkbox <my-peertube-checkbox
inputName="autoplay" [(ngModel)]="customizations.autoplay" inputName="subtitleCheckbox" [(ngModel)]="customizations.subtitleCheckbox"
i18n-labelText labelText="Autoplay" i18n-labelText labelText="Auto select subtitle"
></my-peertube-checkbox> ></my-peertube-checkbox>
</div>
<div class="form-group"> <div class="peertube-select-container" [ngClass]="{ disabled: !customizations.subtitleCheckbox }">
<my-peertube-checkbox <select [(ngModel)]="customizations.subtitle" [disabled]="!customizations.subtitleCheckbox">
inputName="muted" [(ngModel)]="customizations.muted" <option *ngFor="let caption of videoCaptions" [value]="caption.language.id">{{ caption.language.label }}</option>
i18n-labelText labelText="Muted" </select>
></my-peertube-checkbox> </div>
</div>
<div class="form-group">
<my-peertube-checkbox
inputName="loop" [(ngModel)]="customizations.loop"
i18n-labelText labelText="Loop"
></my-peertube-checkbox>
</div> </div>
</div> </div>
<ng-container *ngIf="isInEmbedTab()"> <div (click)="isAdvancedCustomizationCollapsed = !isAdvancedCustomizationCollapsed" role="button" class="advanced-filters-button"
<div class="form-group"> [attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic">
<my-peertube-checkbox
inputName="title" [(ngModel)]="customizations.title" <ng-container *ngIf="isAdvancedCustomizationCollapsed">
i18n-labelText labelText="Display video title" <span class="glyphicon glyphicon-menu-down"></span>
></my-peertube-checkbox>
<ng-container i18n>
More customization
</ng-container>
</ng-container>
<ng-container *ngIf="!isAdvancedCustomizationCollapsed">
<span class="glyphicon glyphicon-menu-up"></span>
<ng-container i18n>
Less customization
</ng-container>
</ng-container>
</div>
<div class="advanced-filters collapse-transition" [ngbCollapse]="isAdvancedCustomizationCollapsed">
<div>
<div class="form-group stop-at">
<my-peertube-checkbox
inputName="stopAt" [(ngModel)]="customizations.stopAtCheckbox"
i18n-labelText labelText="Stop at"
></my-peertube-checkbox>
<my-timestamp-input
[timestamp]="customizations.stopAt"
[maxTimestamp]="video.duration"
[disabled]="!customizations.stopAtCheckbox"
[(ngModel)]="customizations.stopAt"
>
</my-timestamp-input>
</div>
<div class="form-group">
<my-peertube-checkbox
inputName="autoplay" [(ngModel)]="customizations.autoplay"
i18n-labelText labelText="Autoplay"
></my-peertube-checkbox>
</div>
<div class="form-group">
<my-peertube-checkbox
inputName="muted" [(ngModel)]="customizations.muted"
i18n-labelText labelText="Muted"
></my-peertube-checkbox>
</div>
<div class="form-group">
<my-peertube-checkbox
inputName="loop" [(ngModel)]="customizations.loop"
i18n-labelText labelText="Loop"
></my-peertube-checkbox>
</div>
</div> </div>
<div class="form-group"> <ng-container *ngIf="isInEmbedTab()">
<my-peertube-checkbox <div class="form-group">
inputName="warningTitle" [(ngModel)]="customizations.warningTitle" <my-peertube-checkbox
i18n-labelText labelText="Display privacy warning" inputName="title" [(ngModel)]="customizations.title"
></my-peertube-checkbox> i18n-labelText labelText="Display video title"
</div> ></my-peertube-checkbox>
</div>
<div class="form-group"> <div class="form-group">
<my-peertube-checkbox <my-peertube-checkbox
inputName="controls" [(ngModel)]="customizations.controls" inputName="warningTitle" [(ngModel)]="customizations.warningTitle"
i18n-labelText labelText="Display player controls" i18n-labelText labelText="Display privacy warning"
></my-peertube-checkbox> ></my-peertube-checkbox>
</div> </div>
</ng-container>
<div class="form-group">
<my-peertube-checkbox
inputName="controls" [(ngModel)]="customizations.controls"
i18n-labelText labelText="Display player controls"
></my-peertube-checkbox>
</div>
</ng-container>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,6 +1,18 @@
@import '_mixins'; @import '_mixins';
@import '_variables'; @import '_variables';
my-input-readonly-copy {
width: 100%;
}
.title-page.title-page-single {
margin-top: 0;
}
.playlist {
margin-bottom: 50px;
}
.peertube-select-container { .peertube-select-container {
@include peertube-select-container(200px); @include peertube-select-container(200px);
} }
@ -25,10 +37,6 @@
margin-top: 20px; margin-top: 20px;
} }
input.readonly {
font-size: 15px;
}
.filters { .filters {
margin-top: 30px; margin-top: 30px;
padding-top: 30px; padding-top: 30px;

View File

@ -5,6 +5,7 @@ import { buildVideoEmbed, buildVideoLink } from '../../../../assets/player/utils
import { I18n } from '@ngx-translate/i18n-polyfill' import { I18n } from '@ngx-translate/i18n-polyfill'
import { NgbModal, NgbTabChangeEvent } from '@ng-bootstrap/ng-bootstrap' import { NgbModal, NgbTabChangeEvent } from '@ng-bootstrap/ng-bootstrap'
import { VideoCaption } from '@shared/models' import { VideoCaption } from '@shared/models'
import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
type Customizations = { type Customizations = {
startAtCheckbox: boolean startAtCheckbox: boolean
@ -34,18 +35,16 @@ export class VideoShareComponent {
@Input() video: VideoDetails = null @Input() video: VideoDetails = null
@Input() videoCaptions: VideoCaption[] = [] @Input() videoCaptions: VideoCaption[] = []
@Input() playlist: VideoPlaylist = null
activeId: 'url' | 'qrcode' | 'embed' activeId: 'url' | 'qrcode' | 'embed'
customizations: Customizations customizations: Customizations
isAdvancedCustomizationCollapsed = true isAdvancedCustomizationCollapsed = true
includeVideoInPlaylist = false
private currentVideoTimestamp: number private currentVideoTimestamp: number
constructor ( constructor (private modalService: NgbModal) { }
private modalService: NgbModal,
private notifier: Notifier,
private i18n: I18n
) { }
show (currentVideoTimestamp?: number) { show (currentVideoTimestamp?: number) {
this.currentVideoTimestamp = currentVideoTimestamp this.currentVideoTimestamp = currentVideoTimestamp
@ -86,17 +85,22 @@ export class VideoShareComponent {
} }
getVideoUrl () { getVideoUrl () {
const options = this.getOptions() const baseUrl = window.location.origin + '/videos/watch/' + this.video.uuid
const options = this.getOptions(baseUrl)
return buildVideoLink(options) return buildVideoLink(options)
} }
notSecure () { getPlaylistUrl () {
return window.location.protocol === 'http:' const base = window.location.origin + '/videos/watch/playlist/' + this.playlist.uuid
if (!this.includeVideoInPlaylist) return base
return base + '?videoId=' + this.video.uuid
} }
activateCopiedMessage () { notSecure () {
this.notifier.success(this.i18n('Copied')) return window.location.protocol === 'http:'
} }
onTabChange (event: NgbTabChangeEvent) { onTabChange (event: NgbTabChangeEvent) {
@ -107,6 +111,10 @@ export class VideoShareComponent {
return this.activeId === 'embed' return this.activeId === 'embed'
} }
hasPlaylist () {
return !!this.playlist
}
private getOptions (baseUrl?: string) { private getOptions (baseUrl?: string) {
return { return {
baseUrl, baseUrl,

View File

@ -224,5 +224,5 @@
<ng-container *ngIf="video !== null"> <ng-container *ngIf="video !== null">
<my-video-support #videoSupportModal [video]="video"></my-video-support> <my-video-support #videoSupportModal [video]="video"></my-video-support>
<my-video-share #videoShareModal [video]="video" [videoCaptions]="videoCaptions"></my-video-share> <my-video-share #videoShareModal [video]="video" [videoCaptions]="videoCaptions" [playlist]="playlist"></my-video-share>
</ng-container> </ng-container>

View File

@ -51,6 +51,8 @@ function buildVideoLink (options: {
: window.location.origin + window.location.pathname.replace('/embed/', '/watch/') : window.location.origin + window.location.pathname.replace('/embed/', '/watch/')
const params = new URLSearchParams(window.location.search) const params = new URLSearchParams(window.location.search)
// Remove this unused parameter when we are on a playlist page
params.delete('videoId')
if (options.startTime) { if (options.startTime) {
const startTimeInt = Math.floor(options.startTime) const startTimeInt = Math.floor(options.startTime)