From b0777fe326176aebdd196ff974be2481d2d48ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 25 Jan 2016 13:33:20 +0100 Subject: [PATCH 01/25] Bump react infrastructure dependencies --- js/app.js | 3 ++- package.json | 21 +++++++++------------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/js/app.js b/js/app.js index 5ae013e5..bb32ef50 100644 --- a/js/app.js +++ b/js/app.js @@ -4,6 +4,7 @@ import 'classlist-polyfill'; import 'isomorphic-fetch'; import React from 'react'; +import ReactDOM from 'react-dom'; import { Router } from 'react-router'; import AppResolver from './app_resolver'; @@ -70,7 +71,7 @@ const AppGateway = { } }); - React.render(( + ReactDOM.render(( {redirectRoute} {routes} diff --git a/package.json b/package.json index 88d63a78..7c3ffe8a 100644 --- a/package.json +++ b/package.json @@ -15,17 +15,14 @@ "postinstall": "npm run build", "build": "rimraf ./dist && NODE_ENV=production webpack -p", "start": "NODE_ENV=production node server.js", - "build:dev": "rimraf ./build && NODE_ENV=development webpack", "build:extract": "rimraf ./build && NODE_ENV=extract webpack", "clean": "rimraf ./build ./dist", "start:dev": "NODE_ENV=development node server.dev.js", "lint": "eslint ./", "test": "npm run sauce-test", - "sauce-test": "mocha ./test/integration/tests/", "sauce-tunnel": "node ./test/integration/tunnel.js", - "vi-clean": "rm -rf ./gemini-report", "vi-phantom": "phantomjs --webdriver=4444", "vi-update": "gemini update -c ./test/gemini/.gemini.yml", @@ -88,9 +85,8 @@ "express": "^4.13.4", "extract-text-webpack-plugin": "^1.0.1", "file-loader": "^0.8.5", - "history": "1.17.0", "html-webpack-plugin": "^2.19.0", - "invariant": "^2.1.1", + "invariant": "^2.2.1", "isomorphic-fetch": "^2.0.2", "moment": "^2.10.6", "node-sass": "^3.7.0", @@ -98,13 +94,14 @@ "q": "^1.4.1", "query-string": "^3.0.0", "raven-js": "^1.1.19", - "react": "0.13.2", - "react-bootstrap": "0.25.1", - "react-datepicker": "^0.12.0", - "react-router": "1.0.3", - "react-router-bootstrap": "^0.19.0", - "react-star-rating": "~1.3.2", - "react-textarea-autosize": "^2.5.2", + "react": "^15.1.0", + "react-bootstrap": "^0.29.4", + "react-datepicker": "^0.27.0", + "react-dom": "^15.1.0", + "react-router": "^2.4.1", + "react-router-bootstrap": "^0.23.0", + "react-star-rating": "^1.4.2", + "react-textarea-autosize": "^4.0.2", "react-transform-hmr": "^1.0.4", "remove-trailing-slash": "^0.1.0", "resolve-url-loader": "^1.4.3", From 960e16475b00b9073f0cf744c25d28ad4a961139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 25 Jan 2016 14:15:36 +0100 Subject: [PATCH 02/25] Fix all occurrences of react/addons usage Fixed also: cloneWithProps deprecation --- .../ascribe_buttons/acl_button_list.js | 4 ++-- .../ascribe_buttons/form_submit_button.js | 5 ++-- js/components/ascribe_forms/form.js | 3 +-- .../input_contract_agreement_checkbox.js | 5 ++-- js/components/ascribe_forms/property.js | 5 +--- .../slides_container.js | 6 ++--- js/components/ascribe_table/table.js | 5 ++-- .../react_s3_fine_uploader.js | 23 ++++++++++--------- js/stores/edition_list_store.js | 4 ++-- js/stores/piece_list_store.js | 4 ++-- package.json | 1 + 11 files changed, 32 insertions(+), 33 deletions(-) diff --git a/js/components/ascribe_buttons/acl_button_list.js b/js/components/ascribe_buttons/acl_button_list.js index 75add937..dd22bf50 100644 --- a/js/components/ascribe_buttons/acl_button_list.js +++ b/js/components/ascribe_buttons/acl_button_list.js @@ -1,6 +1,6 @@ 'use strict'; -import React from 'react/addons'; +import React from 'react'; import ConsignButton from './acls/consign_button'; import EmailButton from './acls/email_button'; @@ -61,7 +61,7 @@ let AclButtonList = React.createClass({ const { buttonListSize } = this.state; return React.Children.map(children, (child) => { - return React.addons.cloneWithProps(child, { buttonListSize }); + return React.cloneElement(child, { buttonListSize }); }); }, diff --git a/js/components/ascribe_buttons/form_submit_button.js b/js/components/ascribe_buttons/form_submit_button.js index c0335a4b..100ad1b1 100644 --- a/js/components/ascribe_buttons/form_submit_button.js +++ b/js/components/ascribe_buttons/form_submit_button.js @@ -1,6 +1,7 @@ 'use strict'; -import React from 'react/addons'; +import React from 'react'; +import update from 'react-addons-update'; const { string, object } = React.PropTypes; @@ -30,7 +31,7 @@ const FormSubmitButton = React.createClass({ }, setReadyStateForKey(key, state) { - const readyStates = React.addons.update(this.state.readyStates, { [key]: { $set: state } }); + const readyStates = update(this.state.readyStates, { [key]: { $set: state } }); this.setState({ readyStates }); }, diff --git a/js/components/ascribe_forms/form.js b/js/components/ascribe_forms/form.js index 688d5428..378f024d 100644 --- a/js/components/ascribe_forms/form.js +++ b/js/components/ascribe_forms/form.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import ReactAddons from 'react/addons'; import classNames from 'classnames'; @@ -257,7 +256,7 @@ let Form = React.createClass({ }, renderChildren() { - return ReactAddons.Children.map(this.props.children, (child, i) => { + return React.Children.map(this.props.children, (child, i) => { if (child) { // Since refs will be overwritten by this functions return statement, // we still want to be able to define refs for nested `Form` or `Property` diff --git a/js/components/ascribe_forms/input_contract_agreement_checkbox.js b/js/components/ascribe_forms/input_contract_agreement_checkbox.js index f4d14bd9..1e1411db 100644 --- a/js/components/ascribe_forms/input_contract_agreement_checkbox.js +++ b/js/components/ascribe_forms/input_contract_agreement_checkbox.js @@ -1,6 +1,7 @@ 'use strict'; -import React from 'react/addons'; +import React from 'react'; +import update from 'react-addons-update'; import InputCheckbox from './input_checkbox'; @@ -101,7 +102,7 @@ const InputContractAgreementCheckbox = React.createClass({ // so the parent `Property` is able to get the correct value of this component // when the `Form` queries it. this.setState({ - value: React.addons.update(this.state.value, { + value: update(this.state.value, { terms: { $set: event.target.value } }) }); diff --git a/js/components/ascribe_forms/property.js b/js/components/ascribe_forms/property.js index b8b90400..9a495ef6 100644 --- a/js/components/ascribe_forms/property.js +++ b/js/components/ascribe_forms/property.js @@ -1,14 +1,11 @@ 'use strict'; import React from 'react'; -import ReactAddons from 'react/addons'; import Panel from 'react-bootstrap/lib/Panel'; import AppConstants from '../../constants/application_constants'; -import { mergeOptions } from '../../utils/general_utils'; - const { bool, element, string, oneOfType, func, object, arrayOf } = React.PropTypes; @@ -261,7 +258,7 @@ const Property = React.createClass({ // Input's props should only be cloned and propagated down the tree, // if the component is actually being shown (!== 'expanded === false') if((this.state.expanded && this.props.checkboxLabel) || !this.props.checkboxLabel) { - return ReactAddons.Children.map(this.props.children, (child) => { + return React.Children.map(this.props.children, (child) => { // Since refs will be overriden by this functions return statement, // we still want to be able to define refs for nested `Form` or `Property` // children, which is why we're upfront simply invoking the callback-ref- diff --git a/js/components/ascribe_slides_container/slides_container.js b/js/components/ascribe_slides_container/slides_container.js index 109bbae7..ccaef9cb 100644 --- a/js/components/ascribe_slides_container/slides_container.js +++ b/js/components/ascribe_slides_container/slides_container.js @@ -1,6 +1,6 @@ 'use strict'; -import React from 'react/addons'; +import React from 'react'; import { History, Lifecycle } from 'react-router'; import SlidesContainerBreadcrumbs from './slides_container_breadcrumbs'; @@ -120,7 +120,7 @@ const SlidesContainer = React.createClass({ } }, - // Since we need to give the slides a width, we need to call ReactAddons.addons.cloneWithProps + // Since we need to give the slides a width, we need to call React.cloneElement // Also, a key is nice to have! renderChildren() { const startFrom = parseInt(this.props.location.query.start_from, 10) || -1; @@ -129,7 +129,7 @@ const SlidesContainer = React.createClass({ // since the default parameter of startFrom is -1, we do not need to check // if its actually present in the url bar, as it will just not match if(child && i >= startFrom) { - return React.addons.cloneWithProps(child, { + return React.cloneElement(child, { className: 'ascribe-slide', style: { width: this.state.containerWidth diff --git a/js/components/ascribe_table/table.js b/js/components/ascribe_table/table.js index fa040a0b..207b183b 100644 --- a/js/components/ascribe_table/table.js +++ b/js/components/ascribe_table/table.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import ReactAddons from 'react/addons'; import TableHeader from './table_header'; import { ColumnModel } from './models/table_models'; @@ -20,8 +19,8 @@ let Table = React.createClass({ }, renderChildren() { - return ReactAddons.Children.map(this.props.children, (child, i) => { - return ReactAddons.addons.cloneWithProps(child, { + return React.Children.map(this.props.children, (child, i) => { + return React.cloneElement(child, { columnList: this.props.columnList, columnContent: this.props.itemList[i], key: i diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index 1a3baf38..ed7cfe09 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -1,6 +1,7 @@ 'use strict'; -import React from 'react/addons'; +import React from 'react'; +import update from 'react-addons-update'; // FIXME: remove once using react-components import fineUploader from 'exports?qq!./vendor/s3.fine-uploader'; import Q from 'q'; @@ -536,7 +537,7 @@ const ReactS3FineUploader = React.createClass({ changeSet.status = { $set: status }; - let filesToUpload = React.addons.update(this.state.filesToUpload, { [fileId]: changeSet }); + let filesToUpload = update(this.state.filesToUpload, { [fileId]: changeSet }); this.setState({ filesToUpload }, resolve); }); @@ -547,7 +548,7 @@ const ReactS3FineUploader = React.createClass({ if(fileId < filesToUpload.length) { const changeSet = { $set: url }; - const newFilesToUpload = React.addons.update(filesToUpload, { + const newFilesToUpload = update(filesToUpload, { [fileId]: { thumbnailUrl: changeSet } }); @@ -574,7 +575,7 @@ const ReactS3FineUploader = React.createClass({ completed: false }; - let startedChunks = React.addons.update(this.state.startedChunks, { $set: chunks }); + let startedChunks = update(this.state.startedChunks, { $set: chunks }); this.setState({ startedChunks }); }, @@ -588,7 +589,7 @@ const ReactS3FineUploader = React.createClass({ chunks[chunkKey].responseJson = responseJson; chunks[chunkKey].xhr = xhr; - let startedChunks = React.addons.update(this.state.startedChunks, { $set: chunks }); + let startedChunks = update(this.state.startedChunks, { $set: chunks }); this.setState({ startedChunks }); } @@ -619,7 +620,7 @@ const ReactS3FineUploader = React.createClass({ files[id].status = FileStatus.UPLOAD_SUCCESSFUL; files[id].key = this.state.uploader.getKey(id); - let filesToUpload = React.addons.update(this.state.filesToUpload, { $set: files }); + let filesToUpload = update(this.state.filesToUpload, { $set: files }); this.setState({ filesToUpload }); // Only after the blob has been created server-side, we can make the form submittable. @@ -667,7 +668,7 @@ const ReactS3FineUploader = React.createClass({ if (!this.state.errorState.errorClass) { notificationMessage = errorNotificationMessage; - const errorState = React.addons.update(this.state.errorState, { + const errorState = update(this.state.errorState, { errorClass: { $set: this.getUploadErrorClass({ reason: errorReason, @@ -720,7 +721,7 @@ const ReactS3FineUploader = React.createClass({ }, onProgress(id, name, uploadedBytes, totalBytes) { - let filesToUpload = React.addons.update(this.state.filesToUpload, { + let filesToUpload = update(this.state.filesToUpload, { [id]: { progress: { $set: (uploadedBytes / totalBytes) * 100} } @@ -747,7 +748,7 @@ const ReactS3FineUploader = React.createClass({ return file; }); - let filesToUpload = React.addons.update(this.state.filesToUpload, {$set: updatedFilesToUpload}); + let filesToUpload = update(this.state.filesToUpload, {$set: updatedFilesToUpload}); this.setState({filesToUpload }); } else { @@ -846,7 +847,7 @@ const ReactS3FineUploader = React.createClass({ fileIds.forEach((fileId) => { this.state.uploader.retry(fileId); - filesToUpload = React.addons.update(filesToUpload, { [fileId]: { status: { $set: FileStatus.UPLOADING } } }); + filesToUpload = update(filesToUpload, { [fileId]: { status: { $set: FileStatus.UPLOADING } } }); }); this.setState({ @@ -1041,7 +1042,7 @@ const ReactS3FineUploader = React.createClass({ } // set the new file array - let filesToUpload = React.addons.update(this.state.filesToUpload, { $set: oldAndNewFiles }); + let filesToUpload = update(this.state.filesToUpload, { $set: oldAndNewFiles }); this.setState({ filesToUpload }, () => { // when files have been dropped or selected by a user, we want to propagate that diff --git a/js/stores/edition_list_store.js b/js/stores/edition_list_store.js index 683e40ca..939eb063 100644 --- a/js/stores/edition_list_store.js +++ b/js/stores/edition_list_store.js @@ -1,6 +1,6 @@ 'use strict'; -import React from 'react'; +import update from 'react-addons-update'; import { alt } from '../alt'; import EditionsListActions from '../actions/edition_list_actions'; @@ -30,7 +30,7 @@ class EditionListStore { // if edition already exists, just merge if (pieceEditionList[storeEditionIndex]) { - pieceEditionList[storeEditionIndex] = React.addons.update(pieceEditionList[storeEditionIndex], { $merge: updatedEdition }); + pieceEditionList[storeEditionIndex] = update(pieceEditionList[storeEditionIndex], { $merge: updatedEdition }); } else { // if does not exist, assign pieceEditionList[storeEditionIndex] = updatedEdition; diff --git a/js/stores/piece_list_store.js b/js/stores/piece_list_store.js index 22d35c51..a5cd6b36 100644 --- a/js/stores/piece_list_store.js +++ b/js/stores/piece_list_store.js @@ -1,6 +1,6 @@ 'use strict'; -import React from 'react'; +import update from 'react-addons-update'; import { alt } from '../alt'; import PieceListActions from '../actions/piece_list_actions'; @@ -62,7 +62,7 @@ class PieceListStore { pieceList.forEach((piece, i) => { const oldPiece = this.pieceList[i]; if (oldPiece) { - piece = React.addons.update(piece, { + piece = update(piece, { show: { $set: oldPiece.show } }); } diff --git a/package.json b/package.json index 7c3ffe8a..8cbb69e2 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "query-string": "^3.0.0", "raven-js": "^1.1.19", "react": "^15.1.0", + "react-addons-update": "^15.1.0", "react-bootstrap": "^0.29.4", "react-datepicker": "^0.27.0", "react-dom": "^15.1.0", From 8a5a192d24e033e320154f8020256461a250fe6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 25 Jan 2016 14:50:55 +0100 Subject: [PATCH 03/25] Comply to react-router's history changes --- js/history.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/history.js b/js/history.js index 9fcdd3ff..3c138cf3 100644 --- a/js/history.js +++ b/js/history.js @@ -1,12 +1,12 @@ 'use strict'; -import useBasename from 'history/lib/useBasename'; -import useQueries from 'history/lib/useQueries'; +import { useRouterHistory } from 'react-router'; import createBrowserHistory from 'history/lib/createBrowserHistory'; + import AppConstants from './constants/application_constants'; -const history = useBasename(useQueries(createBrowserHistory))({ +const history = useRouterHistory(createBrowserHistory)({ basename: AppConstants.baseUrl }); From fcaa85a0ed1f6889952ed079e4534c828d4690ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 25 Jan 2016 17:53:28 +0100 Subject: [PATCH 04/25] Replace usage of History mixin with contextTypes --- .../ascribe_detail/edition_action_panel.js | 7 +++--- .../ascribe_detail/edition_container.js | 3 +-- .../ascribe_detail/piece_container.js | 9 +++++--- js/components/ascribe_forms/form_login.js | 4 ---- .../form_send_contract_agreement.js | 7 +++--- js/components/ascribe_forms/form_signup.js | 3 --- js/components/ascribe_routes/proxy_handler.js | 23 ++++++++++++++----- .../slides_container.js | 17 ++++++++++---- js/components/logout_container.js | 12 ---------- js/components/password_reset_container.js | 7 +++--- js/components/piece_list.js | 17 +++++++------- js/components/register_piece.js | 7 +++--- .../cyland_detail/cyland_piece_container.js | 7 +++--- .../components/cyland/cyland_landing.js | 7 +----- .../cyland/cyland_register_piece.js | 7 +++--- .../ikonotv/ikonotv_contract_notifications.js | 9 ++++---- .../ikonotv_detail/ikonotv_piece_container.js | 7 +++--- .../ikonotv/ikonotv_register_piece.js | 9 ++++---- .../market/market_register_piece.js | 7 +++--- 19 files changed, 88 insertions(+), 81 deletions(-) diff --git a/js/components/ascribe_detail/edition_action_panel.js b/js/components/ascribe_detail/edition_action_panel.js index 067a5d13..d50d89bd 100644 --- a/js/components/ascribe_detail/edition_action_panel.js +++ b/js/components/ascribe_detail/edition_action_panel.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import Row from 'react-bootstrap/lib/Row'; import Col from 'react-bootstrap/lib/Col'; @@ -44,7 +43,9 @@ let EditionActionPanel = React.createClass({ handleSuccess: React.PropTypes.func }, - mixins: [History], + contextTypes: { + router: React.PropTypes.object.isRequired + }, getDefaultProps() { return { @@ -77,7 +78,7 @@ let EditionActionPanel = React.createClass({ const notification = new GlobalNotificationModel(response.notification, 'success'); GlobalNotificationActions.appendGlobalNotification(notification); - this.history.push('/collection'); + this.context.router.push('/collection'); }, refreshCollection() { diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index 77ebed48..af938a2f 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import ReactError from '../../mixins/react_error'; import { ResourceNotFoundError } from '../../models/errors'; @@ -34,7 +33,7 @@ let EditionContainer = React.createClass({ params: React.PropTypes.object }, - mixins: [History, ReactError], + mixins: [ReactError], getInitialState() { return EditionStore.getInitialState(); diff --git a/js/components/ascribe_detail/piece_container.js b/js/components/ascribe_detail/piece_container.js index e9daee3c..a7611f03 100644 --- a/js/components/ascribe_detail/piece_container.js +++ b/js/components/ascribe_detail/piece_container.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import Moment from 'moment'; import ReactError from '../../mixins/react_error'; @@ -60,7 +59,11 @@ let PieceContainer = React.createClass({ params: React.PropTypes.object }, - mixins: [History, ReactError], + contextTypes: { + router: React.PropTypes.object.isRequired + }, + + mixins: [ReactError], getDefaultProps() { return { @@ -164,7 +167,7 @@ let PieceContainer = React.createClass({ const notification = new GlobalNotificationModel(response.notification, 'success'); GlobalNotificationActions.appendGlobalNotification(notification); - this.history.push('/collection'); + this.context.router.push('/collection'); }, getCreateEditionsDialog() { diff --git a/js/components/ascribe_forms/form_login.js b/js/components/ascribe_forms/form_login.js index 2e04e450..1efab204 100644 --- a/js/components/ascribe_forms/form_login.js +++ b/js/components/ascribe_forms/form_login.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationActions from '../../actions/global_notification_actions'; @@ -12,7 +11,6 @@ import Form from './form'; import Property from './property'; import ApiUrls from '../../constants/api_urls'; -import AppConstants from '../../constants/application_constants'; import AscribeSpinner from '../ascribe_spinner'; import { getLangText } from '../../utils/lang_utils'; @@ -26,8 +24,6 @@ let LoginForm = React.createClass({ whitelabelName: React.PropTypes.string }, - mixins: [History], - getDefaultProps() { return { headerMessage: getLangText('Enter') + ' ascribe', diff --git a/js/components/ascribe_forms/form_send_contract_agreement.js b/js/components/ascribe_forms/form_send_contract_agreement.js index 2fa66b71..bb6895f5 100644 --- a/js/components/ascribe_forms/form_send_contract_agreement.js +++ b/js/components/ascribe_forms/form_send_contract_agreement.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import ContractListActions from '../../actions/contract_list_actions'; import ContractListStore from '../../stores/contract_list_store'; @@ -25,7 +24,9 @@ let SendContractAgreementForm = React.createClass({ handleSuccess: React.PropTypes.func }, - mixins: [History], + contextTypes: { + router: React.PropTypes.object + }, getInitialState() { return mergeOptions( @@ -57,7 +58,7 @@ let SendContractAgreementForm = React.createClass({ const notification = new GlobalNotificationModel(getLangText('Contract agreement sent'), 'success', 10000); GlobalNotificationActions.appendGlobalNotification(notification); - this.history.push('/collection'); + this.context.router.push('/collection'); }, getFormData() { diff --git a/js/components/ascribe_forms/form_signup.js b/js/components/ascribe_forms/form_signup.js index b52eaf6c..3f62ea04 100644 --- a/js/components/ascribe_forms/form_signup.js +++ b/js/components/ascribe_forms/form_signup.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationActions from '../../actions/global_notification_actions'; @@ -32,8 +31,6 @@ let SignupForm = React.createClass({ whitelabelName: React.PropTypes.string }, - mixins: [History], - getDefaultProps() { return { headerMessage: getLangText('Welcome to ascribe'), diff --git a/js/components/ascribe_routes/proxy_handler.js b/js/components/ascribe_routes/proxy_handler.js index cfe64447..70bb53ee 100644 --- a/js/components/ascribe_routes/proxy_handler.js +++ b/js/components/ascribe_routes/proxy_handler.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { RouteContext } from 'react-router'; import history from '../../history'; import UserStore from '../../stores/user_store'; @@ -46,7 +45,7 @@ export function AuthRedirect({ to, when }) { // wants to redirect the user to a specific route when the user is logged out already } else if (!exprToValidate && when === 'loggedIn' && redirect) { delete query.redirect; - window.setTimeout(() => history.replace({ query, pathname: '/' + redirect })); + window.setTimeout(() => history.replace({ query, pathname: `/${redirect}` })); return true; } else if (!exprToValidate && when === 'loggedOut' && redirectAuthenticated) { @@ -86,12 +85,24 @@ export function ProxyHandler(...redirectFunctions) { whitelabel: React.PropTypes.object, // Provided from router - location: object + location: object, + route: object }, - // We need insert `RouteContext` here in order to be able - // to use the `Lifecycle` widget in further down nested components - mixins: [RouteContext], + contextTypes: { + router: object + }, + + childContextTypes: { + route: object, + router: object + }, + + getChildContext() { + return { + route: this.props.route + }; + }, componentDidMount() { this.evaluateRedirectFunctions(); diff --git a/js/components/ascribe_slides_container/slides_container.js b/js/components/ascribe_slides_container/slides_container.js index ccaef9cb..18ccd44b 100644 --- a/js/components/ascribe_slides_container/slides_container.js +++ b/js/components/ascribe_slides_container/slides_container.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History, Lifecycle } from 'react-router'; import SlidesContainerBreadcrumbs from './slides_container_breadcrumbs'; @@ -21,7 +20,10 @@ const SlidesContainer = React.createClass({ pageExitWarning: string }, - mixins: [History, Lifecycle], + contextTypes: { + route: object.isRequired, + router: object.isRequired + }, getInitialState() { return { @@ -37,6 +39,11 @@ const SlidesContainer = React.createClass({ // Initially, we need to dispatch 'resize' once to render correctly window.dispatchEvent(new Event('resize')); + + // Since react-router 2.0.0, we need to define the `routerWillLeave` + // method ourselves. + const { router, route } = this.context; + router.setRouteLeaveHook(route, this.routerWillLeave); }, componentWillUnmount() { @@ -61,10 +68,10 @@ const SlidesContainer = React.createClass({ }, setSlideNum(nextSlideNum, additionalQueryParams = {}) { - const { location: { pathname } } = this.props; - const query = Object.assign({}, this.props.location.query, additionalQueryParams, { slide_num: nextSlideNum }); + const { location: { pathname, query } } = this.props; + const slideQuery = Object.assign({}, query, additionalQueryParams, { slide_num: nextSlideNum }); - this.history.push({ pathname, query }); + this.context.router.push({ pathname, query: slideQuery }); }, // breadcrumbs are defined as attributes of the slides. diff --git a/js/components/logout_container.js b/js/components/logout_container.js index 5b0e9e54..2b714557 100644 --- a/js/components/logout_container.js +++ b/js/components/logout_container.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import AscribeSpinner from './ascribe_spinner'; @@ -12,17 +11,6 @@ import { setDocumentTitle } from '../utils/dom_utils'; let LogoutContainer = React.createClass({ - propTypes: { - // Provided from AscribeApp - currentUser: React.PropTypes.object, - whitelabel: React.PropTypes.object, - - // Provided from router - location: React.PropTypes.object - }, - - mixins: [History], - componentDidMount() { UserActions.logoutCurrentUser(); }, diff --git a/js/components/password_reset_container.js b/js/components/password_reset_container.js index cc8be9af..87d68249 100644 --- a/js/components/password_reset_container.js +++ b/js/components/password_reset_container.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import Form from './ascribe_forms/form'; import Property from './ascribe_forms/property'; @@ -114,7 +113,9 @@ let PasswordResetForm = React.createClass({ token: React.PropTypes.string }, - mixins: [History], + contextTypes: { + router: React.PropTypes.object.isRequired + }, getFormData() { return { @@ -124,7 +125,7 @@ let PasswordResetForm = React.createClass({ }, handleSuccess() { - this.history.push('/collection'); + this.context.router.push('/collection'); const notification = new GlobalNotificationModel(getLangText('Password successfully updated'), 'success', 10000); GlobalNotificationActions.appendGlobalNotification(notification); diff --git a/js/components/piece_list.js b/js/components/piece_list.js index 62cd6201..7a82c61f 100644 --- a/js/components/piece_list.js +++ b/js/components/piece_list.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import PieceListStore from '../stores/piece_list_store'; import PieceListActions from '../actions/piece_list_actions'; @@ -55,7 +54,9 @@ let PieceList = React.createClass({ location: React.PropTypes.object }, - mixins: [History], + contextTypes: { + router: React.PropTypes.object.isRequired + }, getDefaultProps() { return { @@ -135,12 +136,12 @@ let PieceList = React.createClass({ if (redirectTo && redirectTo.pathname && (typeof shouldRedirect === 'function' && shouldRedirect(unfilteredPieceListCount))) { // FIXME: hack to redirect out of the dispatch cycle - window.setTimeout(() => this.history.push({ + window.setTimeout(() => this.context.router.push({ // Occasionally, the back end also sets query parameters for Onion. // We need to consider this by merging all passed query parameters, as we'll // otherwise end up in a 404 screen - query: Object.assign({}, query, redirectTo.query), - pathname: redirectTo.pathname + pathname: redirectTo.pathname, + query: Object.assign({}, query, redirectTo.query) }), 0); } }, @@ -198,11 +199,11 @@ let PieceList = React.createClass({ const { location: { pathname } } = this.props; this.loadPieceList({ search, page: 1 }); - this.history.push({ pathname, query: { page: 1 } }); + this.context.router.push({ pathname, query: { page: 1 } }); }, applyFilterBy(filterBy) { - const { location: { pathname } } = this.props; + const { pathname } = this.props.location; this.setState({ isFilterDirty: true @@ -228,7 +229,7 @@ let PieceList = React.createClass({ // we have to redirect the user always to page one as it could be that there is no page two // for filtered pieces - this.history.push({ pathname, query: { page: 1 } }); + this.context.router.push({ pathname, query: { page: 1 } }); }, applyOrderBy(orderBy) { diff --git a/js/components/register_piece.js b/js/components/register_piece.js index 04568bb0..d3f75e36 100644 --- a/js/components/register_piece.js +++ b/js/components/register_piece.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import Col from 'react-bootstrap/lib/Col'; import Row from 'react-bootstrap/lib/Row'; @@ -37,7 +36,9 @@ let RegisterPiece = React.createClass( { location: React.PropTypes.object }, - mixins: [History], + contextTypes: { + router: React.PropTypes.object.isRequired + }, getInitialState(){ return PieceListStore.getState(); @@ -65,7 +66,7 @@ let RegisterPiece = React.createClass( { // the piece list up to date PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy }); - this.history.push(`/pieces/${response.piece.id}`); + this.context.router.push(`/pieces/${response.piece.id}`); }, getSpecifyEditions() { diff --git a/js/components/whitelabel/wallet/components/cyland/cyland_detail/cyland_piece_container.js b/js/components/whitelabel/wallet/components/cyland/cyland_detail/cyland_piece_container.js index bd7f2cec..eace7322 100644 --- a/js/components/whitelabel/wallet/components/cyland/cyland_detail/cyland_piece_container.js +++ b/js/components/whitelabel/wallet/components/cyland/cyland_detail/cyland_piece_container.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import EditionListActions from '../../../../../../actions/edition_list_actions'; @@ -40,7 +39,9 @@ let CylandPieceContainer = React.createClass({ params: React.PropTypes.object }, - mixins: [History], + contextTypes: { + router: React.PropTypes.object + }, getInitialState() { return mergeOptions( @@ -90,7 +91,7 @@ let CylandPieceContainer = React.createClass({ const notification = new GlobalNotificationModel(response.notification, 'success'); GlobalNotificationActions.appendGlobalNotification(notification); - this.history.push('/collection'); + this.context.router.push('/collection'); }, render() { diff --git a/js/components/whitelabel/wallet/components/cyland/cyland_landing.js b/js/components/whitelabel/wallet/components/cyland/cyland_landing.js index ca064544..d61837b7 100644 --- a/js/components/whitelabel/wallet/components/cyland/cyland_landing.js +++ b/js/components/whitelabel/wallet/components/cyland/cyland_landing.js @@ -6,7 +6,6 @@ import Button from 'react-bootstrap/lib/Button'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; -import AscribeSpinner from '../../../../ascribe_spinner'; import { getLangText } from '../../../../../utils/lang_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils'; @@ -15,11 +14,7 @@ import { setDocumentTitle } from '../../../../../utils/dom_utils'; let CylandLanding = React.createClass({ propTypes: { // Provided from WalletApp - currentUser: React.PropTypes.object, - whitelabel: React.PropTypes.object.isRequired, - - // Provided from router - location: React.PropTypes.object + whitelabel: React.PropTypes.object.isRequired }, render() { diff --git a/js/components/whitelabel/wallet/components/cyland/cyland_register_piece.js b/js/components/whitelabel/wallet/components/cyland/cyland_register_piece.js index 67d15fb6..56d068e8 100644 --- a/js/components/whitelabel/wallet/components/cyland/cyland_register_piece.js +++ b/js/components/whitelabel/wallet/components/cyland/cyland_register_piece.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import Moment from 'moment'; @@ -44,7 +43,9 @@ let CylandRegisterPiece = React.createClass({ location: React.PropTypes.object }, - mixins: [History], + contextTypes: { + router: React.PropTypes.object.isRequired + }, getInitialState(){ return mergeOptions( @@ -110,7 +111,7 @@ let CylandRegisterPiece = React.createClass({ this.refreshPieceList(); - this.history.push(`/pieces/${this.state.piece.id}`); + this.context.router.push(`/pieces/${this.state.piece.id}`); }, nextSlide(queryParams) { diff --git a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_contract_notifications.js b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_contract_notifications.js index 388cf283..db98eb94 100644 --- a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_contract_notifications.js +++ b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_contract_notifications.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import Button from 'react-bootstrap/lib/Button'; @@ -33,7 +32,9 @@ let IkonotvContractNotifications = React.createClass({ location: React.PropTypes.object }, - mixins: [History], + contextTypes: { + router: React.PropTypes.object.isRequired + }, getInitialState() { return NotificationStore.getState(); @@ -115,7 +116,7 @@ let IkonotvContractNotifications = React.createClass({ NotificationActions.flushContractAgreementListNotifications(); NotificationActions.fetchContractAgreementListNotifications(); - this.history.push('/collection'); + this.context.router.push('/collection'); }, handleDeny() { @@ -129,7 +130,7 @@ let IkonotvContractNotifications = React.createClass({ const notification = new GlobalNotificationModel(getLangText('You have denied the conditions'), 'success', 5000); GlobalNotificationActions.appendGlobalNotification(notification); - this.history.push('/collection'); + this.context.router.push('/collection'); }, getCopyrightAssociationForm(){ diff --git a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_detail/ikonotv_piece_container.js b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_detail/ikonotv_piece_container.js index a1f7bab4..15c28260 100644 --- a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_detail/ikonotv_piece_container.js +++ b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_detail/ikonotv_piece_container.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import EditionListActions from '../../../../../../actions/edition_list_actions'; @@ -41,7 +40,9 @@ let IkonotvPieceContainer = React.createClass({ params: React.PropTypes.object }, - mixins: [History], + contextTypes: { + router: React.PropTypes.object.isRequired + }, getInitialState() { return mergeOptions( @@ -91,7 +92,7 @@ let IkonotvPieceContainer = React.createClass({ const notification = new GlobalNotificationModel(response.notification, 'success'); GlobalNotificationActions.appendGlobalNotification(notification); - this.history.push('/collection'); + this.context.router.push('/collection'); }, render() { diff --git a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_register_piece.js b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_register_piece.js index e7864f7b..5c8e8b37 100644 --- a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_register_piece.js +++ b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_register_piece.js @@ -2,7 +2,6 @@ import React from 'react'; import Moment from 'moment'; -import { History } from 'react-router'; import Col from 'react-bootstrap/lib/Col'; import Row from 'react-bootstrap/lib/Row'; @@ -42,7 +41,9 @@ let IkonotvRegisterPiece = React.createClass({ location: React.PropTypes.object }, - mixins: [History], + contextTypes: { + router: React.PropTypes.object.isRequired + }, getInitialState() { return mergeOptions( @@ -92,7 +93,7 @@ let IkonotvRegisterPiece = React.createClass({ } if (!this.canSubmit()) { - this.history.push('/collection'); + this.context.router.push('/collection'); } else { this.nextSlide({ piece_id: response.piece.id }); } @@ -117,7 +118,7 @@ let IkonotvRegisterPiece = React.createClass({ this.refreshPieceList(); - this.history.push(`/pieces/${this.state.piece.id}`); + this.context.router.push(`/pieces/${this.state.piece.id}`); }, nextSlide(queryParams) { diff --git a/js/components/whitelabel/wallet/components/market/market_register_piece.js b/js/components/whitelabel/wallet/components/market/market_register_piece.js index b3d467f4..cfb22b19 100644 --- a/js/components/whitelabel/wallet/components/market/market_register_piece.js +++ b/js/components/whitelabel/wallet/components/market/market_register_piece.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import { History } from 'react-router'; import Col from 'react-bootstrap/lib/Col'; import Row from 'react-bootstrap/lib/Row'; @@ -33,7 +32,9 @@ let MarketRegisterPiece = React.createClass({ location: React.PropTypes.object }, - mixins: [History], + contextTypes: { + router: React.PropTypes.object.isRequired + }, getInitialState(){ return mergeOptions( @@ -82,7 +83,7 @@ let MarketRegisterPiece = React.createClass({ handleAdditionalDataSuccess() { this.refreshPieceList(); - this.history.push('/collection'); + this.context.router.push('/collection'); }, nextSlide(queryParams) { From 7e0068bec8b0cfe9e88833c4f165464d89d88d3b Mon Sep 17 00:00:00 2001 From: tim Date: Wed, 4 May 2016 16:44:39 +0200 Subject: [PATCH 05/25] Fix app crashing warnings --- .../ascribe_buttons/acl_information.js | 6 +- js/components/ascribe_detail/edition.js | 2 +- .../file_drag_and_drop_dialog.js | 16 ++-- js/components/header.js | 37 +++------ js/components/header_notifications.js | 81 +++++-------------- 5 files changed, 45 insertions(+), 97 deletions(-) diff --git a/js/components/ascribe_buttons/acl_information.js b/js/components/ascribe_buttons/acl_information.js index c5dde338..d870a24d 100644 --- a/js/components/ascribe_buttons/acl_information.js +++ b/js/components/ascribe_buttons/acl_information.js @@ -37,13 +37,13 @@ let AclInformation = React.createClass({ } }, - getInfoText(title, info, example){ + getInfoText(title, info, example) { const aim = this.props.aim; if(aim) { if(aim === 'form') { return ( -

+

{replaceSubstringAtIndex(info.slice(2), 's ', ' ')} @@ -55,7 +55,7 @@ let AclInformation = React.createClass({ } else if(aim === 'button') { return ( -

+

{title} diff --git a/js/components/ascribe_detail/edition.js b/js/components/ascribe_detail/edition.js index 178fd18a..541faa42 100644 --- a/js/components/ascribe_detail/edition.js +++ b/js/components/ascribe_detail/edition.js @@ -277,7 +277,7 @@ let CoaDetails = React.createClass({ coaDetailElement = coa; } else { coaDetailElement = [ - , + ,

{getLangText("Just a sec, we're generating your COA")}

,

{getLangText('(you may leave the page)')}

]; diff --git a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_dialog.js b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_dialog.js index 1471595d..5540ad8c 100644 --- a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_dialog.js +++ b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_dialog.js @@ -34,11 +34,13 @@ let FileDragAndDropDialog = React.createClass({ }, render() { - const { enableLocalHashing, - fileClassToUpload, - multipleFiles, - onClick, - uploadMethod } = this.props; + const { + enableLocalHashing, + fileClassToUpload, + multipleFiles, + onClick, + uploadMethod + } = this.props; let dialogElement; if (enableLocalHashing && !uploadMethod) { @@ -66,10 +68,8 @@ let FileDragAndDropDialog = React.createClass({ {getLangText('Hash your work')} - {getLangText('or')} - - diff --git a/js/components/header.js b/js/components/header.js index 6863dc5d..aad25571 100644 --- a/js/components/header.js +++ b/js/components/header.js @@ -3,11 +3,8 @@ import React from 'react'; import { Link } from 'react-router'; -import history from '../history'; - import Nav from 'react-bootstrap/lib/Nav'; import Navbar from 'react-bootstrap/lib/Navbar'; -import CollapsibleNav from 'react-bootstrap/lib/CollapsibleNav'; import DropdownButton from 'react-bootstrap/lib/DropdownButton'; import MenuItem from 'react-bootstrap/lib/MenuItem'; import NavItem from 'react-bootstrap/lib/NavItem'; @@ -43,15 +40,10 @@ let Header = React.createClass({ // conflicts with routes that may need to wait to load the piece list PieceListStore.listen(this.onChange); - // react-bootstrap 0.25.1 has a bug in which it doesn't - // close the mobile expanded navigation after a click by itself. - // To get rid of this, we set the state of the component ourselves. - history.listen(this.onRouteChange); }, componentWillUnmount() { PieceListStore.unlisten(this.onChange); - //history.unlisten(this.onRouteChange); }, onChange(state) { @@ -116,15 +108,6 @@ let Header = React.createClass({ this.refs.dropdownbutton.setDropdownState(false); }, - // On route change, close expanded navbar again since react-bootstrap doesn't close - // the collapsibleNav by itself on click. setState() isn't available on a ref so - // doing this explicitly is the only way for now. - onRouteChange() { - if (this.refs.navbar) { - this.refs.navbar.state.navExpanded = false; - } - }, - render() { const { currentUser, routes, whitelabel } = this.props; const { unfilteredPieceListCount } = this.state; @@ -175,7 +158,7 @@ let Header = React.createClass({ navRoutesLinks = ( @@ -201,26 +184,30 @@ let Header = React.createClass({
- -
{notifications.map((notification, i) => { + const pieceOrEdition = isPiece ? notification.piece : notification.edition; + const href = isPiece ? `/pieces/${pieceOrEdition.id}` + : `/editions/${pieceOrEdition.bitcoin_id}`; + return ( - + + pieceOrEdition={pieceOrEdition} /> ); })} @@ -112,7 +93,7 @@ let HeaderNotifications = React.createClass({ } return ( -