From ab67f6e257f6e8f08145a02a7b93550f99641be4 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Sun, 18 Jun 2017 14:42:32 -0400 Subject: PLT-6215 Major post list refactor (#6501) * Major post list refactor * Fix post and thread deletion * Fix preferences not selecting correctly * Fix military time displaying * Fix UP key for editing posts * Fix ESLint error * Various fixes and updates per feedback * Fix for permalink view * Revert to old scrolling method and various fixes * Add floating timestamp, new message indicator, scroll arrows * Update post loading for focus mode and add visibility limit * Fix pinning posts and a react warning * Add loading UI updates from Asaad * Fix refreshing loop * Temporarily bump post visibility limit * Update infinite scrolling * Remove infinite scrolling --- webapp/components/youtube_video/youtube_video.jsx | 243 ++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 webapp/components/youtube_video/youtube_video.jsx (limited to 'webapp/components/youtube_video/youtube_video.jsx') diff --git a/webapp/components/youtube_video/youtube_video.jsx b/webapp/components/youtube_video/youtube_video.jsx new file mode 100644 index 000000000..5151e6576 --- /dev/null +++ b/webapp/components/youtube_video/youtube_video.jsx @@ -0,0 +1,243 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import WebClient from 'client/web_client.jsx'; +import * as Utils from 'utils/utils.jsx'; + +const ytRegex = /(?:http|https):\/\/(?:www\.|m\.)?(?:(?:youtube\.com\/(?:(?:v\/)|(?:(?:watch|embed\/watch)(?:\/|.*v=))|(?:embed\/)|(?:user\/[^/]+\/u\/[0-9]\/)))|(?:youtu\.be\/))([^#&?]*)/; + +import React from 'react'; +import PropTypes from 'prop-types'; + +export default class YoutubeVideo extends React.PureComponent { + static propTypes = { + channelId: PropTypes.string.isRequired, + currentChannelId: PropTypes.string.isRequired, + link: PropTypes.string.isRequired, + show: PropTypes.bool.isRequired + } + + constructor(props) { + super(props); + + this.updateStateFromProps = this.updateStateFromProps.bind(this); + this.handleReceivedMetadata = this.handleReceivedMetadata.bind(this); + this.handleMetadataError = this.handleMetadataError.bind(this); + this.loadWithoutKey = this.loadWithoutKey.bind(this); + + this.play = this.play.bind(this); + this.stop = this.stop.bind(this); + + this.state = { + loaded: false, + failed: false, + playing: false, + title: '' + }; + } + + componentWillMount() { + this.updateStateFromProps(this.props); + } + + componentWillReceiveProps(nextProps) { + this.updateStateFromProps(nextProps); + } + + updateStateFromProps(props) { + const link = props.link; + + const match = link.trim().match(ytRegex); + if (!match || match[1].length !== 11) { + return; + } + + if (props.show === false) { + this.stop(); + } + + if (props.channelId !== props.currentChannelId) { + this.stop(); + } + + this.setState({ + videoId: match[1], + time: this.handleYoutubeTime(link) + }); + } + + handleYoutubeTime(link) { + const timeRegex = /[\\?&]t=([0-9]+h)?([0-9]+m)?([0-9]+s?)/; + + const time = link.match(timeRegex); + if (!time || !time[0]) { + return ''; + } + + const hours = time[1] ? time[1].match(/([0-9]+)h/) : null; + const minutes = time[2] ? time[2].match(/([0-9]+)m/) : null; + const seconds = time[3] ? time[3].match(/([0-9]+)s?/) : null; + + let ticks = 0; + + if (hours && hours[1]) { + ticks += parseInt(hours[1], 10) * 3600; + } + + if (minutes && minutes[1]) { + ticks += parseInt(minutes[1], 10) * 60; + } + + if (seconds && seconds[1]) { + ticks += parseInt(seconds[1], 10); + } + + return '&start=' + ticks.toString(); + } + + componentDidMount() { + const key = global.window.mm_config.GoogleDeveloperKey; + if (key) { + WebClient.getYoutubeVideoInfo(key, this.state.videoId, + this.handleReceivedMetadata, this.handleMetadataError); + } else { + this.loadWithoutKey(); + } + } + + loadWithoutKey() { + this.setState({ + loaded: true, + thumb: 'https://i.ytimg.com/vi/' + this.state.videoId + '/hqdefault.jpg' + }); + } + + handleMetadataError() { + this.setState({ + failed: true, + loaded: true, + title: Utils.localizeMessage('youtube_video.notFound', 'Video not found') + }); + } + + handleReceivedMetadata(data) { + if (!data || !data.items || !data.items.length || !data.items[0].snippet) { + this.setState({ + failed: true, + loaded: true, + title: Utils.localizeMessage('youtube_video.notFound', 'Video not found') + }); + return null; + } + const metadata = data.items[0].snippet; + let thumb = 'https://i.ytimg.com/vi/' + this.state.videoId + '/hqdefault.jpg'; + if (metadata.liveBroadcastContent === 'live') { + thumb = 'https://i.ytimg.com/vi/' + this.state.videoId + '/hqdefault_live.jpg'; + } + + this.setState({ + loaded: true, + receivedYoutubeData: true, + title: metadata.title, + thumb + }); + return null; + } + + play() { + this.setState({playing: true}); + } + + stop() { + this.setState({playing: false}); + } + + render() { + if (!this.state.loaded) { + return ( +
+
+
+ ); + } + + let header; + if (this.state.title) { + header = ( +

+ {'Youtube - '} + + + {this.state.title} + + +

+ ); + } + + let content; + if (this.state.failed) { + content = ( +
+
+
+
+
{Utils.localizeMessage('youtube_video.notFound', 'Video not found')}
+
+
+
+ ); + } else if (this.state.playing) { + content = ( +