From ef61470b5f701a06fa3b9a707ca12b2027eab03a Mon Sep 17 00:00:00 2001 From: vrde Date: Wed, 29 Jul 2015 18:00:49 +0200 Subject: [PATCH 1/5] First iteration --- js/components/ascribe_media/media_player.js | 36 +++++++++++++++------ js/utils/general_utils.js | 12 +++++++ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/js/components/ascribe_media/media_player.js b/js/components/ascribe_media/media_player.js index da89f4a5..46f6550b 100644 --- a/js/components/ascribe_media/media_player.js +++ b/js/components/ascribe_media/media_player.js @@ -3,6 +3,8 @@ import React from 'react'; import Q from 'q'; +import { escapeHTML } from '../../utils/general_utils'; + import InjectInHeadMixin from '../../mixins/inject_in_head_mixin'; import Panel from 'react-bootstrap/lib/Panel'; import ProgressBar from 'react-bootstrap/lib/ProgressBar'; @@ -97,7 +99,7 @@ let Video = React.createClass({ mixins: [InjectInHeadMixin], getInitialState() { - return { ready: false }; + return { ready: false, videoMounted: false }; }, componentDidMount() { @@ -108,24 +110,38 @@ let Video = React.createClass({ }, componentDidUpdate() { - if (this.state.ready && !this.state.videojs) { - window.videojs(React.findDOMNode(this.refs.video)); + if (this.state.ready && !this.state.videoMounted) { + window.videojs('#mainvideo'); + this.setState({videoMounted: true}); } }, + componentWillUnmount() { + window.videojs('#mainvideo').dispose(); + }, + ready() { - this.setState({ready: true, videojs: false}); + this.setState({ready: true, videoMounted: false}); + }, + + prepareVideoHTML() { + let sources = this.props.extraData.map((data) => ''); + let html = [ + '']; + return html.join('\n'); + }, + + shouldComponentUpdate(nextProps, nextState) { + return nextState.videoMounted === false; }, render() { if (this.state.ready) { return ( - +
); } else { return ( diff --git a/js/utils/general_utils.js b/js/utils/general_utils.js index 00f1da2c..4dfda21d 100644 --- a/js/utils/general_utils.js +++ b/js/utils/general_utils.js @@ -166,3 +166,15 @@ function _mergeOptions(obj1, obj2) { } return obj3; } + +/** + * Escape HTML in a string so it can be injected safely using + * React's `dangerouslySetInnerHTML` + * + * @param s the string to be sanitized + * + * Taken from: http://stackoverflow.com/a/17546215/597097 + */ +export function escapeHTML(s) { + return document.createElement('div').appendChild(document.createTextNode(s)).parentNode.innerHTML; +} From 8f52d8bf877da39098539f2035daa969bcc99fa3 Mon Sep 17 00:00:00 2001 From: vrde Date: Wed, 29 Jul 2015 18:19:04 +0200 Subject: [PATCH 2/5] Comment the solution --- js/components/ascribe_media/media_player.js | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/js/components/ascribe_media/media_player.js b/js/components/ascribe_media/media_player.js index 46f6550b..1803a40f 100644 --- a/js/components/ascribe_media/media_player.js +++ b/js/components/ascribe_media/media_player.js @@ -88,7 +88,28 @@ let Audio = React.createClass({ } }); + let Video = React.createClass({ + /** + * The solution here is a bit convoluted. + * ReactJS is responsible for DOM manipulation but VideoJS updates the DOM + * to instal itself to display the video. + * + * What we do is the following: + * 1) set `state.ready = false` + * 2) render the cover using the `` component (because ready is false) + * 3) on `componentDidMount`, we load the external `css` and `js` resources using + * the `InjectInHeadMixin`, attaching a function to `Promise.then` to change + * `state.ready` to true + * 4) when the promise is succesfully resolved, we change `state.ready` triggering + * a re-render + * 5) the new render calls `prepareVideoHTML` to get the raw HTML of the video tag + * (that will be later processed and expanded by VideoJS) + * 6) `componentDidUpdate` is called after `render`, setting `state.videoMounted` to true, + * to avoid re-installing the VideoJS library + * 7) to close the lifecycle, `componentWillUnmount` is called removing VideoJS from the DOM. + */ + propTypes: { preview: React.PropTypes.string.isRequired, url: React.PropTypes.string.isRequired, @@ -112,7 +133,9 @@ let Video = React.createClass({ componentDidUpdate() { if (this.state.ready && !this.state.videoMounted) { window.videojs('#mainvideo'); + /* eslint-disable */ this.setState({videoMounted: true}); + /* eslint-enable*/ } }, From 91078bfdd94491cf0dd6853a5f5b530f426b5281 Mon Sep 17 00:00:00 2001 From: vrde Date: Wed, 29 Jul 2015 18:24:54 +0200 Subject: [PATCH 3/5] Fix comment --- js/components/ascribe_media/media_player.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/components/ascribe_media/media_player.js b/js/components/ascribe_media/media_player.js index 1803a40f..ad53b61f 100644 --- a/js/components/ascribe_media/media_player.js +++ b/js/components/ascribe_media/media_player.js @@ -93,7 +93,8 @@ let Video = React.createClass({ /** * The solution here is a bit convoluted. * ReactJS is responsible for DOM manipulation but VideoJS updates the DOM - * to instal itself to display the video. + * to install itself to display the video, therefore ReactJS complains that we are + * changing the DOM under its feet. * * What we do is the following: * 1) set `state.ready = false` From 950e63b6d010fca7e90f42f28d9552f972774992 Mon Sep 17 00:00:00 2001 From: diminator Date: Thu, 30 Jul 2015 14:04:39 +0200 Subject: [PATCH 4/5] content copy for hash_locally --- js/components/settings_container.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/js/components/settings_container.js b/js/components/settings_container.js index c414f10f..381cd1c4 100644 --- a/js/components/settings_container.js +++ b/js/components/settings_container.js @@ -117,8 +117,7 @@ let AccountSettings = React.createClass({ - {' ' + getLangText('Enable hash option for slow connections. ' + - 'Computes and uploads a hash of the work instead.')} + {' ' + getLangText('Enable hash option, e.g. slow connections or to keep piece private')} From 84703779a92e397a227503b471b79794e4a314c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Fri, 31 Jul 2015 13:44:57 +0200 Subject: [PATCH 5/5] rename prize endpoint to prizes --- .gitignore | 1 + .../whitelabel/prize/constants/api_urls.js | 14 +++++++------- .../prize/constants/application_prize_constants.js | 9 +++++++++ js/components/whitelabel/prize/routes.js | 2 +- 4 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 js/components/whitelabel/prize/constants/application_prize_constants.js diff --git a/.gitignore b/.gitignore index b39d7830..8378467f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ lib-cov *.gz *.sublime-project *.sublime-workspace +webapp-dependencies.txt pids logs diff --git a/js/components/whitelabel/prize/constants/api_urls.js b/js/components/whitelabel/prize/constants/api_urls.js index e868d403..178dfdfa 100644 --- a/js/components/whitelabel/prize/constants/api_urls.js +++ b/js/components/whitelabel/prize/constants/api_urls.js @@ -1,15 +1,15 @@ 'use strict'; -import AppConstants from '../../../../constants/application_constants'; +import AppPrizeConstants from './application_prize_constants'; function getApiUrls(subdomain) { return { - 'pieces_list': AppConstants.apiEndpoint + 'prize/' + subdomain + '/pieces/', - 'users_login': AppConstants.apiEndpoint + 'prize/' + subdomain + '/users/login/', - 'users_signup': AppConstants.apiEndpoint + 'prize/' + subdomain + '/users/', - 'user': AppConstants.apiEndpoint + 'prize/' + subdomain + '/users/', - 'piece_submit_to_prize': AppConstants.apiEndpoint + 'prize/' + subdomain + '/pieces/${piece_id}/submit/', - 'piece': AppConstants.apiEndpoint + 'prize/' + subdomain + '/pieces/${piece_id}/' + 'pieces_list': AppPrizeConstants.prizeApiEndpoint + subdomain + '/pieces/', + 'users_login': AppPrizeConstants.prizeApiEndpoint + subdomain + '/users/login/', + 'users_signup': AppPrizeConstants.prizeApiEndpoint + subdomain + '/users/', + 'user': AppPrizeConstants.prizeApiEndpoint + subdomain + '/users/', + 'piece_submit_to_prize': AppPrizeConstants.prizeApiEndpoint + subdomain + '/pieces/${piece_id}/submit/', + 'piece': AppPrizeConstants.prizeApiEndpoint + subdomain + '/pieces/${piece_id}/' }; } diff --git a/js/components/whitelabel/prize/constants/application_prize_constants.js b/js/components/whitelabel/prize/constants/application_prize_constants.js new file mode 100644 index 00000000..6026193b --- /dev/null +++ b/js/components/whitelabel/prize/constants/application_prize_constants.js @@ -0,0 +1,9 @@ +'use strict'; + +import AppConstants from '../../../../constants/application_constants'; + +let constants = { + prizeApiEndpoint: AppConstants.apiEndpoint + 'prizes/' +}; + +export default constants; \ No newline at end of file diff --git a/js/components/whitelabel/prize/routes.js b/js/components/whitelabel/prize/routes.js index ac490c4e..9d4e12e0 100644 --- a/js/components/whitelabel/prize/routes.js +++ b/js/components/whitelabel/prize/routes.js @@ -21,7 +21,7 @@ let Route = Router.Route; let baseUrl = AppConstants.baseUrl; -function getRoutes(commonRoutes) { +function getRoutes() { return (