Skip to content
This repository has been archived by the owner on Feb 29, 2020. It is now read-only.

Commit

Permalink
feat(content): show inline video players for youtube and vimeo
Browse files Browse the repository at this point in the history
  • Loading branch information
rittme committed May 27, 2016
1 parent 9297b76 commit b4d5695
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 2 deletions.
2 changes: 1 addition & 1 deletion bin/generate-html.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const defaults = {
function template(rawOptions) {
const options = Object.assign({}, defaults, rawOptions || {});
const csp = options.csp === "on" ?
"<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; script-src 'self'; img-src http: https: data:; style-src 'self' 'unsafe-inline'\">" :
"<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; script-src 'self'; img-src http: https: data:; style-src 'self' 'unsafe-inline'; child-src 'self' https://*.youtube.com https://*.vimeo.com; frame-src 'self' https://*.youtube.com https://*.vimeo.com\">" :
"";
return `<!doctype html>
<html lang="en-us">
Expand Down
15 changes: 14 additions & 1 deletion content-src/components/ActivityFeed/ActivityFeed.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
const React = require("react");
const {connect} = require("react-redux");
const {justDispatch} = require("selectors/selectors");
const {justDispatch, selectSitePreview} = require("selectors/selectors");
const {actions} = require("common/action-manager");
const SiteIcon = require("components/SiteIcon/SiteIcon");
const MediaPreview = require("components/MediaPreview/MediaPreview");
const DeleteMenu = require("components/DeleteMenu/DeleteMenu");
const {prettyUrl, getRandomFromTimestamp} = require("lib/utils");
const moment = require("moment");
Expand Down Expand Up @@ -63,6 +64,17 @@ const ActivityFeedItem = React.createClass({
dateLabel = moment(date).format("h:mm A");
}

let preview;
if (site.media && site.media.type === "video") {
const previewSite = selectSitePreview(site);
if (previewSite.previewURL) {
const previewProps = {
site: previewSite,
};
preview = (<MediaPreview {...previewProps} />);
}
}

return (<li className={classNames("feed-item", {bookmark: site.bookmarkGuid, fixed: this.state.showContextMenu})}>
<a onClick={this.props.onClick} href={site.url} ref="link">
<span className="star" hidden={!site.bookmarkGuid} />
Expand All @@ -71,6 +83,7 @@ const ActivityFeedItem = React.createClass({
<div className="feed-description">
<h4 className="feed-title" ref="title">{title}</h4>
<span className="feed-url" ref="url">{prettyUrl(site.url)}</span>
{preview}
</div>
<div className="feed-stats">
<div ref="lastVisit">{dateLabel}</div>
Expand Down
1 change: 1 addition & 0 deletions content-src/components/ActivityFeed/ActivityFeed.scss
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@
align-items: baseline;
display: flex;
flex-grow: 1;
flex-wrap: wrap;
overflow: hidden;

.feed-description {
Expand Down
43 changes: 43 additions & 0 deletions content-src/components/MediaPreview/MediaPreview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const React = require("react");
const classNames = require("classnames");

const MediaPreview = React.createClass({
getInitialState() {
return {
showPlayer: false
};
},

getDefaultProps() {
return {
site: {},
height: null,
width: null,
type: "",
thumbnail: null,
previewURL: null
};
},

onPreviewClick(evt) {
evt.preventDefault();
this.setState({showPlayer: true});
},

render() {
const site = this.props.site;

let player;
if (this.state.showPlayer) {
player = (<iframe className="video-preview-player" width="367" height="206" type="text/html" src={site.previewURL} frameborder="0" allowFullScreen></iframe>);
}

const style = {backgroundImage: `url(${site.thumbnail.url})`};
return (<div className={classNames("video-preview", {isPlaying: this.state.showPlayer})}
style={style} onClick={this.onPreviewClick}>
{player}
</div>);
}
});

module.exports = MediaPreview;
39 changes: 39 additions & 0 deletions content-src/components/MediaPreview/MediaPreview.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.video-preview {
position: relative;
background: $black no-repeat center center;
width: 367px;
height: 206px;
border-radius: 6px;
transition: 0.2s opacity;
overflow: hidden;
margin-top: 20px;
}

.video-preview::after {
content: '';
width: 65px;
height: 40px;
background: url('img/playButton@2x.png') no-repeat center center;
background-size: 65px 40px;
display: block;
left: 50%;
top: 50%;
position: absolute;
margin-left: -32px;
margin-top: -20px;
}

.video-preview:hover::after {
background-image: url('img/playButton-hover@2x.png');
}

.video-preview.isPlaying::after {
display: none;
}

.video-preview-player {
width: 100%;
height: 100%;
display: block;
border: 0;
}
36 changes: 36 additions & 0 deletions content-src/lib/getVideoPreview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module.exports = function getVideoPreview(site) {
switch (site.provider_url) {
case "https://www.youtube.com/":
return getVideoURL.youtube(site);
case "https://vimeo.com/":
return getVideoURL.vimeo(site);
default:
return null;
}

};

const getVideoURL = {
youtube: function(site) {
const youtubeIdSearch = /(v=|embed\/)([\w-]{11})\??/;
let idMatches = site.url.match(youtubeIdSearch);
if (!idMatches || idMatches.length < 3) {
return null;
}
let videoId = idMatches[2];

return `https://www.youtube.com/embed/${videoId}?autoplay=1`;
},

vimeo: function(site) {
const vimeoIdSearch = /\/([0-9]+)/;
let idMatches = site.url.match(vimeoIdSearch);
if (!idMatches || idMatches.length < 2) {
return null;
}
let videoId = idMatches[1];

return `https://player.vimeo.com/video/${videoId}?autoplay=1`;
}
};

1 change: 1 addition & 0 deletions content-src/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,4 @@ a {
@import './components/Loader/Loader';
@import './components/LoadMore/LoadMore';
@import './components/ContextMenu/ContextMenu';
@import './components/MediaPreview/MediaPreview';
21 changes: 21 additions & 0 deletions content-src/selectors/selectors.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const {createSelector} = require("reselect");
const dedupe = require("lib/dedupe");
const getBestImage = require("lib/getBestImage");
const getVideoPreview = require("lib/getVideoPreview");
const firstRunData = require("lib/first-run-data");
const {prettyUrl, getBlackOrWhite, toRGBString, getRandomColor} = require("lib/utils");
const urlParse = require("url-parse");
Expand Down Expand Up @@ -132,6 +133,26 @@ const selectSiteIcon = createSelector(
}
);

module.exports.selectSitePreview = createSelector(
site => site,
site => {
const type = site.media ? site.media.type : null;
let thumbnail;
let previewURL;
if (type) {
thumbnail = getBestImage(site.images);
if (type === "video") {
previewURL = getVideoPreview(site);
}
}
return {
type,
thumbnail,
previewURL
};
}
);

// Timeline History view
module.exports.selectHistory = createSelector(
[
Expand Down
Binary file added content-src/static/img/playButton-hover@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content-src/static/img/playButton@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit b4d5695

Please sign in to comment.