Add params to share modal

This commit is contained in:
Chocobozzz 2019-06-12 12:40:24 +02:00
parent 011e1e6b37
commit 2f4c784a92
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
15 changed files with 505 additions and 210 deletions

View File

@ -20,7 +20,7 @@
</div>
</div>
<div class="results-filter" [ngbCollapse]="isSearchFilterCollapsed">
<div class="results-filter collapse-transition" [ngbCollapse]="isSearchFilterCollapsed">
<my-search-filters [advancedSearch]="advancedSearch" (filtered)="onFiltered()"></my-search-filters>
</div>
</div>

View File

@ -35,18 +35,6 @@
}
}
}
.results-filter {
// Animation when we show/hide the filters
transition: max-height 0.3s;
display: block !important;
overflow: hidden !important;
max-height: 0;
&.show {
max-height: 1500px;
}
}
}
.entry {

View File

@ -4,14 +4,11 @@ import { SearchComponent } from '@app/search/search.component'
import { SearchService } from '@app/search/search.service'
import { SearchRoutingModule } from '@app/search/search-routing.module'
import { SearchFiltersComponent } from '@app/search/search-filters.component'
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'
@NgModule({
imports: [
SearchRoutingModule,
SharedModule,
NgbCollapseModule
SharedModule
],
declarations: [

View File

@ -53,7 +53,14 @@ import { VideoCaptionService } from '@app/shared/video-caption'
import { PeertubeCheckboxComponent } from '@app/shared/forms/peertube-checkbox.component'
import { VideoImportService } from '@app/shared/video-import/video-import.service'
import { ActionDropdownComponent } from '@app/shared/buttons/action-dropdown.component'
import { NgbDropdownModule, NgbModalModule, NgbPopoverModule, NgbTabsetModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
import {
NgbCollapseModule,
NgbDropdownModule,
NgbModalModule,
NgbPopoverModule,
NgbTabsetModule,
NgbTooltipModule
} from '@ng-bootstrap/ng-bootstrap'
import { RemoteSubscribeComponent, SubscribeButtonComponent, UserSubscriptionService } from '@app/shared/user-subscription'
import { InstanceFeaturesTableComponent } from '@app/shared/instance/instance-features-table.component'
import { OverviewService } from '@app/shared/overview'
@ -100,6 +107,7 @@ import { FollowService } from '@app/shared/instance/follow.service'
NgbPopoverModule,
NgbTabsetModule,
NgbTooltipModule,
NgbCollapseModule,
ClipboardModule,
@ -170,6 +178,7 @@ import { FollowService } from '@app/shared/instance/follow.service'
NgbPopoverModule,
NgbTabsetModule,
NgbTooltipModule,
NgbCollapseModule,
ClipboardModule,

View File

@ -5,53 +5,167 @@
</div>
<div class="modal-body">
<ngb-tabset class="root-tabset bootstrap" (tabChange)="onTabChange($event)">
<div class="start-at">
<my-peertube-checkbox
inputName="startAt" [(ngModel)]="startAtCheckbox"
i18n-labelText labelText="Start at"
></my-peertube-checkbox>
<ngb-tab i18n-title title="URL" id="url">
<ng-template ngbTabContent>
<my-timestamp-input
[timestamp]="currentVideoTimestamp"
[maxTimestamp]="video.duration"
[disabled]="!startAtCheckbox"
[(ngModel)]="currentVideoTimestamp"
>
</my-timestamp-input>
</div>
<div class="tab-content">
<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>
<div class="form-group">
<label i18n>URL</label>
<div class="input-group input-group-sm">
<input #urlInput (click)="urlInput.select()" type="text" class="form-control input-sm 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>
</ng-template>
</ngb-tab>
<ngb-tab i18n-title title="QR-Code" id="qrcode">
<ng-template ngbTabContent>
<div class="tab-content">
<ngx-qrcode qrc-element-type="url" [qrc-value]="getVideoUrl()" qrc-errorCorrectionLevel="Q"></ngx-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
inputName="startAt" [(ngModel)]="customizations.startAtCheckbox"
i18n-labelText labelText="Start at"
></my-peertube-checkbox>
<my-timestamp-input
[timestamp]="customizations.startAt"
[maxTimestamp]="video.duration"
[disabled]="!customizations.startAtCheckbox"
[(ngModel)]="customizations.startAt"
>
</my-timestamp-input>
</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 class="form-group qr-code-group">
<label i18n>QR-Code</label>
<ngx-qrcode qrc-element-type="url" [qrc-value]="getVideoUrl()" qrc-errorCorrectionLevel="Q"></ngx-qrcode>
</div>
<div (click)="isAdvancedCustomizationCollapsed = !isAdvancedCustomizationCollapsed" role="button" class="advanced-filters-button"
[attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic">
<div class="form-group">
<label i18n>Embed</label>
<div class="input-group input-group-sm">
<input #shareInput (click)="shareInput.select()" type="text" class="form-control input-sm 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>
<ng-container *ngIf="isAdvancedCustomizationCollapsed">
<span class="glyphicon glyphicon-menu-down"></span>
<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>
<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 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>
<ng-container *ngIf="isInEmbedTab()">
<div class="form-group">
<my-peertube-checkbox
inputName="title" [(ngModel)]="customizations.title"
i18n-labelText labelText="Display video title"
></my-peertube-checkbox>
</div>
<div class="form-group">
<my-peertube-checkbox
inputName="warningTitle" [(ngModel)]="customizations.warningTitle"
i18n-labelText labelText="Display privacy warning"
></my-peertube-checkbox>
</div>
<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>

View File

@ -1,5 +1,9 @@
@import '~bootstrap/scss/functions';
@import '~bootstrap/scss/variables';
@import '_mixins';
@import '_variables';
.peertube-select-container {
@include peertube-select-container(200px);
}
.action-button-cancel {
margin-right: 0 !important;
@ -9,13 +13,65 @@
text-align: center;
}
.start-at {
.tab-content {
margin-top: 30px;
display: flex;
justify-content: center;
margin-top: 10px;
align-items: center;
flex-direction: column;
}
my-timestamp-input {
margin-left: 10px;
.alert {
margin-top: 20px;
}
input.readonly {
font-size: 15px;
}
.filters {
margin-top: 30px;
padding-top: 30px;
border-top: 1px solid $separator-border-color;
.advanced-filters-button {
display: flex;
justify-content: center;
align-items: center;
margin-top: 30px;
font-size: 16px;
font-weight: $font-semibold;
cursor: pointer;
.glyphicon {
margin-right: 5px;
}
}
.form-group {
margin-bottom: 0;
height: 34px;
display: flex;
align-items: center;
}
.video-caption-block {
display: flex;
align-items: center;
.peertube-select-container {
margin-left: 10px;
}
}
.start-at,
.stop-at {
width: 300px;
display: flex;
align-items: center;
my-timestamp-input {
margin-left: 10px;
}
}
}

View File

@ -3,8 +3,26 @@ import { Notifier } from '@app/core'
import { VideoDetails } from '../../../shared/video/video-details.model'
import { buildVideoEmbed, buildVideoLink } from '../../../../assets/player/utils'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { durationToString } from '@app/shared/misc/utils'
import { NgbModal, NgbTabChangeEvent } from '@ng-bootstrap/ng-bootstrap'
import { VideoCaption } from '@shared/models'
type Customizations = {
startAtCheckbox: boolean
startAt: number
stopAtCheckbox: boolean
stopAt: number
subtitleCheckbox: boolean
subtitle: string
loop: boolean
autoplay: boolean
muted: boolean
title: boolean
warningTitle: boolean
controls: boolean
}
@Component({
selector: 'my-video-share',
@ -15,9 +33,13 @@ export class VideoShareComponent {
@ViewChild('modal') modal: ElementRef
@Input() video: VideoDetails = null
@Input() videoCaptions: VideoCaption[] = []
currentVideoTimestamp: number
startAtCheckbox = false
activeId: 'url' | 'qrcode' | 'embed'
customizations: Customizations
isAdvancedCustomizationCollapsed = true
private currentVideoTimestamp: number
constructor (
private modalService: NgbModal,
@ -26,19 +48,47 @@ export class VideoShareComponent {
) { }
show (currentVideoTimestamp?: number) {
this.currentVideoTimestamp = currentVideoTimestamp ? Math.floor(currentVideoTimestamp) : 0
this.currentVideoTimestamp = currentVideoTimestamp
let subtitle: string
if (this.videoCaptions.length !== 0) {
subtitle = this.videoCaptions[0].language.id
}
this.customizations = {
startAtCheckbox: false,
startAt: currentVideoTimestamp ? Math.floor(currentVideoTimestamp) : 0,
stopAtCheckbox: false,
stopAt: this.video.duration,
subtitleCheckbox: false,
subtitle,
loop: false,
autoplay: false,
muted: false,
// Embed options
title: true,
warningTitle: true,
controls: true
}
this.modalService.open(this.modal)
}
getVideoIframeCode () {
const embedUrl = buildVideoLink(this.getVideoTimestampIfEnabled(), this.video.embedUrl)
const options = this.getOptions(this.video.embedUrl)
const embedUrl = buildVideoLink(options)
return buildVideoEmbed(embedUrl)
}
getVideoUrl () {
return buildVideoLink(this.getVideoTimestampIfEnabled())
const options = this.getOptions()
return buildVideoLink(options)
}
notSecure () {
@ -49,9 +99,30 @@ export class VideoShareComponent {
this.notifier.success(this.i18n('Copied'))
}
private getVideoTimestampIfEnabled () {
if (this.startAtCheckbox === true) return this.currentVideoTimestamp
onTabChange (event: NgbTabChangeEvent) {
this.activeId = event.nextId as any
}
return undefined
isInEmbedTab () {
return this.activeId === 'embed'
}
private getOptions (baseUrl?: string) {
return {
baseUrl,
startTime: this.customizations.startAtCheckbox ? this.customizations.startAt : undefined,
stopTime: this.customizations.stopAtCheckbox ? this.customizations.stopAt : undefined,
subtitle: this.customizations.subtitleCheckbox ? this.customizations.subtitle : undefined,
loop: this.customizations.loop,
autoplay: this.customizations.autoplay,
muted: this.customizations.muted,
title: this.customizations.title,
warningTitle: this.customizations.warningTitle,
controls: this.customizations.controls
}
}
}

View File

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

View File

@ -50,9 +50,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
playerElement: HTMLVideoElement
theaterEnabled = false
userRating: UserVideoRateType = null
video: VideoDetails = null
descriptionLoading = false
video: VideoDetails = null
videoCaptions: VideoCaption[] = []
playlist: VideoPlaylist = null
completeDescriptionShown = false
@ -339,6 +341,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
urlOptions: CustomizationOptions & { playerMode: PlayerMode }
) {
this.video = video
this.videoCaptions = videoCaptions
// Re init attributes
this.descriptionLoading = false

View File

@ -446,7 +446,7 @@ export class PeertubePlayerManager {
label: player.localize('Copy the video URL at the current time'),
listener: function () {
const player = this as videojs.Player
copyToClipboard(buildVideoLink(player.currentTime()))
copyToClipboard(buildVideoLink({ startTime: player.currentTime() }))
}
},
{

View File

@ -27,18 +27,55 @@ function isMobile () {
return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
}
function buildVideoLink (time?: number, url?: string) {
if (!url) url = window.location.origin + window.location.pathname.replace('/embed/', '/watch/')
function buildVideoLink (options: {
baseUrl?: string,
if (time) {
const timeInt = Math.floor(time)
startTime?: number,
stopTime?: number,
const params = new URLSearchParams(window.location.search)
params.set('start', secondsToTime(timeInt))
subtitle?: string,
return url + '?' + params.toString()
loop?: boolean,
autoplay?: boolean,
muted?: boolean,
// Embed options
title?: boolean,
warningTitle?: boolean,
controls?: boolean
} = {}) {
const { baseUrl } = options
const url = baseUrl
? baseUrl
: window.location.origin + window.location.pathname.replace('/embed/', '/watch/')
const params = new URLSearchParams(window.location.search)
if (options.startTime) {
const startTimeInt = Math.floor(options.startTime)
params.set('start', secondsToTime(startTimeInt))
}
if (options.stopTime) {
const stopTimeInt = Math.floor(options.stopTime)
params.set('stop', secondsToTime(stopTimeInt))
}
if (options.subtitle) params.set('subtitle', options.subtitle)
if (options.loop === true) params.set('loop', '1')
if (options.autoplay === true) params.set('autoplay', '1')
if (options.muted === true) params.set('muted', '1')
if (options.title === false) params.set('title', '0')
if (options.warningTitle === false) params.set('warningTitle', '0')
if (options.controls === false) params.set('controls', '0')
let hasParams = false
params.forEach(() => hasParams = true)
if (hasParams) return url + '?' + params.toString()
return url
}

View File

@ -16,7 +16,7 @@ class PeerTubeLinkButton extends Button {
}
updateHref () {
this.el().setAttribute('href', buildVideoLink(this.player().currentTime()))
this.el().setAttribute('href', buildVideoLink({ startTime: this.player().currentTime() }))
}
handleClick () {

View File

@ -12,6 +12,7 @@ $assets-path: '../assets/';
@import './player/index';
@import './loading-bar';
@import './bootstrap';
@import './primeng-custom';
[hidden] {
@ -181,128 +182,11 @@ label {
font-weight: bold;
}
// Thanks https://gist.github.com/alexandrevicenzi/680147013e902a4eaa5d
.glyphicon-refresh-animate {
animation: spin .7s infinite linear;
}
@keyframes spin {
from { transform: scale(1) rotate(0deg);}
to { transform: scale(1) rotate(360deg);}
}
// Bootstrap customizations
.dropdown-menu {
border-radius: 3px;
box-shadow: 0 3px 6px;
font-size: 15px;
.dropdown-item {
padding: 3px 15px;
&:active {
color: #000 !important;
}
}
button {
@include disable-default-a-behaviour;
}
a {
@include disable-default-a-behaviour;
color: #000 !important;
}
}
.modal {
.modal-content {
background-color: var(--mainBackgroundColor);
}
.modal-header {
border-bottom: none;
margin-bottom: 5px;
.modal-title {
font-size: 20px;
font-weight: $font-semibold;
}
my-global-icon {
@include icon(24px);
position: relative;
top: 3px;
float: right;
margin: 0;
padding: 0;
opacity: 1;
}
}
.inputs {
margin-bottom: 0;
text-align: right;
.action-button-cancel {
@include peertube-button;
@include grey-button;
display: inline-block;
margin-right: 10px;
}
.action-button-submit {
@include peertube-button;
@include orange-button;
}
}
}
// Nav customizations
.nav .nav-link {
display: flex !important;
align-items: center;
height: 30px !important;
padding: 10px 15px !important;
}
.nav.nav-pills {
font-size: 16px !important;
.nav-link.active {
font-weight: $font-semibold !important;
}
a {
@include disable-default-a-behaviour;
color: var(--mainForegroundColor);
}
}
ngb-tabset.bootstrap {
.nav-link {
&, & a {
@include disable-default-a-behaviour;
color: var(--mainForegroundColor) !important;
}
}
.nav-pills .nav-link.active {
color: #000 !important;
}
}
.nav-tabs .nav-link.active {
background-color: var(--mainBackgroundColor) !important;
border-bottom: none;
}
.orange-button {
@include peertube-button;
@include orange-button;

138
client/src/sass/bootstrap.scss vendored Normal file
View File

@ -0,0 +1,138 @@
$icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
@import '_bootstrap';
@import '_variables';
@import '_mixins';
// Thanks https://gist.github.com/alexandrevicenzi/680147013e902a4eaa5d
.glyphicon-refresh-animate {
animation: spin .7s infinite linear;
}
@keyframes spin {
from { transform: scale(1) rotate(0deg);}
to { transform: scale(1) rotate(360deg);}
}
.dropdown-menu {
border-radius: 3px;
box-shadow: 0 3px 6px;
font-size: 15px;
.dropdown-item {
padding: 3px 15px;
&:active {
color: #000 !important;
}
}
button {
@include disable-default-a-behaviour;
}
a {
@include disable-default-a-behaviour;
color: #000 !important;
}
}
.modal {
.modal-content {
background-color: var(--mainBackgroundColor);
}
.modal-header {
border-bottom: none;
margin-bottom: 5px;
.modal-title {
font-size: 20px;
font-weight: $font-semibold;
}
my-global-icon {
@include icon(24px);
position: relative;
top: 3px;
float: right;
margin: 0;
padding: 0;
opacity: 1;
}
}
.inputs {
margin-bottom: 0;
text-align: right;
.action-button-cancel {
@include peertube-button;
@include grey-button;
display: inline-block;
margin-right: 10px;
}
.action-button-submit {
@include peertube-button;
@include orange-button;
}
}
}
// Nav customizations
.nav .nav-link {
display: flex !important;
align-items: center;
height: 30px !important;
padding: 10px 15px !important;
}
.nav.nav-pills {
font-size: 16px !important;
.nav-link.active {
font-weight: $font-semibold !important;
}
a {
@include disable-default-a-behaviour;
color: var(--mainForegroundColor);
}
}
ngb-tabset.bootstrap {
.nav-link {
&, & a {
@include disable-default-a-behaviour;
color: var(--mainForegroundColor) !important;
}
}
.nav-pills .nav-link.active {
color: #000 !important;
}
}
.nav-tabs .nav-link.active {
background-color: var(--mainBackgroundColor) !important;
border-bottom: none;
}
.collapse-transition {
// Animation when we show/hide the filters
transition: max-height 0.3s;
display: block !important;
overflow: hidden !important;
max-height: 0;
&.show {
max-height: 1500px;
}
}

View File

@ -235,6 +235,14 @@
position: relative;
font-size: 15px;
&.disabled {
background-color: #E5E5E5;
select {
cursor: default;
}
}
@media screen and (max-width: $width) {
width: 100%;
}
@ -282,16 +290,6 @@
}
}
@mixin peertube-select-disabled-container ($width) {
@include peertube-select-container($width);
background-color: #E5E5E5;
select {
cursor: default;
}
}
// Thanks: https://codepen.io/triss90/pen/XNEdRe/
@mixin peertube-radio-container {
input[type="radio"] {