aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/shared/forms/input-readonly-copy.component.html9
-rw-r--r--client/src/app/shared/forms/input-readonly-copy.component.scss3
-rw-r--r--client/src/app/shared/forms/input-readonly-copy.component.ts21
-rw-r--r--client/src/app/shared/shared.module.ts3
-rw-r--r--client/src/app/videos/+video-watch/modal/video-share.component.html261
-rw-r--r--client/src/app/videos/+video-watch/modal/video-share.component.scss16
-rw-r--r--client/src/app/videos/+video-watch/modal/video-share.component.ts28
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.html2
-rw-r--r--client/src/assets/player/utils.ts2
9 files changed, 204 insertions, 141 deletions
diff --git a/client/src/app/shared/forms/input-readonly-copy.component.html b/client/src/app/shared/forms/input-readonly-copy.component.html
new file mode 100644
index 000000000..27571b63f
--- /dev/null
+++ b/client/src/app/shared/forms/input-readonly-copy.component.html
@@ -0,0 +1,9 @@
1<div class="input-group">
2 <input #urlInput (click)="urlInput.select()" type="text" class="form-control readonly" readonly [value]="value" />
3
4 <div class="input-group-append">
5 <button [ngxClipboard]="urlInput" (click)="activateCopiedMessage()" type="button" class="btn btn-outline-secondary">
6 <span class="glyphicon glyphicon-copy"></span>
7 </button>
8 </div>
9</div>
diff --git a/client/src/app/shared/forms/input-readonly-copy.component.scss b/client/src/app/shared/forms/input-readonly-copy.component.scss
new file mode 100644
index 000000000..8dc4f113c
--- /dev/null
+++ b/client/src/app/shared/forms/input-readonly-copy.component.scss
@@ -0,0 +1,3 @@
1input.readonly {
2 font-size: 15px;
3}
diff --git a/client/src/app/shared/forms/input-readonly-copy.component.ts b/client/src/app/shared/forms/input-readonly-copy.component.ts
new file mode 100644
index 000000000..7528fb7a1
--- /dev/null
+++ b/client/src/app/shared/forms/input-readonly-copy.component.ts
@@ -0,0 +1,21 @@
1import { Component, Input } from '@angular/core'
2import { Notifier } from '@app/core'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4
5@Component({
6 selector: 'my-input-readonly-copy',
7 templateUrl: './input-readonly-copy.component.html',
8 styleUrls: [ './input-readonly-copy.component.scss' ]
9})
10export class InputReadonlyCopyComponent {
11 @Input() value = ''
12
13 constructor (
14 private notifier: Notifier,
15 private i18n: I18n
16 ) { }
17
18 activateCopiedMessage () {
19 this.notifier.success(this.i18n('Copied'))
20 }
21}
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index 8cbb15bfa..29ddf7b81 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -95,6 +95,7 @@ import { ClipboardModule } from 'ngx-clipboard'
95import { FollowService } from '@app/shared/instance/follow.service' 95import { FollowService } from '@app/shared/instance/follow.service'
96import { MultiSelectModule } from 'primeng/multiselect' 96import { MultiSelectModule } from 'primeng/multiselect'
97import { FeatureBooleanComponent } from '@app/shared/instance/feature-boolean.component' 97import { FeatureBooleanComponent } from '@app/shared/instance/feature-boolean.component'
98import { InputReadonlyCopyComponent } from '@app/shared/forms/input-readonly-copy.component'
98 99
99@NgModule({ 100@NgModule({
100 imports: [ 101 imports: [
@@ -155,6 +156,7 @@ import { FeatureBooleanComponent } from '@app/shared/instance/feature-boolean.co
155 ReactiveFileComponent, 156 ReactiveFileComponent,
156 PeertubeCheckboxComponent, 157 PeertubeCheckboxComponent,
157 TimestampInputComponent, 158 TimestampInputComponent,
159 InputReadonlyCopyComponent,
158 160
159 SubscribeButtonComponent, 161 SubscribeButtonComponent,
160 RemoteSubscribeComponent, 162 RemoteSubscribeComponent,
@@ -220,6 +222,7 @@ import { FeatureBooleanComponent } from '@app/shared/instance/feature-boolean.co
220 InfiniteScrollerDirective, 222 InfiniteScrollerDirective,
221 TextareaAutoResizeDirective, 223 TextareaAutoResizeDirective,
222 HelpComponent, 224 HelpComponent,
225 InputReadonlyCopyComponent,
223 226
224 ReactiveFileComponent, 227 ReactiveFileComponent,
225 PeertubeCheckboxComponent, 228 PeertubeCheckboxComponent,
diff --git a/client/src/app/videos/+video-watch/modal/video-share.component.html b/client/src/app/videos/+video-watch/modal/video-share.component.html
index e0be9f265..549a9f30e 100644
--- a/client/src/app/videos/+video-watch/modal/video-share.component.html
+++ b/client/src/app/videos/+video-watch/modal/video-share.component.html
@@ -4,167 +4,176 @@
4 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon> 4 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
5 </div> 5 </div>
6 6
7
7 <div class="modal-body"> 8 <div class="modal-body">
8 <ngb-tabset class="root-tabset bootstrap" (tabChange)="onTabChange($event)"> 9 <div class="playlist" *ngIf="hasPlaylist()">
9 10 <div class="title-page title-page-single" i18n>Share the playlist</div>
10 <ngb-tab i18n-title title="URL" id="url">
11 <ng-template ngbTabContent>
12
13 <div class="tab-content">
14 <div class="input-group">
15 <input #urlInput (click)="urlInput.select()" type="text" class="form-control readonly" readonly [value]="getVideoUrl()" />
16 <div class="input-group-append">
17 <button [ngxClipboard]="urlInput" (click)="activateCopiedMessage()" type="button" class="btn btn-outline-secondary">
18 <span class="glyphicon glyphicon-copy"></span>
19 </button>
20 </div>
21 </div>
22 </div>
23 11
24 </ng-template> 12 <my-input-readonly-copy [value]="getPlaylistUrl()"></my-input-readonly-copy>
25 </ngb-tab>
26 13
27 <ngb-tab i18n-title title="QR-Code" id="qrcode"> 14 <div class="filters">
28 <ng-template ngbTabContent>
29 <div class="tab-content">
30 <qrcode [qrdata]="getVideoUrl()" size="256" level="Q"></qrcode>
31 </div>
32 </ng-template>
33 </ngb-tab>
34
35 <ngb-tab i18n-title title="Embed" id="embed">
36 <ng-template ngbTabContent>
37 <div class="tab-content">
38 <div class="input-group">
39 <input #shareInput (click)="shareInput.select()" type="text" class="form-control readonly" readonly [value]="getVideoIframeCode()" />
40 <div class="input-group-append">
41 <button [ngxClipboard]="shareInput" (click)="activateCopiedMessage()" type="button" class="btn btn-outline-secondary">
42 <span class="glyphicon glyphicon-copy"></span>
43 </button>
44 </div>
45 </div>
46 15
47 <div i18n *ngIf="notSecure()" class="alert alert-warning"> 16 <div class="form-group">
48 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).
49 </div>
50 </div>
51 </ng-template>
52 </ngb-tab>
53
54 </ngb-tabset>
55
56 <div class="filters">
57 <div>
58 <div class="form-group start-at">
59 <my-peertube-checkbox 17 <my-peertube-checkbox
60 inputName="startAt" [(ngModel)]="customizations.startAtCheckbox" 18 inputName="includeVideoInPlaylist" [(ngModel)]="includeVideoInPlaylist"
61 i18n-labelText labelText="Start at" 19 i18n-labelText labelText="Share the playlist at this video position"
62 ></my-peertube-checkbox> 20 ></my-peertube-checkbox>
63
64 <my-timestamp-input
65 [timestamp]="customizations.startAt"
66 [maxTimestamp]="video.duration"
67 [disabled]="!customizations.startAtCheckbox"
68 [(ngModel)]="customizations.startAt"
69 >
70 </my-timestamp-input>
71 </div> 21 </div>
72 22
73 <div *ngIf="videoCaptions.length !== 0" class="form-group video-caption-block">
74 <my-peertube-checkbox
75 inputName="subtitleCheckbox" [(ngModel)]="customizations.subtitleCheckbox"
76 i18n-labelText labelText="Auto select subtitle"
77 ></my-peertube-checkbox>
78
79 <div class="peertube-select-container" [ngClass]="{ disabled: !customizations.subtitleCheckbox }">
80 <select [(ngModel)]="customizations.subtitle" [disabled]="!customizations.subtitleCheckbox">
81 <option *ngFor="let caption of videoCaptions" [value]="caption.language.id">{{ caption.language.label }}</option>
82 </select>
83 </div>
84 </div>
85 </div> 23 </div>
24 </div>
86 25
87 <div (click)="isAdvancedCustomizationCollapsed = !isAdvancedCustomizationCollapsed" role="button" class="advanced-filters-button"
88 [attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic">
89 26
90 <ng-container *ngIf="isAdvancedCustomizationCollapsed"> 27 <div class="video">
91 <span class="glyphicon glyphicon-menu-down"></span> 28 <div class="title-page title-page-single" *ngIf="hasPlaylist()" i18n>Share the video</div>
92 29
93 <ng-container i18n> 30 <ngb-tabset class="root-tabset bootstrap" (tabChange)="onTabChange($event)">
94 More customization
95 </ng-container>
96 </ng-container>
97 31
98 <ng-container *ngIf="!isAdvancedCustomizationCollapsed"> 32 <ngb-tab i18n-title title="URL" id="url">
99 <span class="glyphicon glyphicon-menu-up"></span> 33 <ng-template ngbTabContent>
100 34
101 <ng-container i18n> 35 <div class="tab-content">
102 Less customization 36 <my-input-readonly-copy [value]="getVideoUrl()"></my-input-readonly-copy>
103 </ng-container> 37 </div>
104 </ng-container> 38
105 </div> 39 </ng-template>
40 </ngb-tab>
41
42 <ngb-tab i18n-title title="QR-Code" id="qrcode">
43 <ng-template ngbTabContent>
44 <div class="tab-content">
45 <qrcode [qrdata]="getVideoUrl()" size="256" level="Q"></qrcode>
46 </div>
47 </ng-template>
48 </ngb-tab>
106 49
107 <div class="advanced-filters collapse-transition" [ngbCollapse]="isAdvancedCustomizationCollapsed"> 50 <ngb-tab i18n-title title="Embed" id="embed">
51 <ng-template ngbTabContent>
52 <div class="tab-content">
53 <my-input-readonly-copy [value]="getVideoIframeCode()"></my-input-readonly-copy>
54
55 <div i18n *ngIf="notSecure()" class="alert alert-warning">
56 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).
57 </div>
58 </div>
59 </ng-template>
60 </ngb-tab>
61
62 </ngb-tabset>
63
64 <div class="filters">
108 <div> 65 <div>
109 <div class="form-group stop-at"> 66 <div class="form-group start-at">
110 <my-peertube-checkbox 67 <my-peertube-checkbox
111 inputName="stopAt" [(ngModel)]="customizations.stopAtCheckbox" 68 inputName="startAt" [(ngModel)]="customizations.startAtCheckbox"
112 i18n-labelText labelText="Stop at" 69 i18n-labelText labelText="Start at"
113 ></my-peertube-checkbox> 70 ></my-peertube-checkbox>
114 71
115 <my-timestamp-input 72 <my-timestamp-input
116 [timestamp]="customizations.stopAt" 73 [timestamp]="customizations.startAt"
117 [maxTimestamp]="video.duration" 74 [maxTimestamp]="video.duration"
118 [disabled]="!customizations.stopAtCheckbox" 75 [disabled]="!customizations.startAtCheckbox"
119 [(ngModel)]="customizations.stopAt" 76 [(ngModel)]="customizations.startAt"
120 > 77 >
121 </my-timestamp-input> 78 </my-timestamp-input>
122 </div> 79 </div>
123 80
124 <div class="form-group"> 81 <div *ngIf="videoCaptions.length !== 0" class="form-group video-caption-block">
125 <my-peertube-checkbox 82 <my-peertube-checkbox
126 inputName="autoplay" [(ngModel)]="customizations.autoplay" 83 inputName="subtitleCheckbox" [(ngModel)]="customizations.subtitleCheckbox"
127 i18n-labelText labelText="Autoplay" 84 i18n-labelText labelText="Auto select subtitle"
128 ></my-peertube-checkbox> 85 ></my-peertube-checkbox>
129 </div>
130 86
131 <div class="form-group"> 87 <div class="peertube-select-container" [ngClass]="{ disabled: !customizations.subtitleCheckbox }">
132 <my-peertube-checkbox 88 <select [(ngModel)]="customizations.subtitle" [disabled]="!customizations.subtitleCheckbox">
133 inputName="muted" [(ngModel)]="customizations.muted" 89 <option *ngFor="let caption of videoCaptions" [value]="caption.language.id">{{ caption.language.label }}</option>
134 i18n-labelText labelText="Muted" 90 </select>
135 ></my-peertube-checkbox> 91 </div>
136 </div> 92 </div>
93 </div>
137 94
138 <div class="form-group"> 95 <div (click)="isAdvancedCustomizationCollapsed = !isAdvancedCustomizationCollapsed" role="button" class="advanced-filters-button"
139 <my-peertube-checkbox 96 [attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic">
140 inputName="loop" [(ngModel)]="customizations.loop" 97
141 i18n-labelText labelText="Loop" 98 <ng-container *ngIf="isAdvancedCustomizationCollapsed">
142 ></my-peertube-checkbox> 99 <span class="glyphicon glyphicon-menu-down"></span>
143 </div> 100
101 <ng-container i18n>
102 More customization
103 </ng-container>
104 </ng-container>
105
106 <ng-container *ngIf="!isAdvancedCustomizationCollapsed">
107 <span class="glyphicon glyphicon-menu-up"></span>
108
109 <ng-container i18n>
110 Less customization
111 </ng-container>
112 </ng-container>
144 </div> 113 </div>
145 114
146 <ng-container *ngIf="isInEmbedTab()"> 115 <div class="advanced-filters collapse-transition" [ngbCollapse]="isAdvancedCustomizationCollapsed">
147 <div class="form-group"> 116 <div>
148 <my-peertube-checkbox 117 <div class="form-group stop-at">
149 inputName="title" [(ngModel)]="customizations.title" 118 <my-peertube-checkbox
150 i18n-labelText labelText="Display video title" 119 inputName="stopAt" [(ngModel)]="customizations.stopAtCheckbox"
151 ></my-peertube-checkbox> 120 i18n-labelText labelText="Stop at"
152 </div> 121 ></my-peertube-checkbox>
122
123 <my-timestamp-input
124 [timestamp]="customizations.stopAt"
125 [maxTimestamp]="video.duration"
126 [disabled]="!customizations.stopAtCheckbox"
127 [(ngModel)]="customizations.stopAt"
128 >
129 </my-timestamp-input>
130 </div>
153 131
154 <div class="form-group"> 132 <div class="form-group">
155 <my-peertube-checkbox 133 <my-peertube-checkbox
156 inputName="warningTitle" [(ngModel)]="customizations.warningTitle" 134 inputName="autoplay" [(ngModel)]="customizations.autoplay"
157 i18n-labelText labelText="Display privacy warning" 135 i18n-labelText labelText="Autoplay"
158 ></my-peertube-checkbox> 136 ></my-peertube-checkbox>
159 </div> 137 </div>
160 138
161 <div class="form-group"> 139 <div class="form-group">
162 <my-peertube-checkbox 140 <my-peertube-checkbox
163 inputName="controls" [(ngModel)]="customizations.controls" 141 inputName="muted" [(ngModel)]="customizations.muted"
164 i18n-labelText labelText="Display player controls" 142 i18n-labelText labelText="Muted"
165 ></my-peertube-checkbox> 143 ></my-peertube-checkbox>
144 </div>
145
146 <div class="form-group">
147 <my-peertube-checkbox
148 inputName="loop" [(ngModel)]="customizations.loop"
149 i18n-labelText labelText="Loop"
150 ></my-peertube-checkbox>
151 </div>
166 </div> 152 </div>
167 </ng-container> 153
154 <ng-container *ngIf="isInEmbedTab()">
155 <div class="form-group">
156 <my-peertube-checkbox
157 inputName="title" [(ngModel)]="customizations.title"
158 i18n-labelText labelText="Display video title"
159 ></my-peertube-checkbox>
160 </div>
161
162 <div class="form-group">
163 <my-peertube-checkbox
164 inputName="warningTitle" [(ngModel)]="customizations.warningTitle"
165 i18n-labelText labelText="Display privacy warning"
166 ></my-peertube-checkbox>
167 </div>
168
169 <div class="form-group">
170 <my-peertube-checkbox
171 inputName="controls" [(ngModel)]="customizations.controls"
172 i18n-labelText labelText="Display player controls"
173 ></my-peertube-checkbox>
174 </div>
175 </ng-container>
176 </div>
168 </div> 177 </div>
169 </div> 178 </div>
170 </div> 179 </div>
diff --git a/client/src/app/videos/+video-watch/modal/video-share.component.scss b/client/src/app/videos/+video-watch/modal/video-share.component.scss
index c48abf9e0..8b5952da6 100644
--- a/client/src/app/videos/+video-watch/modal/video-share.component.scss
+++ b/client/src/app/videos/+video-watch/modal/video-share.component.scss
@@ -1,6 +1,18 @@
1@import '_mixins'; 1@import '_mixins';
2@import '_variables'; 2@import '_variables';
3 3
4my-input-readonly-copy {
5 width: 100%;
6}
7
8.title-page.title-page-single {
9 margin-top: 0;
10}
11
12.playlist {
13 margin-bottom: 50px;
14}
15
4.peertube-select-container { 16.peertube-select-container {
5 @include peertube-select-container(200px); 17 @include peertube-select-container(200px);
6} 18}
@@ -25,10 +37,6 @@
25 margin-top: 20px; 37 margin-top: 20px;
26} 38}
27 39
28input.readonly {
29 font-size: 15px;
30}
31
32.filters { 40.filters {
33 margin-top: 30px; 41 margin-top: 30px;
34 padding-top: 30px; 42 padding-top: 30px;
diff --git a/client/src/app/videos/+video-watch/modal/video-share.component.ts b/client/src/app/videos/+video-watch/modal/video-share.component.ts
index f45afccfb..a9a7a0eab 100644
--- a/client/src/app/videos/+video-watch/modal/video-share.component.ts
+++ b/client/src/app/videos/+video-watch/modal/video-share.component.ts
@@ -5,6 +5,7 @@ import { buildVideoEmbed, buildVideoLink } from '../../../../assets/player/utils
5import { I18n } from '@ngx-translate/i18n-polyfill' 5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { NgbModal, NgbTabChangeEvent } from '@ng-bootstrap/ng-bootstrap' 6import { NgbModal, NgbTabChangeEvent } from '@ng-bootstrap/ng-bootstrap'
7import { VideoCaption } from '@shared/models' 7import { VideoCaption } from '@shared/models'
8import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
8 9
9type Customizations = { 10type Customizations = {
10 startAtCheckbox: boolean 11 startAtCheckbox: boolean
@@ -34,18 +35,16 @@ export class VideoShareComponent {
34 35
35 @Input() video: VideoDetails = null 36 @Input() video: VideoDetails = null
36 @Input() videoCaptions: VideoCaption[] = [] 37 @Input() videoCaptions: VideoCaption[] = []
38 @Input() playlist: VideoPlaylist = null
37 39
38 activeId: 'url' | 'qrcode' | 'embed' 40 activeId: 'url' | 'qrcode' | 'embed'
39 customizations: Customizations 41 customizations: Customizations
40 isAdvancedCustomizationCollapsed = true 42 isAdvancedCustomizationCollapsed = true
43 includeVideoInPlaylist = false
41 44
42 private currentVideoTimestamp: number 45 private currentVideoTimestamp: number
43 46
44 constructor ( 47 constructor (private modalService: NgbModal) { }
45 private modalService: NgbModal,
46 private notifier: Notifier,
47 private i18n: I18n
48 ) { }
49 48
50 show (currentVideoTimestamp?: number) { 49 show (currentVideoTimestamp?: number) {
51 this.currentVideoTimestamp = currentVideoTimestamp 50 this.currentVideoTimestamp = currentVideoTimestamp
@@ -86,17 +85,22 @@ export class VideoShareComponent {
86 } 85 }
87 86
88 getVideoUrl () { 87 getVideoUrl () {
89 const options = this.getOptions() 88 const baseUrl = window.location.origin + '/videos/watch/' + this.video.uuid
89 const options = this.getOptions(baseUrl)
90 90
91 return buildVideoLink(options) 91 return buildVideoLink(options)
92 } 92 }
93 93
94 notSecure () { 94 getPlaylistUrl () {
95 return window.location.protocol === 'http:' 95 const base = window.location.origin + '/videos/watch/playlist/' + this.playlist.uuid
96
97 if (!this.includeVideoInPlaylist) return base
98
99 return base + '?videoId=' + this.video.uuid
96 } 100 }
97 101
98 activateCopiedMessage () { 102 notSecure () {
99 this.notifier.success(this.i18n('Copied')) 103 return window.location.protocol === 'http:'
100 } 104 }
101 105
102 onTabChange (event: NgbTabChangeEvent) { 106 onTabChange (event: NgbTabChangeEvent) {
@@ -107,6 +111,10 @@ export class VideoShareComponent {
107 return this.activeId === 'embed' 111 return this.activeId === 'embed'
108 } 112 }
109 113
114 hasPlaylist () {
115 return !!this.playlist
116 }
117
110 private getOptions (baseUrl?: string) { 118 private getOptions (baseUrl?: string) {
111 return { 119 return {
112 baseUrl, 120 baseUrl,
diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html
index 38ad4e948..5b2e91bc5 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.html
+++ b/client/src/app/videos/+video-watch/video-watch.component.html
@@ -224,5 +224,5 @@
224 224
225<ng-container *ngIf="video !== null"> 225<ng-container *ngIf="video !== null">
226 <my-video-support #videoSupportModal [video]="video"></my-video-support> 226 <my-video-support #videoSupportModal [video]="video"></my-video-support>
227 <my-video-share #videoShareModal [video]="video" [videoCaptions]="videoCaptions"></my-video-share> 227 <my-video-share #videoShareModal [video]="video" [videoCaptions]="videoCaptions" [playlist]="playlist"></my-video-share>
228</ng-container> 228</ng-container>
diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts
index 777abb568..629634985 100644
--- a/client/src/assets/player/utils.ts
+++ b/client/src/assets/player/utils.ts
@@ -51,6 +51,8 @@ function buildVideoLink (options: {
51 : window.location.origin + window.location.pathname.replace('/embed/', '/watch/') 51 : window.location.origin + window.location.pathname.replace('/embed/', '/watch/')
52 52
53 const params = new URLSearchParams(window.location.search) 53 const params = new URLSearchParams(window.location.search)
54 // Remove this unused parameter when we are on a playlist page
55 params.delete('videoId')
54 56
55 if (options.startTime) { 57 if (options.startTime) {
56 const startTimeInt = Math.floor(options.startTime) 58 const startTimeInt = Math.floor(options.startTime)