aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/shared')
-rw-r--r--client/src/app/shared/shared-share-modal/index.ts3
-rw-r--r--client/src/app/shared/shared-share-modal/shared-share-modal.module.ts27
-rw-r--r--client/src/app/shared/shared-share-modal/video-share.component.html232
-rw-r--r--client/src/app/shared/shared-share-modal/video-share.component.scss79
-rw-r--r--client/src/app/shared/shared-share-modal/video-share.component.ts148
5 files changed, 489 insertions, 0 deletions
diff --git a/client/src/app/shared/shared-share-modal/index.ts b/client/src/app/shared/shared-share-modal/index.ts
new file mode 100644
index 000000000..e13c08acc
--- /dev/null
+++ b/client/src/app/shared/shared-share-modal/index.ts
@@ -0,0 +1,3 @@
1export * from './video-share.component'
2
3export * from './shared-share-modal.module'
diff --git a/client/src/app/shared/shared-share-modal/shared-share-modal.module.ts b/client/src/app/shared/shared-share-modal/shared-share-modal.module.ts
new file mode 100644
index 000000000..e269eecac
--- /dev/null
+++ b/client/src/app/shared/shared-share-modal/shared-share-modal.module.ts
@@ -0,0 +1,27 @@
1import { QRCodeModule } from 'angularx-qrcode'
2import { NgModule } from '@angular/core'
3import { SharedFormModule } from '../shared-forms'
4import { SharedGlobalIconModule } from '../shared-icons'
5import { SharedMainModule } from '../shared-main/shared-main.module'
6import { VideoShareComponent } from './video-share.component'
7
8@NgModule({
9 imports: [
10 QRCodeModule,
11
12 SharedMainModule,
13 SharedFormModule,
14 SharedGlobalIconModule
15 ],
16
17 declarations: [
18 VideoShareComponent
19 ],
20
21 exports: [
22 VideoShareComponent
23 ],
24
25 providers: [ ]
26})
27export class SharedShareModal { }
diff --git a/client/src/app/shared/shared-share-modal/video-share.component.html b/client/src/app/shared/shared-share-modal/video-share.component.html
new file mode 100644
index 000000000..4174458b5
--- /dev/null
+++ b/client/src/app/shared/shared-share-modal/video-share.component.html
@@ -0,0 +1,232 @@
1<ng-template #modal let-hide="close">
2 <div class="modal-header">
3 <h4 i18n class="modal-title">Share</h4>
4 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
5 </div>
6
7
8 <div class="modal-body">
9
10 <div class="playlist" *ngIf="playlist">
11 <div class="title-page title-page-single" i18n *ngIf="video">Share the playlist</div>
12
13 <div ngbNav #nav="ngbNav" class="nav-tabs" [(activeId)]="activePlaylistId">
14
15 <ng-container ngbNavItem="url">
16 <a ngbNavLink i18n>URL</a>
17
18 <ng-template ngbNavContent>
19 <div class="nav-content">
20
21 <my-input-readonly-copy [value]="getPlaylistUrl()"></my-input-readonly-copy>
22 </div>
23 </ng-template>
24 </ng-container>
25
26 <ng-container ngbNavItem="qrcode">
27 <a ngbNavLink i18n>QR-Code</a>
28
29 <ng-template ngbNavContent>
30 <div class="nav-content">
31 <qrcode [qrdata]="getPlaylistUrl()" [size]="256" level="Q"></qrcode>
32 </div>
33 </ng-template>
34 </ng-container>
35
36 <ng-container ngbNavItem="embed">
37 <a ngbNavLink i18n>Embed</a>
38
39 <ng-template ngbNavContent>
40 <div class="nav-content">
41 <my-input-readonly-copy [value]="getPlaylistIframeCode()"></my-input-readonly-copy>
42
43 <div i18n *ngIf="notSecure()" class="alert alert-warning">
44 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).
45 </div>
46 </div>
47 </ng-template>
48 </ng-container>
49
50 </div>
51
52 <div [ngbNavOutlet]="nav"></div>
53
54 <div class="filters">
55
56 <div class="form-group" *ngIf="video">
57 <my-peertube-checkbox inputName="includeVideoInPlaylist" [(ngModel)]="includeVideoInPlaylist" i18n-labelText
58 labelText="Share the playlist at this video position"></my-peertube-checkbox>
59 </div>
60
61 </div>
62 </div>
63
64
65 <div class="video" *ngIf="video">
66 <div class="title-page title-page-single" *ngIf="playlist" i18n>Share the video</div>
67
68 <div ngbNav #nav="ngbNav" class="nav-tabs" [(activeId)]="activeVideoId">
69
70 <ng-container ngbNavItem="url">
71 <a ngbNavLink i18n>URL</a>
72
73 <ng-template ngbNavContent>
74 <div class="nav-content">
75 <my-input-readonly-copy [value]="getVideoUrl()"></my-input-readonly-copy>
76 </div>
77 </ng-template>
78 </ng-container>
79
80 <ng-container ngbNavItem="qrcode">
81 <a ngbNavLink i18n>QR-Code</a>
82
83 <ng-template ngbNavContent>
84 <div class="nav-content">
85 <qrcode [qrdata]="getVideoUrl()" [size]="256" level="Q"></qrcode>
86 </div>
87 </ng-template>
88 </ng-container>
89
90 <ng-container ngbNavItem="embed">
91 <a ngbNavLink i18n>Embed</a>
92
93 <ng-template ngbNavContent>
94 <div class="nav-content">
95 <my-input-readonly-copy [value]="getVideoIframeCode()"></my-input-readonly-copy>
96
97 <div i18n *ngIf="notSecure()" class="alert alert-warning">
98 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).
99 </div>
100 </div>
101 </ng-template>
102 </ng-container>
103
104 </div>
105
106 <div [ngbNavOutlet]="nav"></div>
107
108 <div class="filters">
109 <div>
110 <div class="form-group start-at">
111 <my-peertube-checkbox
112 inputName="startAt" [(ngModel)]="customizations.startAtCheckbox"
113 i18n-labelText labelText="Start at"
114 ></my-peertube-checkbox>
115
116 <my-timestamp-input
117 [timestamp]="customizations.startAt"
118 [maxTimestamp]="video.duration"
119 [disabled]="!customizations.startAtCheckbox"
120 [(ngModel)]="customizations.startAt"
121 >
122 </my-timestamp-input>
123 </div>
124
125 <div *ngIf="videoCaptions.length !== 0" class="form-group video-caption-block">
126 <my-peertube-checkbox
127 inputName="subtitleCheckbox" [(ngModel)]="customizations.subtitleCheckbox"
128 i18n-labelText labelText="Auto select subtitle"
129 ></my-peertube-checkbox>
130
131 <div class="peertube-select-container" [ngClass]="{ disabled: !customizations.subtitleCheckbox }">
132 <select [(ngModel)]="customizations.subtitle" [disabled]="!customizations.subtitleCheckbox">
133 <option *ngFor="let caption of videoCaptions" [value]="caption.language.id">{{ caption.language.label }}</option>
134 </select>
135 </div>
136 </div>
137 </div>
138
139 <div class="advanced-filters collapse-transition" [ngbCollapse]="isAdvancedCustomizationCollapsed">
140 <div>
141 <div class="form-group stop-at">
142 <my-peertube-checkbox
143 inputName="stopAt" [(ngModel)]="customizations.stopAtCheckbox"
144 i18n-labelText labelText="Stop at"
145 ></my-peertube-checkbox>
146
147 <my-timestamp-input
148 [timestamp]="customizations.stopAt"
149 [maxTimestamp]="video.duration"
150 [disabled]="!customizations.stopAtCheckbox"
151 [(ngModel)]="customizations.stopAt"
152 >
153 </my-timestamp-input>
154 </div>
155
156 <div class="form-group">
157 <my-peertube-checkbox
158 inputName="autoplay" [(ngModel)]="customizations.autoplay"
159 i18n-labelText labelText="Autoplay"
160 ></my-peertube-checkbox>
161 </div>
162
163 <div class="form-group">
164 <my-peertube-checkbox
165 inputName="muted" [(ngModel)]="customizations.muted"
166 i18n-labelText labelText="Muted"
167 ></my-peertube-checkbox>
168 </div>
169
170 <div class="form-group">
171 <my-peertube-checkbox
172 inputName="loop" [(ngModel)]="customizations.loop"
173 i18n-labelText labelText="Loop"
174 ></my-peertube-checkbox>
175 </div>
176 </div>
177
178 <ng-container *ngIf="isVideoInEmbedTab()">
179 <div class="form-group">
180 <my-peertube-checkbox
181 inputName="title" [(ngModel)]="customizations.title"
182 i18n-labelText labelText="Display video title"
183 ></my-peertube-checkbox>
184 </div>
185
186 <div class="form-group">
187 <my-peertube-checkbox
188 inputName="warningTitle" [(ngModel)]="customizations.warningTitle"
189 i18n-labelText labelText="Display privacy warning"
190 ></my-peertube-checkbox>
191 </div>
192
193 <div class="form-group">
194 <my-peertube-checkbox
195 inputName="controls" [(ngModel)]="customizations.controls"
196 i18n-labelText labelText="Display player controls"
197 ></my-peertube-checkbox>
198 </div>
199
200 <div class="form-group">
201 <my-peertube-checkbox
202 inputName="controls" [(ngModel)]="customizations.peertubeLink"
203 i18n-labelText labelText="Display PeerTube button link"
204 ></my-peertube-checkbox>
205 </div>
206 </ng-container>
207 </div>
208
209 <div (click)="isAdvancedCustomizationCollapsed = !isAdvancedCustomizationCollapsed" role="button" class="advanced-filters-button"
210 [attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic">
211
212 <ng-container *ngIf="isAdvancedCustomizationCollapsed">
213 <span class="glyphicon glyphicon-menu-down"></span>
214
215 <ng-container i18n>
216 More customization
217 </ng-container>
218 </ng-container>
219
220 <ng-container *ngIf="!isAdvancedCustomizationCollapsed">
221 <span class="glyphicon glyphicon-menu-up"></span>
222
223 <ng-container i18n>
224 Less customization
225 </ng-container>
226 </ng-container>
227 </div>
228 </div>
229 </div>
230 </div>
231
232</ng-template>
diff --git a/client/src/app/shared/shared-share-modal/video-share.component.scss b/client/src/app/shared/shared-share-modal/video-share.component.scss
new file mode 100644
index 000000000..091d4dc3b
--- /dev/null
+++ b/client/src/app/shared/shared-share-modal/video-share.component.scss
@@ -0,0 +1,79 @@
1@import '_mixins';
2@import '_variables';
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
16.peertube-select-container {
17 @include peertube-select-container(200px);
18}
19
20.qr-code-group {
21 text-align: center;
22}
23
24.nav-content {
25 margin-top: 30px;
26 display: flex;
27 justify-content: center;
28 align-items: center;
29 flex-direction: column;
30}
31
32.alert {
33 margin-top: 20px;
34}
35
36.filters {
37 margin-top: 30px;
38
39 .advanced-filters-button {
40 display: flex;
41 justify-content: center;
42 align-items: center;
43 margin-top: 20px;
44 font-size: 16px;
45 font-weight: $font-semibold;
46 cursor: pointer;
47
48 .glyphicon {
49 margin-right: 5px;
50 }
51 }
52
53 .form-group {
54 margin-bottom: 0;
55 height: 34px;
56 display: flex;
57 align-items: center;
58 }
59
60 .video-caption-block {
61 display: flex;
62 align-items: center;
63
64 .peertube-select-container {
65 margin-left: 10px;
66 }
67 }
68
69 .start-at,
70 .stop-at {
71 width: 300px;
72 display: flex;
73 align-items: center;
74
75 my-timestamp-input {
76 margin-left: 10px;
77 }
78 }
79}
diff --git a/client/src/app/shared/shared-share-modal/video-share.component.ts b/client/src/app/shared/shared-share-modal/video-share.component.ts
new file mode 100644
index 000000000..8d8e8a3a5
--- /dev/null
+++ b/client/src/app/shared/shared-share-modal/video-share.component.ts
@@ -0,0 +1,148 @@
1import { Component, ElementRef, Input, ViewChild } from '@angular/core'
2import { VideoDetails } from '@app/shared/shared-main'
3import { VideoPlaylist } from '@app/shared/shared-video-playlist'
4import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
5import { VideoCaption } from '@shared/models'
6import { buildPlaylistLink, buildVideoLink, buildVideoOrPlaylistEmbed } from '../../../assets/player/utils'
7
8type Customizations = {
9 startAtCheckbox: boolean
10 startAt: number
11
12 stopAtCheckbox: boolean
13 stopAt: number
14
15 subtitleCheckbox: boolean
16 subtitle: string
17
18 loop: boolean
19 autoplay: boolean
20 muted: boolean
21 title: boolean
22 warningTitle: boolean
23 controls: boolean
24 peertubeLink: boolean
25}
26
27type TabId = 'url' | 'qrcode' | 'embed'
28
29@Component({
30 selector: 'my-video-share',
31 templateUrl: './video-share.component.html',
32 styleUrls: [ './video-share.component.scss' ]
33})
34export class VideoShareComponent {
35 @ViewChild('modal', { static: true }) modal: ElementRef
36
37 @Input() video: VideoDetails = null
38 @Input() videoCaptions: VideoCaption[] = []
39 @Input() playlist: VideoPlaylist = null
40
41 activeVideoId: TabId = 'url'
42 activePlaylistId: TabId = 'url'
43
44 customizations: Customizations
45 isAdvancedCustomizationCollapsed = true
46 includeVideoInPlaylist = false
47
48 private playlistPosition: number = null
49
50 constructor (private modalService: NgbModal) { }
51
52 show (currentVideoTimestamp?: number, currentPlaylistPosition?: number) {
53 let subtitle: string
54 if (this.videoCaptions && this.videoCaptions.length !== 0) {
55 subtitle = this.videoCaptions[0].language.id
56 }
57
58 this.customizations = {
59 startAtCheckbox: false,
60 startAt: currentVideoTimestamp ? Math.floor(currentVideoTimestamp) : 0,
61
62 stopAtCheckbox: false,
63 stopAt: this.video?.duration,
64
65 subtitleCheckbox: false,
66 subtitle,
67
68 loop: false,
69 autoplay: false,
70 muted: false,
71
72 // Embed options
73 title: true,
74 warningTitle: true,
75 controls: true,
76 peertubeLink: true
77 }
78
79 this.playlistPosition = currentPlaylistPosition
80
81 this.modalService.open(this.modal, { centered: true })
82 }
83
84 getVideoIframeCode () {
85 const options = this.getVideoOptions(this.video.embedUrl)
86
87 const embedUrl = buildVideoLink(options)
88 return buildVideoOrPlaylistEmbed(embedUrl)
89 }
90
91 getPlaylistIframeCode () {
92 const options = this.getPlaylistOptions(this.playlist.embedUrl)
93
94 const embedUrl = buildPlaylistLink(options)
95 return buildVideoOrPlaylistEmbed(embedUrl)
96 }
97
98 getVideoUrl () {
99 const baseUrl = window.location.origin + '/videos/watch/' + this.video.uuid
100 const options = this.getVideoOptions(baseUrl)
101
102 return buildVideoLink(options)
103 }
104
105 getPlaylistUrl () {
106 const base = window.location.origin + '/videos/watch/playlist/' + this.playlist.uuid
107
108 if (!this.includeVideoInPlaylist) return base
109
110 return base + '?videoId=' + this.video.uuid
111 }
112
113 notSecure () {
114 return window.location.protocol === 'http:'
115 }
116
117 isVideoInEmbedTab () {
118 return this.activeVideoId === 'embed'
119 }
120
121 private getPlaylistOptions (baseUrl?: string) {
122 return {
123 baseUrl,
124
125 playlistPosition: this.playlistPosition || undefined
126 }
127 }
128
129 private getVideoOptions (baseUrl?: string) {
130 return {
131 baseUrl,
132
133 startTime: this.customizations.startAtCheckbox ? this.customizations.startAt : undefined,
134 stopTime: this.customizations.stopAtCheckbox ? this.customizations.stopAt : undefined,
135
136 subtitle: this.customizations.subtitleCheckbox ? this.customizations.subtitle : undefined,
137
138 loop: this.customizations.loop,
139 autoplay: this.customizations.autoplay,
140 muted: this.customizations.muted,
141
142 title: this.customizations.title,
143 warningTitle: this.customizations.warningTitle,
144 controls: this.customizations.controls,
145 peertubeLink: this.customizations.peertubeLink
146 }
147 }
148}