PLT 783 Fix Previews for removed YouTube videos (#3073)
* PLT-783 Fix Previews for removed YouTube videos render incorrectly and throw a 404 * Updating video poster for video not found (#3072)
Этот коммит содержится в:
коммит произвёл
Christopher Speller
родитель
2a137e97c4
Коммит
ed2e37394e
@@ -1,13 +1,13 @@
|
|||||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
import $ from 'jquery';
|
|
||||||
import ChannelStore from 'stores/channel_store.jsx';
|
import ChannelStore from 'stores/channel_store.jsx';
|
||||||
|
import WebClient from 'utils/web_client.jsx';
|
||||||
|
import * as Utils from 'utils/utils.jsx';
|
||||||
|
|
||||||
const ytRegex = /(?:http|https):\/\/(?:www\.)?(?:(?:youtube\.com\/(?:(?:v\/)|(\/u\/\w\/)|(?:(?:watch|embed\/watch)(?:\/|.*v=))|(?:embed\/)|(?:user\/[^\/]+\/u\/[0-9]\/)))|(?:youtu\.be\/))([^#&\?]*)/;
|
const ytRegex = /(?:http|https):\/\/(?:www\.)?(?:(?:youtube\.com\/(?:(?:v\/)|(\/u\/\w\/)|(?:(?:watch|embed\/watch)(?:\/|.*v=))|(?:embed\/)|(?:user\/[^\/]+\/u\/[0-9]\/)))|(?:youtu\.be\/))([^#&\?]*)/;
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Link} from 'react-router';
|
|
||||||
|
|
||||||
export default class YoutubeVideo extends React.Component {
|
export default class YoutubeVideo extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -15,12 +15,15 @@ export default class YoutubeVideo extends React.Component {
|
|||||||
|
|
||||||
this.updateStateFromProps = this.updateStateFromProps.bind(this);
|
this.updateStateFromProps = this.updateStateFromProps.bind(this);
|
||||||
this.handleReceivedMetadata = this.handleReceivedMetadata.bind(this);
|
this.handleReceivedMetadata = this.handleReceivedMetadata.bind(this);
|
||||||
|
this.handleMetadataError = this.handleMetadataError.bind(this);
|
||||||
|
|
||||||
this.play = this.play.bind(this);
|
this.play = this.play.bind(this);
|
||||||
this.stop = this.stop.bind(this);
|
this.stop = this.stop.bind(this);
|
||||||
this.stopOnChannelChange = this.stopOnChannelChange.bind(this);
|
this.stopOnChannelChange = this.stopOnChannelChange.bind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
loaded: false,
|
||||||
|
failed: global.window.mm_config.GoogleDeveloperKey === '',
|
||||||
playing: false,
|
playing: false,
|
||||||
title: ''
|
title: ''
|
||||||
};
|
};
|
||||||
@@ -78,23 +81,33 @@ export default class YoutubeVideo extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (global.window.mm_config.GoogleDeveloperKey) {
|
const key = global.window.mm_config.GoogleDeveloperKey;
|
||||||
$.ajax({
|
if (key) {
|
||||||
async: true,
|
WebClient.getYoutubeVideoInfo(key, this.state.videoId,
|
||||||
url: 'https://www.googleapis.com/youtube/v3/videos',
|
this.handleReceivedMetadata, this.handleMetadataError);
|
||||||
type: 'GET',
|
|
||||||
data: {part: 'snippet', id: this.state.videoId, key: global.window.mm_config.GoogleDeveloperKey},
|
|
||||||
success: this.handleReceivedMetadata
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleMetadataError() {
|
||||||
|
this.setState({
|
||||||
|
failed: true,
|
||||||
|
loaded: true,
|
||||||
|
title: Utils.localizeMessage('youtube_video.notFound', 'Video not found')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
handleReceivedMetadata(data) {
|
handleReceivedMetadata(data) {
|
||||||
if (!data.items.length || !data.items[0].snippet) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
var metadata = data.items[0].snippet;
|
var metadata = data.items[0].snippet;
|
||||||
this.setState({
|
this.setState({
|
||||||
|
loaded: true,
|
||||||
receivedYoutubeData: true,
|
receivedYoutubeData: true,
|
||||||
title: metadata.title
|
title: metadata.title
|
||||||
});
|
});
|
||||||
@@ -120,13 +133,28 @@ export default class YoutubeVideo extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (!this.state.loaded) {
|
||||||
|
return <div className='video-loading'/>;
|
||||||
|
}
|
||||||
|
|
||||||
let header = 'Youtube';
|
let header = 'Youtube';
|
||||||
if (this.state.title) {
|
if (this.state.title) {
|
||||||
header = header + ' - ';
|
header = header + ' - ';
|
||||||
}
|
}
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
if (this.state.playing) {
|
if (this.state.failed) {
|
||||||
|
content = (
|
||||||
|
<div>
|
||||||
|
<div className='video-thumbnail__container'>
|
||||||
|
<div className='video-thumbnail__error'>
|
||||||
|
<div><i className='fa fa-warning fa-2x'/></div>
|
||||||
|
<div>{Utils.localizeMessage('youtube_video.notFound', 'Video not found')}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (this.state.playing) {
|
||||||
content = (
|
content = (
|
||||||
<iframe
|
<iframe
|
||||||
src={'https://www.youtube.com/embed/' + this.state.videoId + '?autoplay=1&autohide=1&border=0&wmode=opaque&fs=1&enablejsapi=1' + this.state.time}
|
src={'https://www.youtube.com/embed/' + this.state.videoId + '?autoplay=1&autohide=1&border=0&wmode=opaque&fs=1&enablejsapi=1' + this.state.time}
|
||||||
@@ -157,7 +185,15 @@ export default class YoutubeVideo extends React.Component {
|
|||||||
<div>
|
<div>
|
||||||
<h4>
|
<h4>
|
||||||
<span className='video-type'>{header}</span>
|
<span className='video-type'>{header}</span>
|
||||||
<span className='video-title'><Link to={this.props.link}>{this.state.title}</Link></span>
|
<span className='video-title'>
|
||||||
|
<a
|
||||||
|
href={this.props.link}
|
||||||
|
target='blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
>
|
||||||
|
{this.state.title}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
</h4>
|
</h4>
|
||||||
<div
|
<div
|
||||||
className='video-div embed-responsive-item'
|
className='video-div embed-responsive-item'
|
||||||
|
|||||||
@@ -1455,5 +1455,6 @@
|
|||||||
"web.footer.privacy": "Privacy",
|
"web.footer.privacy": "Privacy",
|
||||||
"web.footer.terms": "Terms",
|
"web.footer.terms": "Terms",
|
||||||
"web.header.back": "Back",
|
"web.header.back": "Back",
|
||||||
"web.root.singup_info": "All team communication in one place, searchable and accessible anywhere"
|
"web.root.singup_info": "All team communication in one place, searchable and accessible anywhere",
|
||||||
|
"youtube_video.notFound": "Video not found"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1454,5 +1454,6 @@
|
|||||||
"web.footer.privacy": "Privacidad",
|
"web.footer.privacy": "Privacidad",
|
||||||
"web.footer.terms": "Términos",
|
"web.footer.terms": "Términos",
|
||||||
"web.header.back": "Atrás",
|
"web.header.back": "Atrás",
|
||||||
"web.root.singup_info": "Todas las comunicaciones del equipo en un sólo lugar, con búsquedas y accesible desde cualquier parte"
|
"web.root.singup_info": "Todas las comunicaciones del equipo en un sólo lugar, con búsquedas y accesible desde cualquier parte",
|
||||||
|
"youtube_video.notFound": "Video no encontrado"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,22 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.video-thumbnail__error {
|
||||||
|
height: 100%;
|
||||||
|
line-height: 2;
|
||||||
|
padding: 110px 0;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
@include opacity(.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.block {
|
.block {
|
||||||
background-color: alpha-color($black, .5);
|
background-color: alpha-color($black, .5);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
@@ -67,3 +83,7 @@
|
|||||||
height: 500px;
|
height: 500px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.video-loading {
|
||||||
|
height: 360px;
|
||||||
|
}
|
||||||
@@ -682,7 +682,7 @@ export function applyTheme(theme) {
|
|||||||
changeCss('.app__body .post-list__arrows', 'fill:' + changeOpacity(theme.centerChannelColor, 0.3), 1);
|
changeCss('.app__body .post-list__arrows', 'fill:' + changeOpacity(theme.centerChannelColor, 0.3), 1);
|
||||||
changeCss('.app__body .sidebar--left, .app__body .sidebar--right .sidebar--right__header, .app__body .suggestion-list__content .command', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
|
changeCss('.app__body .sidebar--left, .app__body .sidebar--right .sidebar--right__header, .app__body .suggestion-list__content .command', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
|
||||||
changeCss('.app__body .app__content, .app__body .post-create__container .post-create-body .btn-file, .app__body .post-create__container .post-create-footer .msg-typing, .app__body .suggestion-list__content .command, .app__body .modal .modal-content, .app__body .dropdown-menu, .app__body .popover, .app__body .mentions__name, .app__body .tip-overlay', 'color:' + theme.centerChannelColor, 1);
|
changeCss('.app__body .app__content, .app__body .post-create__container .post-create-body .btn-file, .app__body .post-create__container .post-create-footer .msg-typing, .app__body .suggestion-list__content .command, .app__body .modal .modal-content, .app__body .dropdown-menu, .app__body .popover, .app__body .mentions__name, .app__body .tip-overlay', 'color:' + theme.centerChannelColor, 1);
|
||||||
changeCss('.app__body #archive-link-home', 'background:' + changeOpacity(theme.centerChannelColor, 0.15), 1);
|
changeCss('.app__body #archive-link-home, .video-div .video-thumbnail__error', 'background:' + changeOpacity(theme.centerChannelColor, 0.15), 1);
|
||||||
changeCss('.app__body #post-create', 'color:' + theme.centerChannelColor, 2);
|
changeCss('.app__body #post-create', 'color:' + theme.centerChannelColor, 2);
|
||||||
changeCss('.app__body .mentions--top, .app__body .suggestion-list', 'box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 3);
|
changeCss('.app__body .mentions--top, .app__body .suggestion-list', 'box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 3);
|
||||||
changeCss('.app__body .mentions--top, .app__body .suggestion-list', '-webkit-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 2);
|
changeCss('.app__body .mentions--top, .app__body .suggestion-list', '-webkit-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 2);
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import TeamStore from '../stores/team_store.jsx';
|
|||||||
import BrowserStore from '../stores/browser_store.jsx';
|
import BrowserStore from '../stores/browser_store.jsx';
|
||||||
import * as GlobalActions from 'action_creators/global_actions.jsx';
|
import * as GlobalActions from 'action_creators/global_actions.jsx';
|
||||||
|
|
||||||
|
import request from 'superagent';
|
||||||
|
|
||||||
const HTTP_UNAUTHORIZED = 401;
|
const HTTP_UNAUTHORIZED = 401;
|
||||||
|
|
||||||
class WebClientClass extends Client {
|
class WebClientClass extends Client {
|
||||||
@@ -82,6 +84,17 @@ class WebClientClass extends Client {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getYoutubeVideoInfo(googleKey, videoId, success, error) {
|
||||||
|
request.get('https://www.googleapis.com/youtube/v3/videos').
|
||||||
|
query({part: 'snippet', id: videoId, key: googleKey}).
|
||||||
|
end((err, res) => {
|
||||||
|
if (err) {
|
||||||
|
return error(err);
|
||||||
|
}
|
||||||
|
return success(res.body);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var WebClient = new WebClientClass();
|
var WebClient = new WebClientClass();
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user